}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Collect)]
#[collect(require_static)]
pub struct SymbolId(usize, usize);
/// A symbol table (that can only grow!)
pub struct SymbolTable(usize, Vec<Box<str>>);
impl Default for SymbolTable {
fn default() -> Self {
static SYMBOL_TABLE_COUNTER: OnceLock<AtomicUsize> = OnceLock::new();
let counter = SYMBOL_TABLE_COUNTER.get_or_init(|| AtomicUsize::new(0));
let id = counter.fetch_add(1, Ordering::SeqCst);
// Start off with room for 100 symbols
Self(id, Vec::with_capacity(100))
}
}
impl SymbolTable {
/// If a valid symbol, get the id, otherwise, create a new id referencing the symbol
pub fn id<S: AsRef<str>>(&mut self, source: S) -> SymbolId {
if let Some(pos) = self.1.iter().position(|s| s.as_ref() == source.as_ref()) {
SymbolId(self.0, pos)
} else {
let new_pos = self.1.len();
self.1.push(source.as_ref().into());
SymbolId(self.0, new_pos)
}
}
/// Tries to access the underlying string for a given [`SymbolId`]
///
/// Fails if from the wrong table
pub fn symbol(&self, id: SymbolId) -> Option<&str> {
let SymbolId(tid, idx) = id;
// Just use a raw access, as we are the only ones who can hand out
// SymbolIds with the right tid
// no unsafe version, cuz it is trivially easy to misuse,
// and the safe version only needs an integer compare, and *possibly* a bounds check
(tid == self.0).then(|| self.1[idx].as_ref())
}