use std::collections::HashMap;
use std::hash::{BuildHasher, RandomState};
use hashbrown::HashTable;
use crate::ItemKind;
pub(crate) type Sym = u32;
pub(crate) type PathId = u32;
#[derive(Default)]
pub(crate) struct Interner {
syms: HashMap<Box<str>, Sym>,
names: Vec<Box<str>>,
arena: Vec<Sym>,
ends: Vec<u32>,
paths: HashTable<PathId>,
state: RandomState,
kinds: Vec<ItemKind>,
children: HashMap<(PathId, Sym), PathId>,
scratch: Vec<Sym>,
}
impl Interner {
pub(crate) fn sym(&mut self, name: &str) -> Sym {
if let Some(&sym) = self.syms.get(name) {
return sym;
}
let sym = Sym::try_from(self.names.len()).expect("fewer than 2^32 identifiers");
self.names.push(name.into());
self.syms.insert(name.into(), sym);
sym
}
pub(crate) fn find_sym(&self, name: &str) -> Option<Sym> {
self.syms.get(name).copied()
}
pub(crate) fn name(&self, sym: Sym) -> &str {
&self.names[index(sym)]
}
pub(crate) fn path(&mut self, segments: &[Sym], kind: ItemKind) -> PathId {
let hash = self.state.hash_one(segments);
let known = self.paths.find(hash, |&known| {
span(&self.arena, &self.ends, known) == segments
});
if let Some(&id) = known {
return id;
}
let id = PathId::try_from(self.ends.len()).expect("fewer than 2^32 paths");
self.arena.extend_from_slice(segments);
let end = u32::try_from(self.arena.len()).expect("fewer than 2^32 segments");
self.ends.push(end);
let Self {
arena,
ends,
paths,
state,
..
} = self;
paths.insert_unique(hash, id, |&known| state.hash_one(span(arena, ends, known)));
self.kinds.push(kind);
id
}
pub(crate) fn find_path(&self, segments: &[Sym]) -> Option<PathId> {
let hash = self.state.hash_one(segments);
self.paths
.find(hash, |&known| {
span(&self.arena, &self.ends, known) == segments
})
.copied()
}
pub(crate) fn child(&mut self, parent: PathId, last: Sym, kind: ItemKind) -> PathId {
if let Some(&id) = self.children.get(&(parent, last)) {
return id;
}
let mut path = std::mem::take(&mut self.scratch);
path.clear();
path.extend_from_slice(self.segments(parent));
path.push(last);
let id = self.path(&path, kind);
self.scratch = path;
self.children.insert((parent, last), id);
id
}
pub(crate) fn segments(&self, id: PathId) -> &[Sym] {
span(&self.arena, &self.ends, id)
}
pub(crate) fn kind(&self, id: PathId) -> ItemKind {
self.kinds[index(id)]
}
pub(crate) fn display(&self, id: PathId) -> String {
self.join(self.segments(id))
}
pub(crate) fn join(&self, segments: &[Sym]) -> String {
let mut out = String::new();
for (i, &sym) in segments.iter().enumerate() {
if i > 0 {
out.push_str("::");
}
out.push_str(self.name(sym));
}
out
}
}
fn index(id: u32) -> usize {
usize::try_from(id).expect("u32 ids fit in usize on supported targets")
}
fn span<'a>(arena: &'a [Sym], ends: &[u32], id: PathId) -> &'a [Sym] {
let id = index(id);
let start = id.checked_sub(1).map_or(0, |before| index(ends[before]));
&arena[start..index(ends[id])]
}