use std::sync::RwLock;
pub struct StaticInterner;
struct StaticTable {
entries: Vec<&'static str>,
index: std::collections::HashMap<&'static str, u32>,
}
static STATIC_STRINGS: RwLock<Option<StaticTable>> = RwLock::new(None);
impl StaticInterner {
pub fn intern(s: &str) -> &'static str {
let found: Option<&'static str> = STATIC_STRINGS
.read()
.unwrap()
.as_ref()
.and_then(|t| t.index.get(s).map(|&id| t.entries[id as usize]));
if let Some(text) = found {
return text;
}
let mut guard = STATIC_STRINGS.write().unwrap();
let table = guard.get_or_insert_with(|| StaticTable {
entries: Vec::new(),
index: std::collections::HashMap::new(),
});
if let Some(&id) = table.index.get(s) {
return table.entries[id as usize];
}
let leaked: &'static str = Box::leak(s.to_string().into_boxed_str());
let id = table.entries.len() as u32;
table.entries.push(leaked);
table.index.insert(leaked, id);
leaked
}
pub fn resolve(id: u32) -> &'static str {
STATIC_STRINGS
.read()
.unwrap()
.as_ref()
.and_then(|t| t.entries.get(id as usize).copied())
.unwrap_or("")
}
pub fn len() -> usize {
STATIC_STRINGS
.read()
.unwrap()
.as_ref()
.map_or(0, |t| t.entries.len())
}
}
#[inline]
pub fn static_pair(s: &'static str) -> (u64, u64) {
(s.as_ptr() as usize as u64, s.len() as u64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interning_dedups_and_keeps_bytes_in_place() {
let a = StaticInterner::intern("intern-test-constant");
let b = StaticInterner::intern("intern-test-constant");
assert_eq!(
a.as_ptr(),
b.as_ptr(),
"the same text interns to the same bytes"
);
assert_eq!(a, "intern-test-constant");
let (p, l) = static_pair(a);
assert_eq!(p, a.as_ptr() as usize as u64);
assert_eq!(l, a.len() as u64);
}
}