Skip to content

Instantly share code, notes, and snippets.

@stepancheg
Created November 15, 2021 18:20
Show Gist options
  • Save stepancheg/9d2ebac23b27ff8d21a1bcf494b5ac7c to your computer and use it in GitHub Desktop.
Save stepancheg/9d2ebac23b27ff8d21a1bcf494b5ac7c to your computer and use it in GitHub Desktop.
use std::{
borrow::Borrow,
collections::hash_map::DefaultHasher,
fmt,
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
ops::Deref,
ptr,
};
use hashbrown::raw::RawTable;
use parking_lot::{const_mutex, Mutex};
pub struct StaticInterner<T: 'static> {
tables: [Mutex<RawTable<&'static T>>; 64],
}
#[derive(Debug, Ord, PartialOrd)]
pub struct Intern<T: 'static> {
pointer: &'static T,
}
impl<T: 'static> Copy for Intern<T> {}
impl<T: 'static> Clone for Intern<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Deref for Intern<T> {
type Target = T;
fn deref(&self) -> &T {
self.pointer
}
}
impl<T> PartialEq for Intern<T> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.pointer, other.pointer)
}
}
impl<T> Eq for Intern<T> {}
impl<T: Display> Display for Intern<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.pointer, f)
}
}
struct Hashed<T> {
hash: u64,
value: T,
}
impl<T: Hash> Hashed<T> {
fn hash(value: &T) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
fn new(value: T) -> Self {
let hash = Self::hash(&value);
Hashed { hash, value }
}
}
pub trait Equiv<Q: ?Sized> {
fn equivalent(&self, key: &Q) -> bool;
}
impl<Q: ?Sized, K: ?Sized> Equiv<K> for Q
where
Q: Eq,
K: Borrow<Q>,
{
#[inline]
fn equivalent(&self, key: &K) -> bool {
*self == *key.borrow()
}
}
impl<T: 'static> StaticInterner<T> {
/// Create a new interner for given type.
pub const fn new() -> StaticInterner<T> {
StaticInterner {
tables: [
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
const_mutex(RawTable::new()),
],
}
}
pub fn intern<Q>(&'static self, value: Q) -> Intern<T>
where
Q: Hash + Equiv<T> + Into<T>,
T: Eq + Hash,
{
let hashed = Hashed::new(value);
let table = &self.tables[hashed.hash as usize % self.tables.len()];
let mut guard = table.lock();
if let Some(t) = guard.find(hashed.hash, |t| hashed.value.equivalent(*t)) {
return Intern {
pointer: unsafe { t.as_ref() },
};
}
let pointer = Box::leak(Box::new(hashed.value.into()));
guard.insert(hashed.hash, pointer, |t| Hashed::<T>::hash(*t));
Intern { pointer }
}
}
#[cfg(test)]
mod test {
use crate::StaticInterner;
static STRING_INTERNER: StaticInterner<String> = StaticInterner::new();
#[test]
fn test_intern() {
assert_eq!(
STRING_INTERNER.intern("hello".to_owned()),
STRING_INTERNER.intern("hello".to_owned())
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment