use rustc_hash::{FxHashMap, FxHasher};
use std::{
cell::RefCell,
collections::hash_map::Entry,
hash::{Hash, Hasher},
};
use crate::context::{Arena, ArenaIndex};
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
pub struct TypeValueHash {
hash: u64,
}
impl TypeValueHash {
pub fn new<T: Hash + 'static>(t: &T) -> TypeValueHash {
let mut hasher = FxHasher::default();
t.hash(&mut hasher);
std::any::TypeId::of::<T>().hash(&mut hasher);
TypeValueHash {
hash: hasher.finish(),
}
}
}
impl From<TypeValueHash> for u64 {
fn from(value: TypeValueHash) -> Self {
value.hash
}
}
pub type UniqueStoreEq<'a, T> = &'a dyn Fn(&T, &T) -> bool;
pub type UniqueStoreIs<'a, T> = &'a dyn Fn(&T) -> bool;
pub(crate) struct UniqueStore<T: 'static> {
pub(crate) unique_store: Arena<T>,
pub(crate) unique_stores_map: FxHashMap<TypeValueHash, Vec<ArenaIndex>>,
}
impl<T: 'static> Default for UniqueStore<T> {
fn default() -> Self {
Self {
unique_store: Default::default(),
unique_stores_map: Default::default(),
}
}
}
impl<T: 'static> UniqueStore<T> {
pub(crate) fn get_or_create_unique(
&mut self,
t: T,
hash: TypeValueHash,
eq: UniqueStoreEq<T>,
) -> ArenaIndex {
match self.unique_stores_map.entry(hash) {
Entry::Occupied(mut possible_matches) => {
let index_opt = possible_matches.get().iter().find_map(|index| {
let iref = &*self.unique_store.get(*index).unwrap().borrow();
if eq(&t, iref) { Some(*index) } else { None }
});
if let Some(index) = index_opt {
index
} else {
let index = self.unique_store.insert(RefCell::new(t));
possible_matches.get_mut().push(index);
index
}
}
Entry::Vacant(slot) => {
let new_index = self.unique_store.insert(RefCell::new(t));
slot.insert(vec![new_index]);
new_index
}
}
}
pub(crate) fn get(&self, hash: TypeValueHash, is: UniqueStoreIs<T>) -> Option<ArenaIndex> {
self.unique_stores_map
.get(&hash)
.and_then(|mv| {
mv.iter()
.find(|other| is(&*self.unique_store.get(**other).unwrap().borrow()))
})
.copied()
}
}
#[cfg(test)]
mod tests {
use super::{TypeValueHash, UniqueStore};
#[test]
fn test_type_value_hash() {
let u32_tvh = TypeValueHash::new(&0u32);
assert!(u32_tvh == TypeValueHash::new(&0u32));
assert!(u32_tvh != TypeValueHash::new(&0i32));
assert!(u32_tvh != TypeValueHash::new(&0u64));
assert!(u32_tvh != TypeValueHash::new(&1u32));
assert!(u64::from(u32_tvh) != u64::from(TypeValueHash::new(&0i32)));
assert!(u64::from(u32_tvh) != u64::from(TypeValueHash::new(&0u64)));
assert!(u64::from(u32_tvh) != u64::from(TypeValueHash::new(&1u32)));
}
#[test]
fn test_unique_store() {
let mut u32_store = UniqueStore::<u32>::default();
let u32_0_idx = u32_store.get_or_create_unique(0, TypeValueHash::new(&0u32), &u32::eq);
let u32_1_idx = u32_store.get_or_create_unique(1, TypeValueHash::new(&1u32), &u32::eq);
let u32_0_1_idx = u32_store.get_or_create_unique(0, TypeValueHash::new(&0u32), &u32::eq);
assert!(u32_0_idx == u32_0_1_idx && u32_0_idx != u32_1_idx);
let u32_0_2_idx = u32_store
.get(TypeValueHash::new(&0u32), &|x| *x == 0)
.unwrap();
let u32_1_2_idx = u32_store
.get(TypeValueHash::new(&1u32), &|x| *x == 1)
.unwrap();
assert!(u32_0_2_idx == u32_0_idx && u32_1_2_idx == u32_1_idx);
assert!(
u32_store
.get(TypeValueHash::new(&2u32), &|x| *x == 2)
.is_none()
);
}
}