use crate::func::{hashing::get_hasher, StraightHashMap};
use crate::ImmutableString;
#[cfg(feature = "no_std")]
use hashbrown::hash_map::Entry;
#[cfg(not(feature = "no_std"))]
use std::collections::hash_map::Entry;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
ops::AddAssign,
};
pub const MAX_INTERNED_STRINGS: usize = 256;
pub const MAX_STRING_LEN: usize = 24;
pub struct StringsInterner<'a> {
pub capacity: usize,
pub max_string_len: usize,
strings: StraightHashMap<ImmutableString>,
dummy: PhantomData<&'a ()>,
}
impl Default for StringsInterner<'_> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for StringsInterner<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.strings.values()).finish()
}
}
impl StringsInterner<'_> {
#[inline(always)]
#[must_use]
pub fn new() -> Self {
Self {
capacity: MAX_INTERNED_STRINGS,
max_string_len: MAX_STRING_LEN,
strings: StraightHashMap::default(),
dummy: PhantomData,
}
}
#[inline(always)]
#[must_use]
pub fn get<S: AsRef<str> + Into<ImmutableString>>(&mut self, text: S) -> ImmutableString {
self.get_with_mapper(Into::into, text)
}
#[inline]
#[must_use]
pub fn get_with_mapper<S: AsRef<str>>(
&mut self,
mapper: impl Fn(S) -> ImmutableString,
text: S,
) -> ImmutableString {
let key = text.as_ref();
if key.len() > MAX_STRING_LEN {
return mapper(text);
}
let hasher = &mut get_hasher();
key.hash(hasher);
let key = hasher.finish();
let result = match self.strings.entry(key) {
Entry::Occupied(e) => return e.get().clone(),
Entry::Vacant(e) => {
let value = mapper(text);
if value.strong_count() > 1 {
return value;
}
e.insert(value).clone()
}
};
if self.strings.len() > self.capacity {
let max = if self.capacity < 5 {
2
} else {
self.capacity - 3
};
while self.strings.len() > max {
let (_, _, n) =
self.strings
.iter()
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
if k != key
&& (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
{
(v.len(), v.strong_count(), k)
} else {
(x, c, n)
}
});
self.strings.remove(&n);
}
}
result
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.strings.len()
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
#[inline(always)]
#[allow(dead_code)]
pub fn clear(&mut self) {
self.strings.clear();
}
}
impl AddAssign<Self> for StringsInterner<'_> {
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.strings.extend(rhs.strings.into_iter());
}
}
impl AddAssign<&Self> for StringsInterner<'_> {
#[inline(always)]
fn add_assign(&mut self, rhs: &Self) {
self.strings
.extend(rhs.strings.iter().map(|(&k, v)| (k, v.clone())));
}
}