diff --git a/Cargo.toml b/Cargo.toml index d4ea3c8d2..303bb6798 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ repository = "https://github.com/serde-rs/json" rust-version = "1.56" [dependencies] -indexmap = { version = "2", optional = true } +ahash = { version = "0.8", default-features = false, optional = true } +indexmap = { version = "2", default-features = false, optional = true } itoa = "1.0" ryu = "1.0" serde = { version = "1.0.194", default-features = false } @@ -45,7 +46,7 @@ features = ["raw_value"] [features] default = ["std"] -std = ["serde/std"] +std = ["serde/std", "ahash?/std", "indexmap?/std"] # Provide integration for heap-allocated collections without depending on the # rest of the Rust standard library. @@ -55,7 +56,7 @@ alloc = ["serde/alloc"] # Make serde_json::Map use a representation which maintains insertion order. # This allows data to be read into a Value and written back to a JSON string # while preserving the order of map keys in the input. -preserve_order = ["indexmap", "std"] +preserve_order = ["ahash", "indexmap"] # Use sufficient precision when parsing fixed precision floats from JSON to # ensure that they maintain accuracy when round-tripped through JSON. This comes diff --git a/src/map.rs b/src/map.rs index 675058ba1..2b488739d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -7,6 +7,8 @@ //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html use crate::value::Value; +#[cfg(feature = "preserve_order")] +use ahash::RandomState; use alloc::string::String; use core::borrow::Borrow; use core::fmt::{self, Debug}; @@ -30,14 +32,17 @@ pub struct Map { #[cfg(not(feature = "preserve_order"))] type MapImpl = BTreeMap; #[cfg(feature = "preserve_order")] -type MapImpl = IndexMap; +type MapImpl = IndexMap; impl Map { /// Makes a new empty Map. #[inline] pub fn new() -> Self { Map { + #[cfg(not(feature = "preserve_order"))] map: MapImpl::new(), + #[cfg(feature = "preserve_order")] + map: MapImpl::with_hasher(RandomState::new()), } } @@ -52,7 +57,7 @@ impl Map { BTreeMap::new() }, #[cfg(feature = "preserve_order")] - map: IndexMap::with_capacity(capacity), + map: IndexMap::with_capacity_and_hasher(capacity, RandomState::new()), } } @@ -159,8 +164,10 @@ impl Map { #[inline] pub fn append(&mut self, other: &mut Self) { #[cfg(feature = "preserve_order")] - self.map - .extend(mem::replace(&mut other.map, MapImpl::default())); + self.map.extend(mem::replace( + &mut other.map, + MapImpl::with_hasher(RandomState::new()), + )); #[cfg(not(feature = "preserve_order"))] self.map.append(&mut other.map); } @@ -252,7 +259,10 @@ impl Default for Map { #[inline] fn default() -> Self { Map { + #[cfg(not(feature = "preserve_order"))] map: MapImpl::new(), + #[cfg(feature = "preserve_order")] + map: MapImpl::with_hasher(RandomState::new()), } } } @@ -400,8 +410,16 @@ impl FromIterator<(String, Value)> for Map { where T: IntoIterator, { - Map { + Self { + #[cfg(not(feature = "preserve_order"))] map: FromIterator::from_iter(iter), + #[cfg(feature = "preserve_order")] + map: { + // TODO: replace with `iter.into_iter().collect();` when RandomState will impl Default + let mut map = MapImpl::with_hasher(RandomState::new()); + map.extend(iter); + map + }, } } }