use crate::{
backend::Backend,
compat::{
DefaultHashBuilder,
HashMap,
},
DefaultBackend,
DefaultSymbol,
Symbol,
};
use core::{
fmt,
fmt::{
Debug,
Formatter,
},
hash::{
BuildHasher,
Hash,
Hasher,
},
iter::FromIterator,
};
fn make_hash<T>(builder: &impl BuildHasher, value: &T) -> u64
where
T: ?Sized + Hash,
{
let state = &mut builder.build_hasher();
value.hash(state);
state.finish()
}
pub struct StringInterner<
S = DefaultSymbol,
B = DefaultBackend<S>,
H = DefaultHashBuilder,
> where
S: Symbol,
H: BuildHasher,
{
dedup: HashMap<S, (), ()>,
hasher: H,
backend: B,
}
impl<S, B, H> Debug for StringInterner<S, B, H>
where
S: Symbol + Debug,
B: Backend<S> + Debug,
H: BuildHasher,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StringInterner")
.field("dedup", &self.dedup)
.field("backend", &self.backend)
.finish()
}
}
#[cfg(feature = "backends")]
impl Default for StringInterner {
#[cfg_attr(feature = "inline-more", inline)]
fn default() -> Self {
StringInterner::new()
}
}
impl<S, B, H> Clone for StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S> + Clone,
H: BuildHasher + Clone,
{
fn clone(&self) -> Self {
Self {
dedup: self.dedup.clone(),
hasher: self.hasher.clone(),
backend: self.backend.clone(),
}
}
}
impl<S, B, H> PartialEq for StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S> + PartialEq,
H: BuildHasher,
{
fn eq(&self, rhs: &Self) -> bool {
self.len() == rhs.len() && self.backend == rhs.backend
}
}
impl<S, B, H> Eq for StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S> + Eq,
H: BuildHasher,
{
}
impl<S, B, H> StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S>,
H: BuildHasher + Default,
{
#[cfg_attr(feature = "inline-more", inline)]
pub fn new() -> Self {
Self {
dedup: HashMap::default(),
hasher: Default::default(),
backend: B::default(),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn with_capacity(cap: usize) -> Self {
Self {
dedup: HashMap::with_capacity_and_hasher(cap, ()),
hasher: Default::default(),
backend: B::with_capacity(cap),
}
}
}
impl<S, B, H> StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S>,
H: BuildHasher,
{
#[cfg_attr(feature = "inline-more", inline)]
pub fn with_hasher(hash_builder: H) -> Self {
StringInterner {
dedup: HashMap::default(),
hasher: hash_builder,
backend: B::default(),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn with_capacity_and_hasher(cap: usize, hash_builder: H) -> Self {
StringInterner {
dedup: HashMap::with_capacity_and_hasher(cap, ()),
hasher: hash_builder,
backend: B::with_capacity(cap),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn len(&self) -> usize {
self.dedup.len()
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn get<T>(&self, string: T) -> Option<S>
where
T: AsRef<str>,
{
let string = string.as_ref();
let Self {
dedup,
hasher,
backend,
} = self;
let hash = make_hash(hasher, string);
dedup
.raw_entry()
.from_hash(hash, |symbol| {
string == unsafe { backend.resolve_unchecked(*symbol) }
})
.map(|(&symbol, &())| symbol)
}
#[cfg_attr(feature = "inline-more", inline)]
fn get_or_intern_using<T>(&mut self, string: T, intern_fn: fn(&mut B, T) -> S) -> S
where
T: Copy + Hash + AsRef<str> + for<'a> PartialEq<&'a str>,
{
let Self {
dedup,
hasher,
backend,
} = self;
let hash = make_hash(hasher, string.as_ref());
let entry = dedup.raw_entry_mut().from_hash(hash, |symbol| {
string == unsafe { backend.resolve_unchecked(*symbol) }
});
use crate::compat::hash_map::RawEntryMut;
let (&mut symbol, &mut ()) = match entry {
RawEntryMut::Occupied(occupied) => occupied.into_key_value(),
RawEntryMut::Vacant(vacant) => {
let symbol = intern_fn(backend, string);
vacant.insert_with_hasher(hash, symbol, (), |symbol| {
let string = unsafe { backend.resolve_unchecked(*symbol) };
make_hash(hasher, string)
})
}
};
symbol
}
#[inline]
pub fn get_or_intern<T>(&mut self, string: T) -> S
where
T: AsRef<str>,
{
self.get_or_intern_using(string.as_ref(), B::intern)
}
#[inline]
pub fn get_or_intern_static(&mut self, string: &'static str) -> S {
self.get_or_intern_using(string, B::intern_static)
}
#[inline]
pub fn resolve(&self, symbol: S) -> Option<&str> {
self.backend.resolve(symbol)
}
}
impl<S, B, H, T> FromIterator<T> for StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S>,
H: BuildHasher + Default,
T: AsRef<str>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
let iter = iter.into_iter();
let (capacity, _) = iter.size_hint();
let mut interner = Self::with_capacity(capacity);
interner.extend(iter);
interner
}
}
impl<S, B, H, T> Extend<T> for StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S>,
H: BuildHasher,
T: AsRef<str>,
{
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = T>,
{
for s in iter {
self.get_or_intern(s.as_ref());
}
}
}
impl<'a, S, B, H> IntoIterator for &'a StringInterner<S, B, H>
where
S: Symbol,
B: Backend<S>,
&'a B: IntoIterator<Item = (S, &'a str)>,
H: BuildHasher,
{
type Item = (S, &'a str);
type IntoIter = <&'a B as IntoIterator>::IntoIter;
#[cfg_attr(feature = "inline-more", inline)]
fn into_iter(self) -> Self::IntoIter {
self.backend.into_iter()
}
}