use core::cell::RefCell;
use std::collections::HashSet;
struct InternTable {
set: HashSet<crate::Rc<str>>,
}
impl InternTable {
fn new() -> Self {
Self {
set: HashSet::new(),
}
}
fn intern(&mut self, rc: crate::Rc<str>) -> crate::Rc<str> {
if let Some(existing) = self.set.get(rc.as_ref()) {
return crate::Rc::clone(existing);
}
self.set.insert(crate::Rc::clone(&rc));
rc
}
}
std::thread_local! {
static TABLE: RefCell<Option<InternTable>> = const { RefCell::new(None) };
}
pub struct InternGuard {
installed_by_us: bool,
}
impl InternGuard {
pub fn install() -> Self {
let installed_by_us = TABLE.with(|t| {
let mut slot = t.borrow_mut();
if slot.is_none() {
*slot = Some(InternTable::new());
true
} else {
false
}
});
Self { installed_by_us }
}
}
impl Drop for InternGuard {
fn drop(&mut self) {
if self.installed_by_us {
TABLE.with(|t| {
*t.borrow_mut() = None;
});
}
}
}
pub fn intern_key(rc: crate::Rc<str>) -> crate::Rc<str> {
TABLE.with(|t| match t.borrow_mut().as_mut() {
Some(table) => table.intern(rc),
None => rc,
})
}
#[cfg(test)]
pub fn is_installed() -> bool {
TABLE.with(|t| t.borrow().is_some())
}