diff --git a/src/lib.rs b/src/lib.rs index aa5e026..20c6fd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,7 @@ pub mod common; #[cfg(feature = "cst")] pub mod cst; pub mod errors; +pub mod map; mod parse_to_ast; mod parse_to_value; mod parser; @@ -161,6 +162,7 @@ mod string; pub mod tokens; mod value; +pub use map::Map; pub use parse_to_ast::*; pub use parse_to_value::*; pub use scanner::*; diff --git a/src/map.rs b/src/map.rs new file mode 100644 index 0000000..f2735c1 --- /dev/null +++ b/src/map.rs @@ -0,0 +1,392 @@ +#[cfg(not(feature = "preserve_order"))] +use std::borrow::Borrow; +use std::hash::Hash; + +// the concrete backing map, selected by the enabled cargo features +#[cfg(all(not(feature = "preserve_order"), not(feature = "fast_hash")))] +type MapInner = std::collections::HashMap; +#[cfg(all(not(feature = "preserve_order"), feature = "fast_hash"))] +type MapInner = std::collections::HashMap; +#[cfg(all(feature = "preserve_order", not(feature = "fast_hash")))] +type MapInner = indexmap::IndexMap; +#[cfg(all(feature = "preserve_order", feature = "fast_hash"))] +type MapInner = indexmap::IndexMap; + +// backend-specific iterator and entry types, re-exported so the return types of +// `Map`'s methods can be named +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::Entry; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::IntoIter; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::IntoKeys; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::IntoValues; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::Iter; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::IterMut; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::Keys; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::Values; +#[cfg(not(feature = "preserve_order"))] +pub use std::collections::hash_map::ValuesMut; + +#[cfg(feature = "preserve_order")] +pub use indexmap::map::Entry; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::IntoIter; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::IntoKeys; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::IntoValues; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::Iter; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::IterMut; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::Keys; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::Values; +#[cfg(feature = "preserve_order")] +pub use indexmap::map::ValuesMut; + +/// The map used to store the properties of a [`JsonObject`](crate::JsonObject). +/// +/// The backing implementation and hasher are selected by the `preserve_order` +/// and `fast_hash` cargo features, but this type exposes the same API +/// regardless of which are enabled. It's meant to be a drop-in replacement for +/// the standard library's `HashMap`. +pub struct Map(MapInner); + +impl Map { + /// Creates an empty map. + pub fn new() -> Self { + Map(MapInner::default()) + } + + /// Creates an empty map with at least the specified capacity. + pub fn with_capacity(capacity: usize) -> Self { + Map(MapInner::with_capacity_and_hasher(capacity, Default::default())) + } + + /// Gets the number of entries. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Gets if there are no entries. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Gets the number of entries the map can hold without reallocating. + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + /// Removes all entries. + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Iterates over the entries. + pub fn iter(&self) -> Iter<'_, K, V> { + self.0.iter() + } + + /// Iterates over the entries with mutable references to the values. + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + self.0.iter_mut() + } + + /// Iterates over the keys. + pub fn keys(&self) -> Keys<'_, K, V> { + self.0.keys() + } + + /// Iterates over the values. + pub fn values(&self) -> Values<'_, K, V> { + self.0.values() + } + + /// Iterates over mutable references to the values. + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + self.0.values_mut() + } + + /// Consumes the map, iterating over its keys. + pub fn into_keys(self) -> IntoKeys { + self.0.into_keys() + } + + /// Consumes the map, iterating over its values. + pub fn into_values(self) -> IntoValues { + self.0.into_values() + } +} + +impl Map { + /// Inserts an entry, returning the previous value for the key if it existed. + pub fn insert(&mut self, key: K, value: V) -> Option { + self.0.insert(key, value) + } + + /// Gets the entry for the given key for in-place manipulation. + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + self.0.entry(key) + } + + /// Reserves capacity for at least `additional` more entries. + pub fn reserve(&mut self, additional: usize) { + self.0.reserve(additional); + } + + /// Retains only the entries for which the predicate returns `true`. + pub fn retain bool>(&mut self, f: F) { + self.0.retain(f); + } +} + +#[cfg(not(feature = "preserve_order"))] +impl Map { + /// Gets a reference to the value for the given key. + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.get(key) + } + + /// Gets a mutable reference to the value for the given key. + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.get_mut(key) + } + + /// Gets the key and value for the given key. + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.get_key_value(key) + } + + /// Gets if the map contains the given key. + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.contains_key(key) + } + + /// Removes and returns the value for the given key. + pub fn remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.remove(key) + } + + /// Removes and returns the entry for the given key. + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + self.0.remove_entry(key) + } +} + +#[cfg(feature = "preserve_order")] +impl Map { + /// Gets a reference to the value for the given key. + pub fn get(&self, key: &Q) -> Option<&V> + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.get(key) + } + + /// Gets a mutable reference to the value for the given key. + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.get_mut(key) + } + + /// Gets the key and value for the given key. + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.get_key_value(key) + } + + /// Gets if the map contains the given key. + pub fn contains_key(&self, key: &Q) -> bool + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.contains_key(key) + } + + /// Removes and returns the value for the given key, preserving the order of + /// the remaining entries. + pub fn remove(&mut self, key: &Q) -> Option + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.shift_remove(key) + } + + /// Removes and returns the entry for the given key, preserving the order of + /// the remaining entries. + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + where + Q: Hash + indexmap::Equivalent + ?Sized, + { + self.0.shift_remove_entry(key) + } +} + +impl Default for Map { + fn default() -> Self { + Map::new() + } +} + +impl Clone for Map { + fn clone(&self) -> Self { + Map(self.0.clone()) + } +} + +impl std::fmt::Debug for Map { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl PartialEq for Map { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for Map {} + +impl FromIterator<(K, V)> for Map { + fn from_iter>(iter: T) -> Self { + Map(MapInner::from_iter(iter)) + } +} + +impl Extend<(K, V)> for Map { + fn extend>(&mut self, iter: T) { + self.0.extend(iter); + } +} + +#[cfg(not(feature = "preserve_order"))] +impl, Q: Hash + Eq + ?Sized, V> std::ops::Index<&Q> for Map { + type Output = V; + fn index(&self, key: &Q) -> &V { + &self.0[key] + } +} + +#[cfg(feature = "preserve_order")] +impl + ?Sized, V> std::ops::Index<&Q> for Map { + type Output = V; + fn index(&self, key: &Q) -> &V { + &self.0[key] + } +} + +impl IntoIterator for Map { + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, K, V> IntoIterator for &'a Map { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, K, V> IntoIterator for &'a mut Map { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn drop_in_api() { + let mut map: Map = Map::new(); + assert!(map.is_empty()); + + map.insert("a".to_string(), 1); + map.insert("b".to_string(), 2); + *map.entry("c".to_string()).or_insert(0) += 3; + *map.entry("a".to_string()).or_insert(0) += 10; + assert_eq!(map.len(), 3); + + // lookups, including the `Index` impl + assert_eq!(map.get("a"), Some(&11)); + assert_eq!(map["b"], 2); + assert!(map.contains_key("c")); + assert_eq!(map.get("missing"), None); + + // mutation + if let Some(v) = map.get_mut("b") { + *v = 20; + } + assert_eq!(map.get("b"), Some(&20)); + + // removal + assert_eq!(map.remove("c"), Some(3)); + assert_eq!(map.remove("c"), None); + assert_eq!(map.remove_entry("a"), Some(("a".to_string(), 11))); + + // retain + map.retain(|_, v| *v > 15); + assert_eq!(map.len(), 1); + assert!(map.contains_key("b")); + + // iteration by reference + let mut count = 0; + for (_k, _v) in &map { + count += 1; + } + assert_eq!(count, 1); + + // FromIterator + Extend + let mut other: Map = [("x".to_string(), 1)].into_iter().collect(); + other.extend([("y".to_string(), 2)]); + assert_eq!(other.len(), 2); + } +} diff --git a/src/parse_to_value.rs b/src/parse_to_value.rs index 5e9f3f9..24a3a89 100644 --- a/src/parse_to_value.rs +++ b/src/parse_to_value.rs @@ -2,8 +2,8 @@ use super::ParseOptions; use super::errors::*; use super::tokens::Token; use super::value::*; +use crate::map::Map; use crate::parser::JsoncParser; -use crate::value::Map; /// Parses a string containing JSONC to a `JsonValue`. /// diff --git a/src/parser.rs b/src/parser.rs index f7e9b39..900d2c8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -13,13 +13,6 @@ pub(crate) enum ObjectKey<'a> { } impl<'a> ObjectKey<'a> { - pub fn into_string(self) -> String { - match self { - ObjectKey::String(s) => s.into_owned(), - ObjectKey::Word(s) => s.to_string(), - } - } - /// Converts the key into a `Cow`, borrowing from the source when possible /// to avoid an allocation for clean (unescaped) keys. pub fn into_cow(self) -> Cow<'a, str> { diff --git a/src/value.rs b/src/value.rs index d4aa458..b6fbb5c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,9 @@ use core::slice::Iter; use std::borrow::Cow; +use crate::map::IntoIter as MapIntoIter; +use crate::map::Map; + /// A JSON value. #[derive(Clone, PartialEq, Debug)] pub enum JsonValue<'a> { @@ -12,25 +15,13 @@ pub enum JsonValue<'a> { Null, } -#[cfg(all(not(feature = "preserve_order"), not(feature = "fast_hash")))] -pub type Map = std::collections::HashMap; -#[cfg(all(not(feature = "preserve_order"), feature = "fast_hash"))] -pub type Map = std::collections::HashMap; -#[cfg(all(feature = "preserve_order", not(feature = "fast_hash")))] -pub type Map = indexmap::IndexMap; -#[cfg(all(feature = "preserve_order", feature = "fast_hash"))] -pub type Map = indexmap::IndexMap; - /// A JSON object. #[derive(Clone, PartialEq, Debug)] pub struct JsonObject<'a>(Map, JsonValue<'a>>); impl<'a> IntoIterator for JsonObject<'a> { type Item = (Cow<'a, str>, JsonValue<'a>); - #[cfg(not(feature = "preserve_order"))] - type IntoIter = std::collections::hash_map::IntoIter, JsonValue<'a>>; - #[cfg(feature = "preserve_order")] - type IntoIter = indexmap::map::IntoIter, JsonValue<'a>>; + type IntoIter = MapIntoIter, JsonValue<'a>>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() @@ -43,21 +34,9 @@ impl<'a> From, JsonValue<'a>>> for JsonObject<'a> { } } -#[cfg(not(feature = "preserve_order"))] -#[inline(always)] -fn remove_entry<'a>(map: &mut Map, JsonValue<'a>>, key: &str) -> Option<(Cow<'a, str>, JsonValue<'a>)> { - map.remove_entry(key) -} - -#[cfg(feature = "preserve_order")] -#[inline(always)] -fn remove_entry<'a>(map: &mut Map, JsonValue<'a>>, key: &str) -> Option<(Cow<'a, str>, JsonValue<'a>)> { - map.shift_remove_entry(key) -} - macro_rules! generate_take { ($self:ident, $name:ident, $value_type:ident) => { - match remove_entry(&mut $self.0, $name) { + match $self.0.remove_entry($name) { Some((_, JsonValue::$value_type(value))) => Some(value), Some((key, value)) => { // add it back @@ -86,7 +65,7 @@ impl<'a> JsonObject<'a> { /// Creates a new JsonObject with the specified capacity. pub fn with_capacity(capacity: usize) -> JsonObject<'a> { - JsonObject(Map::with_capacity_and_hasher(capacity, Default::default())) + JsonObject(Map::with_capacity(capacity)) } /// Drops the object returning the inner map. @@ -143,7 +122,7 @@ impl<'a> JsonObject<'a> { /// Takes a value from the object by name. /// Returns `None` when it doesn't exist. pub fn take(&mut self, name: &str) -> Option> { - remove_entry(&mut self.0, name).map(|(_, value)| value) + self.0.remove_entry(name).map(|(_, value)| value) } /// Takes a string property value from the object by name.