use hashbrown::hash_map::RawEntryMut;
use hashbrown::HashMap;
use core::borrow::Borrow;
use std::hash::{BuildHasher, Hash, RandomState};
pub mod backend;
pub use backend::{Backend, DefaultBackendBuilder, StringBackend};
use crate::backend::Internable;
pub type Symbol<T, B = <T as DefaultBackendBuilder>::Backend> = <B as Backend<T>>::Symbol;
pub type StringInterner = Interner<str,StringBackend>;
pub struct Interner<
T,
B = <T as DefaultBackendBuilder>::Backend,
H = RandomState
>
where
T: Hash + Eq + PartialEq + ?Sized,
H: BuildHasher,
B: Backend<T>,
{
backend: B,
set: HashMap<B::Symbol, (), ()>,
hasher: H,
}
impl<T, B, H> Interner<T, B, H>
where
T: Hash + Eq + PartialEq + ?Sized,
H: BuildHasher,
B: Backend<T>,
{
pub fn new() -> Self
where
B: Default,
H: Default,
{
Self {
backend: B::default(),
set: HashMap::default(),
hasher: H::default(),
}
}
pub fn with_hasher(hasher: H) -> Self
where
B: Default,
{
Self {
backend: B::default(),
set: HashMap::default(),
hasher,
}
}
pub fn with_backend(backend: B) -> Self
where
H: Default,
{
Self {
backend,
set: HashMap::default(),
hasher: H::default(),
}
}
pub const fn with_backend_and_hasher(backend: B, hasher: H) -> Self {
Self {
backend,
hasher,
set: HashMap::with_hasher(()),
}
}
pub fn get_or_intern<Ref>(&mut self, src: &Ref) -> B::Symbol
where
Ref: Internable<T, B> + ?Sized + Hash + Eq,
T: Borrow<Ref>,
{
let Self {
backend,
set,
hasher,
} = self;
let hash = hasher.hash_one(src);
let entry = set
.raw_entry_mut()
.from_hash(hash, |&sym| {
src == unsafe { backend.get_unchecked(sym) }.borrow()
});
let k = match entry {
RawEntryMut::Occupied(occupied) => occupied.into_key(),
RawEntryMut::Vacant(vacant) => {
let sym = backend.intern(src);
vacant
.insert_with_hasher(hash, sym, (), |sym| {
let src = unsafe { backend.get_unchecked(*sym) };
hasher.hash_one(src)
})
.0
}
};
*k
}
pub fn resolve(&self, sym: B::Symbol) -> Option<&T> {
self.backend.get(sym)
}
}
impl<T,B> Default for Interner<T,B>
where
T: Hash + Eq + PartialEq + ?Sized,
B: Backend<T> + Default
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test;