use hashbrown::HashMap;
use std::{
any::{Any, TypeId},
hash::{BuildHasher, Hasher},
ptr::NonNull,
sync::RwLock,
};
struct TypeIdHasher(u64);
impl Hasher for TypeIdHasher {
#[inline(always)]
fn finish(&self) -> u64 {
self.0
}
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.write_u8(byte);
}
}
#[inline(always)]
fn write_u8(&mut self, i: u8) {
self.0 = self.0.rotate_left(8);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u16(&mut self, i: u16) {
self.0 = self.0.rotate_left(16);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u32(&mut self, i: u32) {
self.0 = self.0.rotate_left(32);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u64(&mut self, i: u64) {
self.0 ^= i;
}
#[inline(always)]
fn write_u128(&mut self, i: u128) {
self.write_u64(i as u64);
self.write_u64((i >> 64) as u64);
}
#[inline(always)]
fn write_usize(&mut self, i: usize) {
match usize::BITS {
128 => self.write_u128(i as u128),
64 => self.write_u64(i as u64),
32 => self.write_u32(i as u32),
16 => self.write_u16(i as u16),
_ => self.write(&i.to_ne_bytes()),
}
}
}
struct TypeIdBuildHasher;
impl BuildHasher for TypeIdBuildHasher {
type Hasher = TypeIdHasher;
fn build_hasher(&self) -> Self::Hasher {
TypeIdHasher(0)
}
}
struct Value(NonNull<dyn Any + Send + Sync>);
impl Value {
unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) {
unsafe { &*self.0.as_ptr() }
}
fn new(v: Box<dyn Any + Send + Sync>) -> Self {
unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) }
}
}
unsafe impl Send for Value {}
unsafe impl Sync for Value {}
impl Drop for Value {
fn drop(&mut self) {
unsafe { std::ptr::drop_in_place(self.0.as_ptr()) }
}
}
pub struct TypeIdMap(RwLock<HashMap<TypeId, Value, TypeIdBuildHasher>>);
impl TypeIdMap {
pub const fn new() -> Self {
Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher)))
}
#[cold]
unsafe fn insert_slow(
&self,
type_id: TypeId,
make: fn() -> Box<dyn Any + Sync + Send>,
) -> &(dyn Any + Sync + Send) {
let value = Value::new(make());
let mut write_guard = self.0.write().unwrap();
unsafe {
write_guard
.entry(type_id)
.or_insert(value)
.get_transmute_lifetime()
}
}
pub fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
let type_id = TypeId::of::<T>();
let read_guard = self.0.read().unwrap();
let retval = read_guard
.get(&type_id)
.map(|v| unsafe { Value::get_transmute_lifetime(v) });
drop(read_guard);
let retval = match retval {
Some(retval) => retval,
None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) },
};
unsafe { &*(retval as *const dyn Any as *const T) }
}
}
impl Default for TypeIdMap {
fn default() -> Self {
Self::new()
}
}