From b00b2cd56578488a4702a2aa4f4de90daf0b78f9 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 7 Jul 2026 10:35:09 -0400 Subject: [PATCH] perf(serde): borrow object keys during deserialization to avoid per-key allocation --- benches/bench.rs | 20 ++++++++++++++++++++ src/serde.rs | 7 +++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 255bea9..e2b0cc2 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -34,6 +34,26 @@ fn key_heavy_json_value(b: &mut Bencher) { bench_value(b, &get_key_heavy_json()); } +// struct deserialization matches keys against static field names, so borrowed +// keys avoid the per-property allocation entirely (unlike a serde_json::Value +// target, whose Map must own its keys regardless) +#[bench] +#[cfg(feature = "serde")] +fn key_heavy_json_serde_struct(b: &mut Bencher) { + #[derive(serde::Deserialize)] + #[allow(dead_code)] + struct Item { + id: u32, + name: String, + kind: String, + enabled: bool, + count: u32, + tag: String, + } + let json = get_key_heavy_json(); + b.iter(|| jsonc_parser::parse_to_serde_value::>(&json, &Default::default()).unwrap()); +} + #[bench] fn tsconfig_json_ast(b: &mut Bencher) { bench_ast(b, &get_tsconfig_json()); diff --git a/src/serde.rs b/src/serde.rs index 1e5fedc..38665a0 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -332,9 +332,12 @@ impl<'de, 'b> MapAccess<'de> for ScannerMapAccess<'de, 'b> { match key { None => Ok(None), Some(key) => { - let key_str = key.into_string(); + // borrow the key from the source when it's clean, only allocating for + // owned (escaped) keys — mirrors the value path's `Cow` keys seed - .deserialize(>::into_deserializer(key_str)) + .deserialize( as IntoDeserializer>::into_deserializer( + key.into_cow(), + )) .map(Some) } }