spade_common/
interning.rs

1use std::{
2    ops::DerefMut,
3    sync::{LazyLock, Mutex},
4};
5
6use bumpalo::Bump;
7use rustc_hash::{FxBuildHasher, FxHashSet};
8
9pub static INTERNER: LazyLock<Interner> = LazyLock::new(|| Interner::new());
10
11pub struct Interner {
12    inner: Mutex<(FxHashSet<&'static str>, Bump)>,
13}
14
15impl Interner {
16    pub fn new() -> Self {
17        Self {
18            inner: Mutex::new((
19                FxHashSet::with_capacity_and_hasher(256, FxBuildHasher::default()),
20                Bump::new(),
21            )),
22        }
23    }
24
25    pub fn intern(&self, string: &str) -> &'static str {
26        let mut guard = self.inner.lock().unwrap();
27        let (lookup, arena) = DerefMut::deref_mut(&mut guard);
28
29        if let Some(prev) = lookup.get(string) {
30            return prev;
31        }
32
33        let ptr = arena.alloc_str(string);
34        let ptr = unsafe {
35            // SAFETY: the arena lives forever
36            ::core::mem::transmute::<&'_ str, &'static str>(ptr)
37        };
38        lookup.insert(ptr);
39        ptr
40    }
41}