use ahash::AHashMap;
use std::marker::PhantomData;
#[derive(Debug)]
pub struct Interner<Id: Copy + From<u32> + Into<u32>> {
table: Vec<String>, index: AHashMap<String, u32>, _marker: PhantomData<fn() -> Id>,
}
impl<Id: Copy + From<u32> + Into<u32>> Interner<Id> {
pub fn new() -> Self {
Self {
table: Vec::new(),
index: AHashMap::new(),
_marker: PhantomData,
}
}
#[allow(clippy::expect_used)]
pub fn intern(&mut self, s: &str) -> Id {
if let Some(&id) = self.index.get(s) {
return Id::from(id);
}
let id = u32::try_from(self.table.len())
.expect("interner overflowed u32 — schemas should be small");
self.table.push(s.to_owned());
self.index.insert(s.to_owned(), id);
Id::from(id)
}
pub fn get(&self, s: &str) -> Option<Id> {
self.index.get(s).copied().map(Id::from)
}
#[must_use]
pub fn lookup(&self, id: Id) -> &str {
let idx: u32 = id.into();
&self.table[idx as usize]
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.table.len()
}
}
impl<Id: Copy + From<u32> + Into<u32>> Default for Interner<Id> {
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::cast_possible_truncation)]
impl From<u32> for crate::KindId {
fn from(v: u32) -> Self {
Self(v as u16)
}
}
impl From<crate::KindId> for u32 {
fn from(v: crate::KindId) -> Self {
Self::from(v.0)
}
}
#[allow(clippy::cast_possible_truncation)]
impl From<u32> for crate::AttrId {
fn from(v: u32) -> Self {
Self(v as u16)
}
}
impl From<crate::AttrId> for u32 {
fn from(v: crate::AttrId) -> Self {
Self::from(v.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AttrId;
#[test]
fn interns_and_dedupes() {
let mut i: Interner<AttrId> = Interner::new();
let a = i.intern("audience");
let b = i.intern("audience");
let c = i.intern("dwell");
assert_eq!(a, b);
assert_ne!(a, c);
assert_eq!(i.len(), 2);
}
#[test]
fn lookup_round_trips() {
let mut i: Interner<AttrId> = Interner::new();
let id = i.intern("foo");
assert_eq!(i.lookup(id), "foo");
}
#[test]
fn get_does_not_insert() {
let i = Interner::<AttrId>::new();
assert!(i.get("missing").is_none());
assert_eq!(i.len(), 0);
}
}