use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
thread_local! {
static SYMBOL_INTERNER: RefCell<HashMap<Box<str>, Arc<str>>> = RefCell::new(HashMap::new());
}
#[must_use]
pub fn intern(s: &str) -> Arc<str> {
SYMBOL_INTERNER.with(|cell| {
let mut table = cell.borrow_mut();
if let Some(arc) = table.get(s) {
return Arc::clone(arc);
}
let arc: Arc<str> = Arc::from(s);
table.insert(s.to_owned().into_boxed_str(), Arc::clone(&arc));
arc
})
}
pub fn clear() {
SYMBOL_INTERNER.with(|cell| cell.borrow_mut().clear());
}
#[must_use]
pub fn size() -> usize {
SYMBOL_INTERNER.with(|cell| cell.borrow().len())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intern_returns_shared_arc() {
let a = intern("foo");
let b = intern("foo");
assert!(Arc::ptr_eq(&a, &b), "same name must share storage");
}
#[test]
fn intern_distinguishes_distinct_names() {
let a = intern("foo");
let b = intern("bar");
assert!(!Arc::ptr_eq(&a, &b));
assert_eq!(&*a, "foo");
assert_eq!(&*b, "bar");
}
#[test]
fn intern_grows_table() {
clear();
assert_eq!(size(), 0);
let _ = intern("alpha");
let _ = intern("beta");
let _ = intern("alpha"); assert_eq!(size(), 2);
}
#[test]
fn clear_drops_table_but_arcs_survive() {
let a = intern("persists");
clear();
assert_eq!(&*a, "persists");
let b = intern("persists");
assert!(!Arc::ptr_eq(&a, &b));
assert_eq!(&*a, &*b);
}
}