use rustc_hash::FxHashMap;
pub(super) struct StringInterner {
data: Vec<u8>,
table: Vec<(u32, u32)>,
lookup: FxHashMap<String, u32>,
}
impl StringInterner {
pub(super) fn new() -> Self {
let mut interner = Self {
data: Vec::new(),
table: Vec::new(),
lookup: FxHashMap::default(),
};
interner.table.push((0, 0));
interner.lookup.insert(String::new(), 0);
interner
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn intern(&mut self, s: &str) -> u32 {
if let Some(&id) = self.lookup.get(s) {
return id;
}
let offset = self.data.len() as u32;
let len = s.len() as u32;
self.data.extend_from_slice(s.as_bytes());
let id = self.table.len() as u32;
self.table.push((offset, len));
self.lookup.insert(s.to_string(), id);
id
}
pub(super) fn resolve(&self, id: u32) -> &str {
let (offset, len) = self.table[id as usize];
let bytes = &self.data[offset as usize..(offset + len) as usize];
std::str::from_utf8(bytes).unwrap_or("")
}
pub(super) fn heap_size_estimate(&self) -> usize {
let mut total = self.data.capacity();
total += self.table.capacity() * std::mem::size_of::<(u32, u32)>();
total += self.lookup.capacity()
* (std::mem::size_of::<String>() + std::mem::size_of::<u32>() + 1);
for key in self.lookup.keys() {
total += key.capacity();
}
total
}
}