diff --git a/src/types/interner.rs b/src/types/interner.rs index 866593ce..6fc37188 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,7 +1,14 @@ +use crate::func::hashing::get_hasher; use crate::{Identifier, ImmutableString}; + #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{collections::BTreeMap, marker::PhantomData, ops::AddAssign}; +use std::{ + collections::BTreeMap, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::AddAssign, +}; /// _(internals)_ A factory of identifiers from text strings. /// Exported under the `internals` feature only. @@ -10,13 +17,13 @@ use std::{collections::BTreeMap, marker::PhantomData, ops::AddAssign}; #[derive(Debug, Clone, Default, Hash)] pub struct StringsInterner<'a> { /// Normal strings. - strings: BTreeMap, + strings: BTreeMap, /// Property getters. #[cfg(not(feature = "no_object"))] - getters: BTreeMap, + getters: BTreeMap, /// Property setters. #[cfg(not(feature = "no_object"))] - setters: BTreeMap, + setters: BTreeMap, /// Take care of the lifetime parameter. dummy: PhantomData<&'a ()>, } @@ -35,12 +42,14 @@ impl StringsInterner<'_> { dummy: PhantomData, } } + /// Get an identifier from a text string and prefix, adding it to the interner if necessary. #[inline(always)] #[must_use] pub fn get(&mut self, text: impl AsRef) -> ImmutableString { self.get_with_prefix("", text) } + /// Get an identifier from a text string and prefix, adding it to the interner if necessary. /// /// # Prefix @@ -75,14 +84,40 @@ impl StringsInterner<'_> { _ => unreachable!("unsupported prefix {}", prefix), }; - if !dict.is_empty() && dict.contains_key(text) { - dict.get(text).unwrap().clone() + let hasher = &mut get_hasher(); + text.hash(hasher); + let key = hasher.finish(); + + if !dict.is_empty() && dict.contains_key(&key) { + dict.get(&key).unwrap().clone() } else { let value: ImmutableString = mapper(text).into(); - dict.insert(text.into(), value.clone()); + dict.insert(key, value.clone()); value } } + + /// Number of strings interned. + #[inline(always)] + #[must_use] + pub fn len(&self) -> usize { + #[cfg(not(feature = "no_object"))] + return self.strings.len() + self.getters.len() + self.setters.len(); + + #[cfg(feature = "no_object")] + return self.strings.len(); + } + + /// Number of strings interned. + #[inline(always)] + #[must_use] + pub fn is_empty(&self) -> bool { + #[cfg(not(feature = "no_object"))] + return self.strings.is_empty() || self.getters.is_empty() || self.setters.is_empty(); + + #[cfg(feature = "no_object")] + return self.strings.is_empty(); + } } impl AddAssign for StringsInterner<'_> {