From db62def2306243d8a9245d2b8f3ebcd00f82e2bb Mon Sep 17 00:00:00 2001 From: "wangjie.wjdew" Date: Wed, 22 Jan 2025 21:34:15 +0800 Subject: [PATCH] perf: use hashmap to optimize object get and insert operation --- Cargo.toml | 1 + benchmarks/benches/value_operator.rs | 8 +- src/index.rs | 10 +- src/value/macros.rs | 6 +- src/value/node.rs | 154 +++++++++++++-------------- src/value/object.rs | 93 ++++++++-------- src/value/partial_eq.rs | 6 +- src/value/ser.rs | 21 ++-- 8 files changed, 150 insertions(+), 149 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ed708d..157e782 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ version = "0.4.0-rc4" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ahash = "0.8" bumpalo = "3.13" bytes = "1.9" cfg-if = "1.0" diff --git a/benchmarks/benches/value_operator.rs b/benchmarks/benches/value_operator.rs index 2eb9d10..3954819 100644 --- a/benchmarks/benches/value_operator.rs +++ b/benchmarks/benches/value_operator.rs @@ -177,10 +177,12 @@ fn bench_object_insert(c: &mut Criterion) { b.iter_batched( || (), |_| { - let mut val = sonic_rs::Object::new(); + let mut val = sonic_rs::Object::with_capacity(100); for i in 0..100 { let mut obj = sonic_rs::json!({"a":{"b":{"c":{"d":{}}}}}); - for j in 0..100 { + *obj["a"]["b"]["c"]["d"].as_object_mut().unwrap() = + sonic_rs::Object::with_capacity(1000); + for j in 0..1000 { obj["a"]["b"]["c"]["d"] .as_object_mut() .unwrap() @@ -200,7 +202,7 @@ fn bench_object_insert(c: &mut Criterion) { let mut val = serde_json::Map::new(); for i in 0..100 { let mut obj = serde_json::json!({"a":{"b":{"c":{"d":{}}}}}); - for j in 0..100 { + for j in 0..1000 { obj["a"]["b"]["c"]["d"] .as_object_mut() .unwrap() diff --git a/src/index.rs b/src/index.rs index f0ffb03..0cf0a95 100644 --- a/src/index.rs +++ b/src/index.rs @@ -170,7 +170,7 @@ macro_rules! impl_str_index { if !v.is_object() { return None; } - v.get_key_mut(*self).map(|v| v.0) + v.get_key_mut(*self) } #[inline] @@ -184,11 +184,11 @@ macro_rules! impl_str_index { obj.as_object_mut() .expect(&format!("cannot access key in non-object value {:?}", typ)) .0 - .get_key_mut(*self).map_or_else(|| { + .get_key_mut(*self).unwrap_or_else(|| { let o = unsafe { dormant_obj.reborrow() }; - let inserted = o.append_pair((Into::::into((*self)), Value::new_null())); - &mut inserted.1 - }, |v| v.0) + let inserted = o.insert(&self, Value::new_null()); + inserted + }) } #[inline] diff --git a/src/value/macros.rs b/src/value/macros.rs index 19b3994..aebeb1a 100644 --- a/src/value/macros.rs +++ b/src/value/macros.rs @@ -226,8 +226,7 @@ macro_rules! json_internal { // Insert the current entry followed by trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { let key: &str = ($($key)+).as_ref(); - let pair = ($crate::Value::copy_str(key), $value); - let _ = $object.append_pair(pair); + let _ = $object.insert(key, $value); json_internal!(@object $object () ($($rest)*) ($($rest)*)); }; @@ -239,8 +238,7 @@ macro_rules! json_internal { // Insert the last entry without trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr)) => { let key: &str = ($($key)+).as_ref(); - let pair = ($crate::Value::copy_str(key), $value); - let _ = $object.append_pair(pair); + let _ = $object.insert(key, $value); }; // Next value is `null`. diff --git a/src/value/node.rs b/src/value/node.rs index f12b91a..c554637 100644 --- a/src/value/node.rs +++ b/src/value/node.rs @@ -10,6 +10,7 @@ use std::{ sync::Arc, }; +use ahash::AHashMap; use bumpalo::Bump; use faststr::FastStr; use ref_cast::RefCast; @@ -79,27 +80,27 @@ pub struct Value { // - Owned Node : mutable // - Shared Node: in SharedDom, not mutable // -// | Kind | 3 bits | 5 bits | 24 bits | ----> 32 bits ----> | 32 bits | 32 bits | limit | -// |-------------|-----------------|-------------------|--------------------------------|-------------------------|----------------------| -// | Null | 0 | 0 | + | | -// | True | 0 | 1 | + | | -// | False | 0 | 2 | + | | -// | I64 | 0 | 3 | + i64 | | -// | U64 | 0 | 4 | + u64 | | -// | F64 | 0 | 5 | + f64 | | -// | empty arr | 0 | 6 | | -// | empty obj | 0 | 7 | | -// | static str | 0 | 8 | | string length + *const u8 | excced will fallback | -// | faststr | 1 | 0 | + Box | | -// |rawnum_fastst| 1 | 1 | + Box | | -// | arr_mut | 1 | 2 | + Arc> | | -// | obj_mut | 1 | 3 | + Arc> | | -// | str_node | 2 | node idx | string length + *const u8 | max len 2^32 | -// | raw_num_node| 3 | node idx | string length + *const u8 | max len 2^32 | -// | arr_node | 4 | node idx | array length + *const Node | max len 2^32 | -// | obj_node | 5 | node idx | object length + *const Pair | max len 2^32 | -// |str_esc_raw | 6 | *const RawStrHeader (in SharedDom, MUST aligned 8) + *const u8 | | -// | root_node | 7 | *const ShardDom (from Arc, MUST aligned 8) + *const Node (head) | | +// | Kind | 3 bits | 5 bits | 24 bits | ----> 32 bits ----> | 32 bits | 32 bits | limit | +// |-------------|-----------------|-------------------|--------------------------------|-------------------------------|----------------------| +// | Null | 0 | 0 | + | | +// | True | 0 | 1 | + | | +// | False | 0 | 2 | + | | +// | I64 | 0 | 3 | + i64 | | +// | U64 | 0 | 4 | + u64 | | +// | F64 | 0 | 5 | + f64 | | +// | empty arr | 0 | 6 | | +// | empty obj | 0 | 7 | | +// | static str | 0 | 8 | | string length + *const u8 | excced will fallback | +// | faststr | 1 | 0 | + Box | | +// |rawnum_fastst| 1 | 1 | + Box | | +// | arr_mut | 1 | 2 | + Arc> | | +// | obj_mut | 1 | 3 | + Arc> | | +// | str_node | 2 | node idx | string length + *const u8 | max len 2^32 | +// | raw_num_node| 3 | node idx | string length + *const u8 | max len 2^32 | +// | arr_node | 4 | node idx | array length + *const Node | max len 2^32 | +// | obj_node | 5 | node idx | object length + *const Pair | max len 2^32 | +// |str_esc_raw | 6 | *const RawStrHeader (in SharedDom, MUST aligned 8) + *const u8 | | +// | root_node | 7 | *const ShardDom (from Arc, MUST aligned 8) + *const Node (head) | | // // NB: we will check the JSON length when parsing, if JSON is > 2GB, will return a error, so we will not check the limits when parsing or using dom. #[allow(clippy::box_collection)] @@ -117,7 +118,7 @@ pub(crate) union Data { pub(crate) root: NonNull, pub(crate) str_own: ManuallyDrop>, - pub(crate) obj_own: ManuallyDrop>>, + pub(crate) obj_own: ManuallyDrop>>, pub(crate) arr_own: ManuallyDrop>>, pub(crate) parent: u64, @@ -427,7 +428,7 @@ enum ValueDetail<'a> { FastStr(&'a FastStr), RawNumFasStr(&'a FastStr), Array(&'a Arc>), - Object(&'a Arc>), + Object(&'a Arc>), Root(NodeInDom<'a>), NodeInDom(NodeInDom<'a>), EmptyArray, @@ -474,13 +475,21 @@ pub enum ValueRefInner<'a> { RawNum(&'a str), Array(&'a [Value]), Object(&'a [Pair]), + ObjectOwned(&'a Arc>), EmptyArray, EmptyObject, } impl<'a> From<&'a [Pair]> for Value { fn from(value: &'a [Pair]) -> Self { - let newd = value.to_vec(); + let mut newd = AHashMap::with_capacity(value.len()); + + for (k, v) in value { + if let Some(k) = k.as_str() { + newd.insert(FastStr::new(k), v.clone()); + } + } + Self { meta: Meta::new(Meta::OBJ_MUT), data: Data { @@ -518,7 +527,7 @@ pub(crate) enum ValueMut<'a> { Str, RawNum, Array(&'a mut Vec), - Object(&'a mut Vec), + Object(&'a mut AHashMap), } impl Value { @@ -609,7 +618,7 @@ impl Value { ValueDetail::FastStr(s) => ValueRefInner::Str(s.as_str()), ValueDetail::RawNumFasStr(s) => ValueRefInner::RawNum(s.as_str()), ValueDetail::Array(a) => ValueRefInner::Array(a), - ValueDetail::Object(o) => ValueRefInner::Object(o), + ValueDetail::Object(o) => ValueRefInner::ObjectOwned(o), ValueDetail::Root(n) | ValueDetail::NodeInDom(n) => n.get_inner(), ValueDetail::EmptyArray => ValueRefInner::EmptyArray, ValueDetail::EmptyObject => ValueRefInner::EmptyObject, @@ -693,8 +702,8 @@ impl From>> for Value { } } -impl From>> for Value { - fn from(value: Arc>) -> Self { +impl From>> for Value { + fn from(value: Arc>) -> Self { Self { meta: Meta::new(Meta::OBJ_MUT), data: Data { @@ -770,7 +779,7 @@ impl super::value_trait::JsonValueTrait for Value { ValueRefInner::Number(_) => JsonType::Number, ValueRefInner::Str(_) | ValueRefInner::RawStr(_) => JsonType::String, ValueRefInner::Array(_) => JsonType::Array, - ValueRefInner::Object(_) => JsonType::Object, + ValueRefInner::Object(_) | ValueRefInner::ObjectOwned(_) => JsonType::Object, ValueRefInner::RawNum(_) => JsonType::Number, ValueRefInner::EmptyArray => JsonType::Array, ValueRefInner::EmptyObject => JsonType::Object, @@ -963,9 +972,9 @@ impl Value { ValueRefInner::Array(_) | ValueRefInner::EmptyArray => { ValueRef::Array(self.as_array().unwrap()) } - ValueRefInner::Object(_) | ValueRefInner::EmptyObject => { - ValueRef::Object(self.as_object().unwrap()) - } + ValueRefInner::Object(_) + | ValueRefInner::EmptyObject + | ValueRefInner::ObjectOwned(_) => ValueRef::Object(self.as_object().unwrap()), ValueRefInner::RawNum(raw) => { crate::from_str(raw).map_or(ValueRef::Null, ValueRef::Number) } @@ -1197,7 +1206,7 @@ impl Value { #[doc(hidden)] pub fn new_object_with(capacity: usize) -> Self { - let obj_own = ManuallyDrop::new(Arc::new(Vec::with_capacity(capacity))); + let obj_own = ManuallyDrop::new(Arc::new(AHashMap::with_capacity(capacity))); Value { meta: Meta::new(Meta::OBJ_MUT), data: Data { obj_own }, @@ -1231,39 +1240,27 @@ impl Value { pub(crate) fn get_key_value(&self, key: &str) -> Option<(&str, &Self)> { debug_assert!(self.is_object()); - if let ValueRefInner::Object(kv) = self.as_ref2() { + let ref_inner = self.as_ref2(); + if let ValueRefInner::Object(kv) = ref_inner { for (k, v) in kv { let k = k.as_str().expect("key is not string"); if k == key { return Some((k, v)); } } - } - None - } - - #[inline] - pub(crate) fn get_key_offset(&self, key: &str) -> Option { - debug_assert!(self.is_object()); - if let ValueRefInner::Object(kv) = self.as_ref2() { - for (i, pair) in kv.iter().enumerate() { - debug_assert!(pair.0.is_str()); - if pair.0.equal_str(key) { - return Some(i); - } + } else if let ValueRefInner::ObjectOwned(kv) = ref_inner { + if let Some((k, v)) = kv.get_key_value(key) { + return Some((k.as_str(), v)); } } None } #[inline] - pub(crate) fn get_key_mut(&mut self, key: &str) -> Option<(&mut Self, usize)> { + pub(crate) fn get_key_mut(&mut self, key: &str) -> Option<&mut Self> { if let ValueMut::Object(kv) = self.as_mut() { - for (i, (k, v)) in kv.iter_mut().enumerate() { - debug_assert!(k.is_str()); - if k.equal_str(key) { - return Some((v, i)); - } + if let Some(v) = kv.get_mut(key) { + return Some(v); } } None @@ -1313,25 +1310,14 @@ impl Value { } #[inline] - pub(crate) fn remove_pair_index(&mut self, index: usize) -> (Value, Value) { + pub(crate) fn remove_key(&mut self, k: &str) -> Option { debug_assert!(self.is_object()); match self.as_mut() { - ValueMut::Object(obj) => obj.remove(index), + ValueMut::Object(obj) => obj.remove(k), _ => unreachable!("value is not object"), } } - #[inline] - pub(crate) fn remove_key(&mut self, k: &str) -> Option { - debug_assert!(self.is_object()); - if let Some(i) = self.get_key_offset(k) { - let (_, val) = self.remove_pair_index(i); - Some(val) - } else { - None - } - } - /// Take the value from the node, and set the node as a empty node. /// Take will creat a new root node. /// @@ -1380,13 +1366,12 @@ impl Value { #[doc(hidden)] #[inline] - pub fn append_pair(&mut self, pair: Pair) -> &mut Pair { + pub fn insert(&mut self, key: &str, val: Value) -> &mut Value { debug_assert!(self.is_object()); match self.as_mut() { ValueMut::Object(obj) => { - obj.push(pair); - let len = obj.len(); - &mut obj[len - 1] + obj.insert(FastStr::new(key), val); + obj.get_mut(key).unwrap() } _ => unreachable!("value is not object"), } @@ -1401,15 +1386,6 @@ impl Value { } } - #[inline] - pub(crate) fn pop_pair(&mut self) -> Option { - debug_assert!(self.is_object()); - match self.as_mut() { - ValueMut::Object(obj) => obj.pop(), - _ => unreachable!("value is not object"), - } - } - #[inline(never)] pub(crate) fn parse_with_padding(&mut self, json: &[u8], cfg: DeserializeCfg) -> Result { // allocate the padding buffer for the input json @@ -1795,6 +1771,26 @@ impl Serialize for Value { map.end() } } + ValueRefInner::ObjectOwned(o) => { + #[cfg(feature = "sort_keys")] + { + let mut map = tri!(serializer.serialize_map(Some(o.len()))); + for (k, v) in o.iter() { + tri!(map.serialize_key(k.as_str())); + tri!(map.serialize_value(v)); + } + map.end() + } + #[cfg(not(feature = "sort_keys"))] + { + let mut map = tri!(serializer.serialize_map(Some(o.len()))); + for (k, v) in o.iter() { + tri!(map.serialize_key(k.as_str())); + tri!(map.serialize_value(v)); + } + map.end() + } + } ValueRefInner::RawNum(raw) => { use serde::ser::SerializeStruct; diff --git a/src/value/object.rs b/src/value/object.rs index 4cb9a9d..a2da338 100644 --- a/src/value/object.rs +++ b/src/value/object.rs @@ -1,6 +1,8 @@ //! Represents a parsed JSON object. +use core::hash; use std::{iter::FusedIterator, marker::PhantomData, slice}; +use faststr::FastStr; use ref_cast::RefCast; use super::{node::ValueMut, value_trait::JsonValueTrait}; @@ -175,7 +177,7 @@ impl Object { /// ``` #[inline] pub fn get_mut>(&mut self, key: &Q) -> Option<&mut Value> { - self.0.get_key_mut(key.as_ref()).map(|v| v.0) + self.0.get_key_mut(key.as_ref()) } /// Returns the key-value pair corresponding to the supplied key. @@ -369,7 +371,7 @@ impl Object { /// assert_eq!(obj.get(&"10"), None); /// ``` #[inline] - pub fn entry<'a, Q: AsRef + ?Sized>(&'a mut self, key: &Q) -> Entry<'a> { + pub fn entry<'a, Q: AsRef + ?Sized>(&'a mut self, key: &'a Q) -> Entry<'a> { let (obj, dormant_obj) = DormantMutRef::new(self); match obj.0.get_key_mut(key.as_ref()) { None => { @@ -381,9 +383,7 @@ impl Object { _marker: PhantomData, }) } - Some((handle, offset)) => { - Entry::Occupied(OccupiedEntry::new(handle, offset, dormant_obj)) - } + Some(handle) => Entry::Occupied(OccupiedEntry::new(handle, key.as_ref(), dormant_obj)), } } @@ -408,7 +408,7 @@ impl Object { F: FnMut(&str, &mut Value) -> bool, { if let ValueMut::Object(s) = self.0.as_mut() { - s.retain_mut(|(k, v)| f(k.as_str().unwrap(), v)) + s.retain(|k, v| f(k.as_str(), v)); } else { unreachable!("should not used in array") } @@ -430,28 +430,18 @@ impl Object { /// ``` #[inline] pub fn append(&mut self, other: &mut Self) { - while let Some((k, v)) = other.0.pop_pair() { - self.0.append_pair((k, v)); + if let ValueMut::Object(o) = self.0.as_mut() { + o.reserve(other.len()); + if let ValueMut::Object(oo) = other.0.as_mut() { + o.extend(oo.drain().into_iter()); + } else { + unreachable!("should not used in array") + } + } else { + unreachable!("should not used in array") } } - /// Append the key-value pair to the object. Not check the key is exist or not. - /// - /// # Examples - /// - /// ``` - /// use sonic_rs::object; - /// - /// let mut a = object! {"a": null}; - /// a.append_pair("b", 1); - /// - /// assert_eq!(a, object! {"a": null, "b": 1}); - /// ``` - #[inline] - pub fn append_pair, V: Into>(&mut self, key: K, value: V) { - let _ = self.0.append_pair((key.as_ref().into(), value.into())); - } - /// Reserves capacity for at least additional more elements to be inserted in the given. /// /// # Examples @@ -473,12 +463,12 @@ impl Object { /// A view into a single occupied location in a `Object`. pub struct OccupiedEntry<'a> { handle: &'a mut Value, - offset: usize, + key: &'a str, dormant_obj: DormantMutRef<'a, Object>, _marker: PhantomData<&'a mut Pair>, } -impl<'a> OccupiedEntry<'a> { +impl<'a, 'k> OccupiedEntry<'a> { /// Gets a reference to the value in the entry. /// /// # Examples @@ -594,19 +584,19 @@ impl<'a> OccupiedEntry<'a> { #[inline] pub fn remove(mut self) -> Value { let obj = unsafe { self.dormant_obj.reborrow() }; - let (_, val) = obj.0.remove_pair_index(self.offset); + let val = obj.0.remove_key(self.key).unwrap(); val } #[inline] pub(crate) fn new( handle: &'a mut Value, - offset: usize, + key: &'a str, dormant_obj: DormantMutRef<'a, Object>, ) -> Self { Self { handle, - offset, + key, dormant_obj, _marker: PhantomData, } @@ -644,8 +634,8 @@ impl<'a> VacantEntry<'a> { pub fn insert>(self, val: T) -> &'a mut Value { let obj = unsafe { self.dormant_obj.awaken() }; obj.reserve(1); - let pair = obj.0.append_pair((self.key, val.into())); - &mut pair.1 + let val = obj.0.insert(self.key.as_str().unwrap(), val.into()); + val } /// Get the key of the vacant entry. @@ -833,9 +823,25 @@ pub struct Iter<'a>(slice::Iter<'a, (Value, Value)>); impl_entry_iter!((Iter<'a>): (&'a str, &'a Value)); /// A mutable iterator over the entries of a `Object`. -pub struct IterMut<'a>(slice::IterMut<'a, (Value, Value)>); -impl_entry_iter!((IterMut<'a>): (&'a str, &'a mut Value)); +pub struct IterMut<'a>(std::collections::hash_map::IterMut<'a, FastStr, Value>); +impl<'a> Iterator for IterMut<'a> { + type Item = (&'a str, &'a mut Value); + + #[inline] + fn next(&mut self) -> Option { + self.0.next().map(|(k, v)| (k.as_str(), v)) + } +} + +impl<'a> ExactSizeIterator for IterMut<'a> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } +} + +impl<'a> FusedIterator for IterMut<'a> {} /// An iterator over the keys of a `Object`. pub struct Keys<'a>(Iter<'a>); @@ -899,7 +905,6 @@ impl_value_iter!((Values<'a>): &'a Value); /// A mutable iterator over the values of a `Object`. pub struct ValuesMut<'a>(IterMut<'a>); -impl_value_iter!((ValuesMut<'a>): &'a mut Value); impl<'a> IntoIterator for &'a Object { type Item = (&'a str, &'a Value); @@ -911,15 +916,15 @@ impl<'a> IntoIterator for &'a Object { } } -impl<'a> IntoIterator for &'a mut Object { - type Item = (&'a str, &'a mut Value); - type IntoIter = IterMut<'a>; +// impl<'a> IntoIterator for &'a mut Object { +// type Item = (&'a str, &'a mut Value); +// type IntoIter = IterMut<'a>; - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} +// #[inline] +// fn into_iter(self) -> Self::IntoIter { +// self.iter_mut() +// } +// } impl<'a, Q: AsRef + ?Sized> std::ops::Index<&'a Q> for Object { type Output = Value; @@ -1010,7 +1015,7 @@ mod test { assert_eq!(obj["e"][0], i); } - for (i, v) in obj.iter_mut().enumerate() { + for (i, v) in obj.iter_mut().0.enumerate() { *(v.1) = Value::from(&i.to_string()); } diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 6282f16..67d4337 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -24,9 +24,9 @@ impl PartialEq for Value { ValueRefInner::Array(_) | ValueRefInner::EmptyArray => { other.as_value_slice() == self.as_value_slice() } - ValueRefInner::Object(_) | ValueRefInner::EmptyObject => { - other.as_object() == self.as_object() - } + ValueRefInner::Object(_) + | ValueRefInner::EmptyObject + | ValueRefInner::ObjectOwned(_) => other.as_object() == self.as_object(), } } } diff --git a/src/value/ser.rs b/src/value/ser.rs index 4cec817..3671810 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -66,6 +66,7 @@ where // Not export this because it is mainly used in `json!`. pub(crate) struct Serializer; +use super::JsonValueTrait; use crate::serde::tri; impl serde::Serializer for Serializer { @@ -223,8 +224,7 @@ impl serde::Serializer for Serializer { T: ?Sized + Serialize, { let mut object = Value::new_object_with(1); - let pair = (Value::from_static_str(variant), tri!(to_value(value))); - object.append_pair(pair); + object.insert(&variant, tri!(to_value(value))); Ok(object) } @@ -271,7 +271,7 @@ impl serde::Serializer for Serializer { len: usize, ) -> Result { Ok(SerializeTupleVariant { - static_name: Value::from_static_str(variant), + static_name: variant, vec: Value::new_array_with(len), }) } @@ -305,7 +305,7 @@ impl serde::Serializer for Serializer { len: usize, ) -> Result { Ok(SerializeStructVariant { - static_name: Value::from_static_str(variant), + static_name: variant, object: Value::new_object_with(len), }) } @@ -326,7 +326,7 @@ pub(crate) struct SerializeVec { /// Serializing Rust tuple variant into `Value`. pub(crate) struct SerializeTupleVariant { - static_name: Value, + static_name: &'static str, vec: Value, } @@ -347,7 +347,7 @@ enum MapInner { /// Serializing Rust struct variant into `Value`. pub(crate) struct SerializeStructVariant { - static_name: Value, + static_name: &'static str, object: Value, } @@ -414,7 +414,7 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { fn end(self) -> Result { let mut object = Value::new_object_with(1); - object.append_pair((self.static_name, self.vec)); + object.insert(&self.static_name, self.vec); Ok(object) } } @@ -446,7 +446,7 @@ impl serde::ser::SerializeMap for SerializeMap { // Panic because this indicates a bug in the program rather than an // expected failure. let key = key.expect("serialize_value called before serialize_key"); - object.append_pair((key, tri!(to_value(value)))); + object.insert(key.as_str().unwrap(), tri!(to_value(value))); Ok(()) } MapInner::RawNumber { .. } => unreachable!(), @@ -855,14 +855,13 @@ impl serde::ser::SerializeStructVariant for SerializeStructVariant { where T: ?Sized + Serialize, { - self.object - .append_pair((Value::from_static_str(key), tri!(to_value(value)))); + self.object.insert(key, tri!(to_value(value))); Ok(()) } fn end(self) -> Result { let mut object = Value::new_object_with(1); - object.append_pair((self.static_name, self.object)); + object.insert(self.static_name, self.object); Ok(object) } }