use std::collections::HashMap;
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
pub struct InternedStr(u32);
impl InternedStr {
pub fn as_u32(self) -> u32 {
self.0
}
}
#[derive(Clone, Debug, Default)]
pub struct StringInterner {
strings: Vec<String>,
map: HashMap<String, InternedStr>,
}
impl StringInterner {
pub fn new() -> Self {
Self {
strings: Vec::new(),
map: HashMap::new(),
}
}
pub fn intern(&mut self, s: &str) -> InternedStr {
if let Some(&id) = self.map.get(s) {
return id;
}
let id = InternedStr(self.strings.len() as u32);
self.strings.push(s.to_owned());
self.map.insert(s.to_owned(), id);
id
}
pub fn get(&self, id: InternedStr) -> &str {
&self.strings[id.0 as usize]
}
pub fn lookup(&self, s: &str) -> Option<InternedStr> {
self.map.get(s).copied()
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intern_new_string() {
let mut interner = StringInterner::new();
let id1 = interner.intern("hello");
let id2 = interner.intern("world");
assert_ne!(id1, id2);
assert_eq!(interner.get(id1), "hello");
assert_eq!(interner.get(id2), "world");
}
#[test]
fn test_intern_same_string() {
let mut interner = StringInterner::new();
let id1 = interner.intern("hello");
let id2 = interner.intern("hello");
assert_eq!(id1, id2);
assert_eq!(interner.len(), 1);
}
#[test]
fn test_intern_empty_string() {
let mut interner = StringInterner::new();
let id = interner.intern("");
assert_eq!(interner.get(id), "");
}
}