Skip to content

Commit

Permalink
Support simd_json (#105)
Browse files Browse the repository at this point in the history
This PR adds support for the simd-json library whenever decoding or encoding JSON responses. This may be enabled independently of serenity and twilight support for SIMD acceleration.

Co-authored-by: Kyle Simpson <[email protected]>
  • Loading branch information
vicky5124 and FelixMcFelix committed Nov 19, 2023
1 parent 8cc7a22 commit cb0a74f
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 17 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
- Windows
- driver only
- gateway only
- simd json

include:
- name: beta
Expand All @@ -55,6 +56,10 @@ jobs:
- name: gateway only
features: serenity-rustls
dont-test: true
- name: simd json
features: simd-json serenity-rustls driver gateway serenity/simd_json
rustflags: -C target-cpu=native
dont-test: true

steps:
- name: Checkout sources
Expand Down Expand Up @@ -94,6 +99,10 @@ jobs:
target
key: ${{ runner.os }}-test-${{ steps.tc.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.toml') }}

- name: Set RUSTFLAGS
if: runner.os != 'Windows'
run: echo "RUSTFLAGS=${{ matrix.rustflags || '' }}" >> $GITHUB_ENV

- name: Build all features
if: matrix.features == ''
run: cargo build --features full-doc
Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ features = ["voice", "gateway"]
optional = true
version = "0.1"

[dependencies.simd-json]
optional = true
features = ["serde_impl"]
version = "0.6.0"

[dependencies.streamcatcher]
optional = true
version = "1"
Expand Down Expand Up @@ -225,6 +230,8 @@ zlib-simd = ["twilight-gateway/zlib-simd"]
zlib-stock = ["twilight-gateway/zlib-stock"]
serenity-deps = ["async-trait"]

simdjson = []

rustls-marker = []
native-marker = []

Expand Down
13 changes: 12 additions & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ args = ["fmt", "--all"]
args = ["build", "--features", "full-doc"]
dependencies = ["format"]

[tasks.build-simd]
args = ["build", "--features", "full-doc,simd-json,serenity/simd_json,twilight-gateway/simd-json"]
command = "cargo"
dependencies = ["format"]
env = { "RUSTFLAGS" = "-C target-cpu=native" }

[tasks.build-examples]
args = ["build", "--manifest-path", "./examples/Cargo.toml", "--workspace"]
command = "cargo"
Expand All @@ -24,7 +30,7 @@ command = "cargo"
dependencies = ["format"]

[tasks.build-variants]
dependencies = ["build", "build-gateway", "build-driver"]
dependencies = ["build", "build-gateway", "build-driver", "build-simd"]

[tasks.check]
args = ["check", "--features", "full-doc"]
Expand All @@ -37,6 +43,11 @@ dependencies = ["format"]
[tasks.test]
args = ["test", "--features", "full-doc"]

[tasks.test-simd]
args = ["test", "--features", "full-doc,simd-json,serenity/simd_json,twilight-gateway/simd-json"]
command = "cargo"
env = { "RUSTFLAGS" = "-C target-cpu=native" }

[tasks.bench]
description = "Runs performance benchmarks."
category = "Test"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ The library offers:
* And, by default, a fully featured voice system featuring events, queues, RT(C)P packet
handling, seeking on compatible streams, shared multithreaded audio stream caches,
and direct Opus data passthrough from DCA files.
* To be able to use `simd-json` from serenity, you will need to enable the `simdjson`
feature on both songbird and serenity.

## Intents
Songbird's gateway functionality requires you to specify the `GUILD_VOICE_STATES` intent.
Expand Down
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#[cfg(feature = "serenity")]
use futures::channel::mpsc::TrySendError;
#[cfg(not(feature = "simd-json"))]
pub use serde_json::Error as JsonError;
#[cfg(feature = "serenity")]
use serenity::gateway::InterMessage;
#[cfg(feature = "simd-json")]
pub use simd_json::Error as JsonError;
#[cfg(feature = "gateway")]
use std::{error::Error, fmt};
#[cfg(feature = "twilight")]
Expand Down
2 changes: 1 addition & 1 deletion src/input/adapters/cached/compressed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ impl Compressed {
)?;
let mut metabytes = b"DCA1\0\0\0\0".to_vec();
let orig_len = metabytes.len();
serde_json::to_writer(&mut metabytes, &metadata)?;
crate::json::to_writer(&mut metabytes, &metadata)?;
let meta_len = (metabytes.len() - orig_len)
.try_into()
.map_err(|_| CodecCacheError::MetadataTooLarge)?;
Expand Down
3 changes: 1 addition & 2 deletions src/input/adapters/cached/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::input::AudioStreamError;
use crate::{error::JsonError, input::AudioStreamError};
use audiopus::error::Error as OpusError;
use serde_json::Error as JsonError;
use std::{
error::Error as StdError,
fmt::{Display, Formatter, Result as FmtResult},
Expand Down
6 changes: 4 additions & 2 deletions src/input/codecs/dca/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ impl FormatReader for DcaReader {
return symph_err::decode_error("missing DCA1 metadata block");
}

let raw_json = source.read_boxed_slice_exact(size as usize)?;
let mut raw_json = source.read_boxed_slice_exact(size as usize)?;

let metadata: DcaMetadata = serde_json::from_slice::<DcaMetadata>(&raw_json)
// NOTE: must be mut for simd-json.
#[allow(clippy::unnecessary_mut_passed)]
let metadata: DcaMetadata = crate::json::from_slice::<DcaMetadata>(&mut raw_json)
.map_err(|_| SymphError::DecodeError("malformed DCA1 metadata block"))?;

let mut revision = MetadataBuilder::new();
Expand Down
5 changes: 3 additions & 2 deletions src/input/metadata/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::error::JsonError;
use std::time::Duration;
use symphonia_core::{meta::Metadata as ContainerMetadata, probe::ProbedMetadata};

Expand Down Expand Up @@ -47,8 +48,8 @@ pub struct AuxMetadata {

impl AuxMetadata {
/// Extract metadata and details from the output of `ffprobe -of json`.
pub fn from_ffprobe_json(value: &[u8]) -> Result<Self, serde_json::Error> {
let output: ffprobe::Output = serde_json::from_slice(value)?;
pub fn from_ffprobe_json(value: &mut [u8]) -> Result<Self, JsonError> {
let output: ffprobe::Output = crate::json::from_slice(value)?;

Ok(output.into_aux_metadata())
}
Expand Down
4 changes: 2 additions & 2 deletions src/input/sources/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ impl<P: AsRef<Path> + Send + Sync> Compose for File<P> {
"-i",
];

let output = Command::new("ffprobe")
let mut output = Command::new("ffprobe")
.args(&args)
.output()
.await
.map_err(|e| AudioStreamError::Fail(Box::new(e)))?;

AuxMetadata::from_ffprobe_json(&output.stdout[..])
AuxMetadata::from_ffprobe_json(&mut output.stdout[..])
.map_err(|e| AudioStreamError::Fail(Box::new(e)))
}
}
6 changes: 4 additions & 2 deletions src/input/sources/ytdl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@ impl YoutubeDl {
async fn query(&mut self) -> Result<Output, AudioStreamError> {
let ytdl_args = ["-j", &self.url, "-f", "ba[abr>0][vcodec=none]/best"];

let output = Command::new(self.program)
let mut output = Command::new(self.program)
.args(&ytdl_args)
.output()
.await
.map_err(|e| AudioStreamError::Fail(Box::new(e)))?;

let stdout: Output = serde_json::from_slice(&output.stdout[..])
// NOTE: must be mut for simd-json.
#[allow(clippy::unnecessary_mut_passed)]
let stdout: Output = crate::json::from_slice(&mut output.stdout[..])
.map_err(|e| AudioStreamError::Fail(Box::new(e)))?;

self.metadata = Some(stdout.as_aux_metadata());
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ pub use serenity_voice_model as model;
#[cfg(feature = "driver")]
pub use typemap_rev as typemap;

// Re-export serde-json APIs locally to minimise conditional config elsewhere.
#[cfg(not(feature = "simd-json"))]
pub(crate) use serde_json as json;
#[cfg(feature = "simd-json")]
pub(crate) use simd_json::serde as json;

#[cfg(feature = "driver")]
pub use crate::{
driver::Driver,
Expand Down
10 changes: 5 additions & 5 deletions src/ws.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::model::Event;
use crate::{error::JsonError, model::Event};

use async_trait::async_trait;
use async_tungstenite::{
Expand All @@ -8,7 +8,6 @@ use async_tungstenite::{
WebSocketStream,
};
use futures::{SinkExt, StreamExt, TryStreamExt};
use serde_json::Error as JsonError;
use tokio::time::{timeout, Duration};
use tracing::instrument;

Expand Down Expand Up @@ -92,7 +91,7 @@ impl ReceiverExt for WsStream {
#[async_trait]
impl SenderExt for SplitSink<WsStream, Message> {
async fn send_json(&mut self, value: &Event) -> Result<()> {
Ok(serde_json::to_string(value)
Ok(crate::json::to_string(value)
.map(Message::Text)
.map_err(Error::from)
.map(|m| self.send(m))?
Expand All @@ -103,7 +102,7 @@ impl SenderExt for SplitSink<WsStream, Message> {
#[async_trait]
impl SenderExt for WsStream {
async fn send_json(&mut self, value: &Event) -> Result<()> {
Ok(serde_json::to_string(value)
Ok(crate::json::to_string(value)
.map(Message::Text)
.map_err(Error::from)
.map(|m| self.send(m))?
Expand All @@ -114,7 +113,8 @@ impl SenderExt for WsStream {
#[inline]
pub(crate) fn convert_ws_message(message: Option<Message>) -> Result<Option<Event>> {
Ok(match message {
Some(Message::Text(payload)) => serde_json::from_str(&payload).map(Some)?,
Some(Message::Text(mut payload)) =>
crate::json::from_str(payload.as_mut_str()).map(Some)?,
Some(Message::Binary(bytes)) => {
return Err(Error::UnexpectedBinaryMessage(bytes));
},
Expand Down

0 comments on commit cb0a74f

Please sign in to comment.