use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
const WELL_KNOWN: &[&str] = &[
"length",
"prototype",
"constructor",
"__proto__",
"toString",
"valueOf",
"hasOwnProperty",
"name",
"message",
"stack",
"undefined",
"null",
"NaN",
"Infinity",
"arguments",
"caller",
"callee",
"apply",
"call",
"bind",
"then",
"catch",
"finally",
"next",
"done",
"value",
"get",
"set",
"writable",
"enumerable",
"configurable",
];
fn seed_pool() -> HashSet<Rc<str>> {
let mut pool = HashSet::with_capacity(WELL_KNOWN.len());
for &s in WELL_KNOWN {
pool.insert(Rc::from(s));
}
pool
}
thread_local! {
static INTERN_POOL: RefCell<HashSet<Rc<str>>> = RefCell::new(seed_pool());
}
pub fn intern(s: &str) -> Rc<str> {
INTERN_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if let Some(existing) = pool.get(s) {
Rc::clone(existing)
} else {
let rc: Rc<str> = s.into();
pool.insert(Rc::clone(&rc));
rc
}
})
}
pub fn clear_intern_pool() {
INTERN_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
pool.clear();
*pool = seed_pool();
});
}
#[inline]
pub fn interned_eq(a: &Rc<str>, b: &Rc<str>) -> bool {
Rc::ptr_eq(a, b)
}
#[inline(always)]
pub fn str_eq_fast(a: &str, b: &str) -> bool {
std::ptr::eq(a, b) || a == b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intern_returns_same_rc() {
let a = intern("hello");
let b = intern("hello");
assert!(Rc::ptr_eq(&a, &b));
}
#[test]
fn test_intern_different_strings() {
let a = intern("foo");
let b = intern("bar");
assert!(!Rc::ptr_eq(&a, &b));
}
#[test]
fn test_interned_eq() {
let a = intern("test");
let b = intern("test");
let c = intern("other");
assert!(interned_eq(&a, &b));
assert!(!interned_eq(&a, &c));
}
#[test]
fn test_well_known_pre_seeded() {
for &name in WELL_KNOWN {
let a = intern(name);
let b = intern(name);
assert!(Rc::ptr_eq(&a, &b), "well-known name {name:?} not interned");
}
}
#[test]
fn test_clear_reseeds_well_known() {
let before = intern("length");
clear_intern_pool();
let after = intern("length");
assert_eq!(&*before, &*after);
assert!(!Rc::ptr_eq(&before, &after));
}
#[test]
fn test_str_eq_fast_same_pointer() {
let s = intern("prototype");
let a: &str = &s;
let b: &str = &s;
assert!(str_eq_fast(a, b));
}
#[test]
fn test_str_eq_fast_different_pointer_same_content() {
let a = String::from("hello");
let b = String::from("hello");
assert!(str_eq_fast(&a, &b));
}
#[test]
fn test_str_eq_fast_different_content() {
assert!(!str_eq_fast("foo", "bar"));
}
}