use {
rustc_hash::FxBuildHasher,
std::{
borrow::Borrow,
fmt,
hash::{BuildHasher, Hash},
sync::RwLock,
},
};
pub struct Interner<T> {
inner: RwLock<crate::Interner<T>>,
}
impl<T> Default for Interner<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Interner<T> {
#[must_use]
pub const fn new() -> Self {
Self { inner: RwLock::new(crate::Interner::new()) }
}
#[expect(clippy::missing_panics_doc)]
pub fn len(&self) -> usize {
self.inner.read().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn insert_arena(&self, value: T) -> &mut T {
let inner = self.inner.write().unwrap();
unsafe { longer_mut(inner.insert_arena(value)) }
}
}
impl<T: Hash + Eq> Interner<T> {
#[expect(clippy::missing_panics_doc)]
#[must_use]
pub fn try_resolve<Q>(&self, value: &Q) -> Option<&T>
where
T: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.inner.read().unwrap().try_resolve(value).map(|cached| unsafe { longer(cached) })
}
#[expect(clippy::missing_panics_doc, clippy::readonly_write_lock)]
pub fn intern(&self, value: T) -> &T {
let hash = FxBuildHasher.hash_one(&value);
let inner = self.inner.read().unwrap();
if let Some(cached) = inner.try_resolve_with(&value, hash) {
return unsafe { longer(cached) };
}
drop(inner);
let inner = self.inner.write().unwrap();
if let Some(cached) = inner.try_resolve_with(&value, hash) {
return unsafe { longer(cached) };
}
unsafe { longer(inner.insert(hash, value)) }
}
#[expect(clippy::missing_panics_doc, clippy::readonly_write_lock)]
pub fn intern_new(&self, value: T) -> &T {
let hash = FxBuildHasher.hash_one(&value);
let inner = self.inner.write().unwrap();
unsafe { longer(inner.insert(hash, value)) }
}
pub fn intern_ref_unique(&self, value: &'static T) {
self.inner.write().unwrap().intern_ref_unique(value)
}
}
impl<T: fmt::Debug> fmt::Debug for Interner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Ok(inner) = self.inner.try_read() else {
return f.debug_set().finish_non_exhaustive();
};
inner.fmt(f)
}
}
unsafe fn longer<'b, T>(short: &T) -> &'b T {
unsafe { std::mem::transmute(short) }
}
unsafe fn longer_mut<'b, T>(short: &mut T) -> &'b mut T {
unsafe { std::mem::transmute(short) }
}
unsafe impl<T: Send> Send for Interner<T> {}
unsafe impl<T: Sync> Sync for Interner<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn addr() {
static INTERNER: Interner<i32> = Interner::new();
let a1: *const _ = INTERNER.intern(1);
let b1: *const _ = INTERNER.intern(1);
INTERNER.intern(2);
assert!(INTERNER.try_resolve(&1) == Some(&1));
assert_eq!(a1.addr(), b1.addr());
}
#[test]
fn recursive() {
#[derive(Debug, PartialEq, Eq, Hash)]
enum Type<'tcx> {
Int,
Array(&'tcx Type<'tcx>),
}
let interner = Interner::new();
let int = interner.intern(Type::Int);
let array = interner.intern(Type::Array(int));
println!("{array:?}");
}
#[test]
fn insert_arena() {
let interner = Interner::new();
let a1: *const _ = interner.insert_arena(1);
let a2: *const _ = interner.intern(1);
assert_ne!(a1.addr(), a2.addr());
}
#[test]
fn test_grow() {
let interner = Interner::new();
let one: *const _ = interner.intern(1);
for i in 2..1000 {
interner.intern(i);
}
let one2: *const _ = interner.intern(1);
assert_eq!(one.addr(), one2.addr())
}
#[test]
fn test_intern_ref_unique() {
let interner = Interner::new();
static ONE: i32 = 1;
let a1 = &ONE;
interner.intern_ref_unique(a1);
let a2: *const _ = interner.intern(1);
assert_eq!((a1 as *const i32).addr(), a2.addr());
}
}