From 4268eddc2dc40a1f0a19c154f0895f1fb0f456b8 Mon Sep 17 00:00:00 2001 From: Aumetra Weisman Date: Sun, 8 Sep 2024 16:42:08 +0000 Subject: [PATCH] Fix `fast-cjson` (#581) * Fix fast-cjson impl * Alphabetically sort * Add raw number tests --- Cargo.lock | 3 +- Cargo.toml | 13 ++++-- lib/fast-cjson/src/lib.rs | 17 +++++-- lib/fast-cjson/tests/proptest.rs | 3 ++ lib/fast-cjson/tests/test.rs | 78 ++++++++++++++++++++++++++++++-- 5 files changed, 100 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c6948576..b10a15c94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4796,8 +4796,7 @@ dependencies = [ [[package]] name = "olpc-cjson" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d637c9c15b639ccff597da8f4fa968300651ad2f1e968aefc3b4927a6fb2027a" +source = "git+https://github.com/aumetra/aws-tough.git?rev=3b46feaeaf8048d46f85d023295e65563b84d7d6#3b46feaeaf8048d46f85d023295e65563b84d7d6" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index f4ee9b120..013b45aa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/lib/fast-cjson/src/lib.rs b/lib/fast-cjson/src/lib.rs index 2d65a6c4a..4fab7f4e5 100644 --- a/lib/fast-cjson/src/lib.rs +++ b/lib/fast-cjson/src/lib.rs @@ -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! { @@ -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( &mut self, diff --git a/lib/fast-cjson/tests/proptest.rs b/lib/fast-cjson/tests/proptest.rs index 80b3459d8..ed0fec1bc 100644 --- a/lib/fast-cjson/tests/proptest.rs +++ b/lib/fast-cjson/tests/proptest.rs @@ -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); } } diff --git a/lib/fast-cjson/tests/test.rs b/lib/fast-cjson/tests/test.rs index ff1f94ba5..33c7b4b3c 100644 --- a/lib/fast-cjson/tests/test.rs +++ b/lib/fast-cjson/tests/test.rs @@ -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> { 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)+)) }; } @@ -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()); + } +}