From d66b42e2edf20caf22df72b8c7d140921e23b670 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 2 Jun 2025 16:59:23 +0200 Subject: [PATCH 1/4] Avoid string allocation when deserializing numbers --- crates/core/src/util.rs | 50 ++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 2e50951..9f2d342 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -3,11 +3,9 @@ extern crate alloc; use alloc::format; use alloc::string::String; -use serde::Deserialize; -use serde_json as json; - #[cfg(not(feature = "getrandom"))] use crate::sqlite; +use serde::de::Visitor; use uuid::Uuid; @@ -46,25 +44,47 @@ pub fn deserialize_string_to_i64<'de, D>(deserializer: D) -> Result, { - let value = json::Value::deserialize(deserializer)?; - - match value { - json::Value::String(s) => s.parse::().map_err(serde::de::Error::custom), - _ => Err(serde::de::Error::custom("Expected a string.")), - } + deserialize_optional_string_to_i64(deserializer)? + .ok_or_else(|| serde::de::Error::custom("Expected a string.")) } pub fn deserialize_optional_string_to_i64<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { - let value = json::Value::deserialize(deserializer)?; - - match value { - json::Value::Null => Ok(None), - json::Value::String(s) => s.parse::().map(Some).map_err(serde::de::Error::custom), - _ => Err(serde::de::Error::custom("Expected a string or null.")), + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a string or null") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse::().map(Some).map_err(serde::de::Error::custom) + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(None) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(self) + } } + + // Using a custom visitor here to avoid an intermediate string allocation + deserializer.deserialize_option(ValueVisitor) } // Use getrandom crate to generate UUID. From c7da94443ef8c5a319e2dad9aa82720be20be9ca Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 2 Jun 2025 17:05:03 +0200 Subject: [PATCH 2/4] Use visitors properly --- crates/core/src/util.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 9f2d342..5be026e 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -44,8 +44,24 @@ pub fn deserialize_string_to_i64<'de, D>(deserializer: D) -> Result, { - deserialize_optional_string_to_i64(deserializer)? - .ok_or_else(|| serde::de::Error::custom("Expected a string.")) + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value = i64; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse::().map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(ValueVisitor) } pub fn deserialize_optional_string_to_i64<'de, D>(deserializer: D) -> Result, D::Error> @@ -61,13 +77,6 @@ where formatter.write_str("a string or null") } - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - v.parse::().map(Some).map_err(serde::de::Error::custom) - } - fn visit_none(self) -> Result where E: serde::de::Error, @@ -79,7 +88,7 @@ where where D: serde::Deserializer<'de>, { - deserializer.deserialize_str(self) + Ok(Some(deserialize_string_to_i64(deserializer)?)) } } From f3efd269f158b53b0b8c225f5f0b37222f68fa3d Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 2 Jun 2025 17:09:30 +0200 Subject: [PATCH 3/4] Improve expectaction --- crates/core/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 5be026e..a2a4d74 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -50,7 +50,7 @@ where type Value = i64; fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { - formatter.write_str("a string") + formatter.write_str("a string representation of a number") } fn visit_str(self, v: &str) -> Result From a905e67943118596db20409695f64fa3a9fb8501 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 2 Jun 2025 17:10:50 +0200 Subject: [PATCH 4/4] Move comment to correct location --- crates/core/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index a2a4d74..a9e0842 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -61,6 +61,7 @@ where } } + // Using a custom visitor here to avoid an intermediate string allocation deserializer.deserialize_str(ValueVisitor) } @@ -92,7 +93,6 @@ where } } - // Using a custom visitor here to avoid an intermediate string allocation deserializer.deserialize_option(ValueVisitor) }