Skip to content

Commit

Permalink
Fix fast-cjson (#581)
Browse files Browse the repository at this point in the history
* Fix fast-cjson impl

* Alphabetically sort

* Add raw number tests
  • Loading branch information
aumetra authored Sep 8, 2024
1 parent 8fece6c commit 4268edd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 14 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,15 @@ install-updater = true
install-path = "CARGO_HOME"

[patch.crates-io]
# Use RustCrypto "x509-cert" crate for certificate parsing
tokio-postgres-rustls = { git = "https://github.com/jbg/tokio-postgres-rustls.git", rev = "b16c1bc0f5d4f91324174fd1bd839d743a70f86a" }
# TCP nodelay for `axum::serve` and cloning improvements
axum = { git = "https://github.com/tokio-rs/axum.git", rev = "918170a12bfcc488000998f33684c29f5599d0d6" }
axum-core = { git = "https://github.com/tokio-rs/axum.git", rev = "918170a12bfcc488000998f33684c29f5599d0d6" }

# Fix proptests by fixing a olpc-cjson bug
olpc-cjson = { git = "https://github.com/aumetra/aws-tough.git", rev = "3b46feaeaf8048d46f85d023295e65563b84d7d6" }

# SIMD runtime detection and generic I/O wrapper
sonic-rs = { git = "https://github.com/aumetra/sonic-rs.git", rev = "6e17d8f1dfff5d325e90200806c9abae6eeaadfd" }

# TCP nodelay for `axum::serve` and cloning improvements
axum = { git = "https://github.com/tokio-rs/axum.git", rev = "918170a12bfcc488000998f33684c29f5599d0d6" }
axum-core = { git = "https://github.com/tokio-rs/axum.git", rev = "918170a12bfcc488000998f33684c29f5599d0d6" }
# Use RustCrypto "x509-cert" crate for certificate parsing
tokio-postgres-rustls = { git = "https://github.com/jbg/tokio-postgres-rustls.git", rev = "b16c1bc0f5d4f91324174fd1bd839d743a70f86a" }
17 changes: 14 additions & 3 deletions lib/fast-cjson/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,26 @@ impl Formatter for CanonicalFormatter {
wrapper! {
write_null;
write_bool, bool;
}

wrapper! {
write_i8, i8;
write_i16, i16;
write_i32, i32;
write_i64, i64;
write_i128, i128;
}

wrapper! {
write_u8, u8;
write_u16, u16;
write_u32, u32;
write_u64, u64;
write_u128, u128;
}

wrapper! {
write_byte_array, &[u8];
}

wrapper! {
Expand All @@ -153,9 +165,8 @@ impl Formatter for CanonicalFormatter {
float_err!()
}

// By default this is only used for u128/i128. If sonic_rs's `arbitrary_precision` feature is
// enabled, all numbers are internally stored as strings, and this method is always used (even
// for floating point values).
// If sonic_rs's `arbitrary_precision` feature is enabled, all numbers are internally stored as strings,
// and this method is always used (even for floating point values).
#[inline]
fn write_number_str<W: io::Write + ?Sized>(
&mut self,
Expand Down
3 changes: 3 additions & 0 deletions lib/fast-cjson/tests/proptest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ proptest! {
let mut our_cjson_ser = sonic_rs::Serializer::with_formatter(&mut our_cjson, fast_cjson::CanonicalFormatter::new());
data.serialize(&mut our_cjson_ser).unwrap();

let olpc_cjson = String::from_utf8(olpc_cjson).unwrap();
let our_cjson = String::from_utf8(our_cjson).unwrap();

prop_assert_eq!(olpc_cjson, our_cjson);
}
}
78 changes: 74 additions & 4 deletions lib/fast-cjson/tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
use fast_cjson::CanonicalFormatter;
use serde::Serialize;
use sonic_rs::Serializer;
use serde::{Deserialize, Serialize};
use sonic_rs::{LazyValue, Serializer};
use std::io;

/// Small wrapper around the `sonic_rs` json! macro to encode the value as canonical JSON.
macro_rules! encode {
($($tt:tt)+) => {
(@raw $($tt:tt)+) => {
(|v: sonic_rs::Value| -> io::Result<Vec<u8>> {
let mut buf = Vec::new();
let mut ser = Serializer::with_formatter(&mut buf, CanonicalFormatter::new());
v.serialize(&mut ser)?;
Ok(buf)
})(sonic_rs::json!($($tt)+))
})($($tt)+)
};

($($tt:tt)+) => {
encode!(@raw sonic_rs::json!($($tt)+))
};
}

Expand Down Expand Up @@ -172,3 +176,69 @@ fn actual_tuf_signed() {
];
assert_eq!(expected, encoded);
}

#[test]
fn raw_value() {
let encoded = encode!({
"nested": {
"bad": true,
"good": false
},
"b": 2,
"a": 1,
"c": {
"h": {
"h": -5,
"i": 3
},
"a": null,
"x": {}
}
})
.unwrap();

#[derive(Deserialize, Serialize)]
struct TestValue<'a> {
#[serde(borrow)]
a: LazyValue<'a>,

#[serde(borrow)]
b: LazyValue<'a>,

#[serde(borrow)]
c: LazyValue<'a>,

#[serde(borrow)]
nested: LazyValue<'a>,
}

let parsed: TestValue<'_> = sonic_rs::from_slice(&encoded).unwrap();
let encoded = encode!(parsed).unwrap();

assert_eq!(
br#"{"a":{"$sonic_rs::LazyValue":"1"},"b":{"$sonic_rs::LazyValue":"2"},"c":{"$sonic_rs::LazyValue":"{\"a\":null,\"h\":{\"h\":-5,\"i\":3},\"x\":{}}"},"nested":{"$sonic_rs::LazyValue":"{\"bad\":true,\"good\":false}"}}"#,
encoded.as_slice(),
);
}

#[test]
fn accept_raw_integer() {
let raw_number: sonic_rs::RawNumber = sonic_rs::from_str("8").unwrap();

let mut buf = Vec::new();
let mut ser = Serializer::with_formatter(&mut buf, CanonicalFormatter::new());
assert!(raw_number.serialize(&mut ser).is_ok());
}

#[test]
fn reject_raw_float() {
let raw_number: sonic_rs::RawNumber = sonic_rs::from_str("8.0").unwrap();
let raw_number_small: sonic_rs::RawNumber = sonic_rs::from_str("12.3e+11").unwrap();
let raw_number_large: sonic_rs::RawNumber = sonic_rs::from_str("13.12E+161").unwrap();

for number in &[raw_number, raw_number_small, raw_number_large] {
let mut buf = Vec::new();
let mut ser = Serializer::with_formatter(&mut buf, CanonicalFormatter::new());
assert!(number.serialize(&mut ser).is_err());
}
}

0 comments on commit 4268edd

Please sign in to comment.