use std::collections::HashMap;
use std::fmt;
use std::sync;
static INDEXD_BUF_SIZE: usize = 256;
static INTERNER: std::sync::LazyLock<sync::Mutex<Interner>> = std::sync::LazyLock::new(|| {
let mut interner = Interner {
str_to_idx: HashMap::new(),
idx_to_str: Vec::new(),
};
for i in 0..INDEXD_BUF_SIZE {
let s = format!("x_{i}");
let s = s.leak();
interner.str_to_idx.insert(s.to_string(), i);
interner.idx_to_str.push(s);
}
sync::Mutex::new(interner)
});
struct Interner {
str_to_idx: HashMap<String, usize>,
idx_to_str: Vec<&'static str>,
}
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct InternStr(usize);
impl InternStr {
pub fn new<S: AsRef<str>>(s: S) -> InternStr {
let mut interner = INTERNER.lock().unwrap();
if let Some(idx) = interner.str_to_idx.get(s.as_ref()) {
InternStr(*idx)
} else {
let s = s.as_ref().to_string();
let idx = interner.idx_to_str.len();
interner.str_to_idx.insert(s.clone(), idx);
interner.idx_to_str.push(Box::leak(Box::new(s)));
InternStr(idx)
}
}
pub fn indexd(idx: usize) -> InternStr {
if idx < INDEXD_BUF_SIZE {
InternStr(idx)
} else {
InternStr::new(format!("x_{idx}").as_str())
}
}
pub fn as_str(&self) -> &'static str {
let interner = INTERNER.lock().unwrap();
interner.idx_to_str[self.0]
}
}
impl fmt::Debug for InternStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Display for InternStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl AsRef<str> for InternStr {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[test]
fn intern_new_test() {
let foo1: &str = "foo";
let foo2: String = "foo".to_string();
let bar1: &str = "bar";
let bar2: String = "bar".to_string();
let s1 = InternStr::new(foo1);
let s2 = InternStr::new(foo2);
let s3 = InternStr::new(bar1);
let s4 = InternStr::new(bar2);
assert_eq!(s1, s2);
assert_eq!(s3, s4);
assert_ne!(s1, s3);
assert_ne!(s2, s4);
assert_eq!(format!("{s1}"), "foo");
assert_eq!(format!("{s2}"), "foo");
assert_eq!(format!("{s3}"), "bar");
assert_eq!(format!("{s4}"), "bar");
}
#[test]
fn intern_indexd_test() {
let s1 = InternStr::indexd(42);
let s2 = InternStr::indexd(42);
let s3 = InternStr::indexd(500);
let s4 = InternStr::indexd(500);
assert_eq!(s1, s2);
assert_eq!(s3, s4);
assert_ne!(s1, s3);
assert_ne!(s2, s4);
assert_eq!(format!("{s1}"), "x_42");
assert_eq!(format!("{s2}"), "x_42");
assert_eq!(format!("{s3}"), "x_500");
assert_eq!(format!("{s4}"), "x_500");
}