Created
November 15, 2021 18:20
-
-
Save stepancheg/9d2ebac23b27ff8d21a1bcf494b5ac7c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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