use super::{GetOrInternWithHint, InternHint, Sym};
use alloc::{
borrow::Borrow,
collections::{btree_map::Entry, BTreeMap},
sync::Arc,
vec::Vec,
};
use core::{cmp::Ordering, mem, ops::Deref};
pub type StringInternerImpl = StringInterner;
mod hint {
#[cold]
#[inline]
pub fn cold() {}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct StringInterner {
string2symbol: BTreeMap<LenOrder, Sym>,
strings: Vec<Arc<str>>,
}
impl GetOrInternWithHint for StringInterner {
fn get_or_intern_with_hint<T>(&mut self, string: T, hint: InternHint) -> Sym
where
T: AsRef<str>,
{
let string = string.as_ref();
match hint {
InternHint::LikelyExists => self.get_or_intern_hint_existing(string),
InternHint::LikelyNew | InternHint::None => self.get_or_intern_hint_new(string),
}
}
}
impl StringInterner {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn len(&self) -> usize {
self.string2symbol.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn get_or_intern_hint_new(&mut self, string: &str) -> Sym {
match self.string2symbol.entry(LenOrder(string.into())) {
Entry::Vacant(entry) => {
let symbol = Sym::from_usize(self.strings.len());
self.strings.push(entry.key().clone().0);
entry.insert(symbol);
symbol
}
Entry::Occupied(entry) => {
hint::cold();
*entry.get()
}
}
}
#[inline]
fn get_or_intern_hint_existing(&mut self, string: &str) -> Sym {
match self.string2symbol.get(<&LenOrderStr>::from(string)) {
Some(symbol) => *symbol,
None => self.intern(string),
}
}
#[cold]
fn intern<T>(&mut self, string: T) -> Sym
where
T: AsRef<str>,
{
let string = string.as_ref();
let symbol = Sym::from_usize(self.strings.len());
let rc_string: Arc<str> = Arc::from(string);
let old = self
.string2symbol
.insert(LenOrder(rc_string.clone()), symbol);
assert!(old.is_none());
self.strings.push(rc_string);
symbol
}
#[inline]
pub fn get<T>(&self, string: T) -> Option<Sym>
where
T: AsRef<str>,
{
self.string2symbol
.get(<&LenOrderStr>::from(string.as_ref()))
.copied()
}
#[inline]
pub fn resolve(&self, symbol: Sym) -> Option<&str> {
self.strings.get(symbol.into_usize()).map(Deref::deref)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct LenOrder(Arc<str>);
impl Ord for LenOrder {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for LenOrder {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl LenOrder {
#[inline]
pub fn as_str(&self) -> &LenOrderStr {
(&*self.0).into()
}
}
#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct LenOrderStr(str);
impl<'a> From<&'a str> for &'a LenOrderStr {
#[inline]
fn from(value: &'a str) -> Self {
unsafe { mem::transmute(value) }
}
}
impl Borrow<LenOrderStr> for LenOrder {
#[inline]
fn borrow(&self) -> &LenOrderStr {
(&*self.0).into()
}
}
impl PartialOrd for LenOrderStr {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for LenOrderStr {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
let lhs = self.0.as_bytes();
let rhs = other.0.as_bytes();
match lhs.len().cmp(&rhs.len()) {
Ordering::Equal => {
if lhs.len() < 8 {
for (l, r) in lhs.iter().zip(rhs) {
match l.cmp(r) {
Ordering::Equal => (),
ordering => return ordering,
}
}
Ordering::Equal
} else {
lhs.cmp(rhs)
}
}
ordering => ordering,
}
}
}