use std::cell::RefCell;
use std::collections::HashSet;
use std::sync::{Arc, OnceLock, RwLock};
const LOCAL_CAP: usize = 256;
const SWEEP_MIN: usize = 1024;
thread_local! {
static LOCAL_TABLE: RefCell<HashSet<Arc<str>>> =
RefCell::new(HashSet::with_capacity(32));
}
struct Table {
keys: HashSet<Arc<str>>,
sweep_at: usize,
}
impl Table {
fn insert(&mut self, fresh: Arc<str>) {
if self.keys.len() >= self.sweep_at {
self.keys.retain(|key| Arc::strong_count(key) > 1);
self.sweep_at = (self.keys.len() * 2).max(SWEEP_MIN);
}
self.keys.insert(fresh);
}
}
fn table() -> &'static RwLock<Table> {
static TABLE: OnceLock<RwLock<Table>> = OnceLock::new();
TABLE.get_or_init(|| {
RwLock::new(Table {
keys: HashSet::with_capacity(64),
sweep_at: SWEEP_MIN,
})
})
}
#[inline]
fn local_get(s: &str) -> Option<Arc<str>> {
LOCAL_TABLE.with(|local| local.borrow().get(s).cloned())
}
#[inline(never)]
fn local_insert(shared: &Arc<str>) {
LOCAL_TABLE.with(|local| {
let mut local = local.borrow_mut();
if local.len() >= LOCAL_CAP {
local.clear();
}
local.insert(shared.clone());
});
}
#[inline]
pub fn intern(s: &str) -> Arc<str> {
if let Some(existing) = local_get(s) {
return existing;
}
let shared = intern_global(s, Arc::from);
local_insert(&shared);
shared
}
pub fn intern_owned(s: String) -> Arc<str> {
if let Some(existing) = local_get(&s) {
return existing;
}
let shared = intern_global(s, |s: String| Arc::from(s.into_boxed_str()));
local_insert(&shared);
shared
}
#[inline(never)]
fn intern_global<S: AsRef<str>>(s: S, make: impl FnOnce(S) -> Arc<str>) -> Arc<str> {
{
let guard = table().read().unwrap_or_else(|p| p.into_inner());
if let Some(existing) = guard.keys.get(s.as_ref()) {
return existing.clone();
}
}
let mut guard = table().write().unwrap_or_else(|p| p.into_inner());
if let Some(existing) = guard.keys.get(s.as_ref()) {
return existing.clone();
}
let fresh = make(s);
guard.insert(fresh.clone());
fresh
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intern_returns_pointer_equal_arcs() {
let a = intern("first_name");
let b = intern("first_name");
assert!(Arc::ptr_eq(&a, &b));
}
#[test]
fn distinct_keys_get_distinct_arcs() {
let a = intern("alpha");
let b = intern("beta");
assert!(!Arc::ptr_eq(&a, &b));
assert_eq!(&*a, "alpha");
assert_eq!(&*b, "beta");
}
#[test]
fn intern_owned_dedups_with_intern() {
let a = intern("from_borrowed");
let b = intern_owned(String::from("from_borrowed"));
assert!(Arc::ptr_eq(&a, &b));
}
#[test]
fn unreferenced_keys_are_swept() {
for i in 0..100_000 {
drop(intern_owned(format!("one_off_key_{i}")));
}
let held = intern("held_across_sweeps");
for i in 0..100_000 {
drop(intern(&format!("another_one_off_key_{i}")));
}
let len = table().read().unwrap().keys.len();
assert!(
len < 4 * SWEEP_MIN + 4 * LOCAL_CAP,
"table holds {len} keys"
);
assert!(LOCAL_TABLE.with(|local| local.borrow().len()) <= LOCAL_CAP);
assert!(Arc::ptr_eq(
&held,
&intern_owned("held_across_sweeps".into())
));
assert_eq!(&*intern("one_off_key_7"), "one_off_key_7");
}
}