use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Symbol(u32);
impl Symbol {
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_raw(index: u32) -> Self {
Self(index)
}
}
impl PartialOrd for Symbol {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Symbol {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
#[derive(Debug, Clone, Default)]
pub struct Interner {
map: HashMap<String, Symbol>,
strings: Vec<String>,
}
impl Interner {
pub fn new() -> Self {
Self::default()
}
pub fn intern(&mut self, s: &str) -> Symbol {
if let Some(&sym) = self.map.get(s) {
return sym;
}
let sym = Symbol(self.strings.len() as u32);
self.strings.push(s.to_owned());
self.map.insert(s.to_owned(), sym);
sym
}
pub fn intern_owned(&mut self, s: String) -> Symbol {
if let Some(&sym) = self.map.get(&s) {
return sym;
}
let sym = Symbol(self.strings.len() as u32);
self.strings.push(s.clone());
self.map.insert(s, sym);
sym
}
#[inline]
pub fn resolve(&self, sym: Symbol) -> &str {
&self.strings[sym.0 as usize]
}
#[inline]
pub fn try_resolve(&self, sym: Symbol) -> Option<&str> {
self.strings.get(sym.0 as usize).map(|s| s.as_str())
}
#[inline]
pub fn len(&self) -> usize {
self.strings.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (Symbol, &str)> {
self.strings
.iter()
.enumerate()
.map(|(i, s)| (Symbol(i as u32), s.as_str()))
}
pub fn to_blob(&self) -> (Vec<u8>, Vec<u32>) {
let mut blob = Vec::new();
let mut offsets = Vec::with_capacity(self.strings.len() + 1);
for s in &self.strings {
offsets.push(blob.len() as u32);
blob.extend_from_slice(s.as_bytes());
}
offsets.push(blob.len() as u32);
(blob, offsets)
}
}