use dashmap::{DashMap, mapref::entry::Entry};
use ahash::AHasher;
use std::hash::{Hash, Hasher, BuildHasherDefault};
use std::convert::Infallible;
use either::Either;
use super::node::{WeakNode, Node, Data};
use crate::util::PassThroughHasher;
use crate::value::{ValueDesc, ValueData, ValId};
pub trait CacheType<T> {
type UnderlyingMap;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct GlobalCache;
impl<T> CacheType<T> for GlobalCache {
type UnderlyingMap = DashMap<u64, WeakNode<T>, BuildHasherDefault<PassThroughHasher>>;
}
#[derive(Debug, Clone)]
pub struct NodeCache<T, C: CacheType<T> = GlobalCache> {
pub map: C::UnderlyingMap,
pub keys: (u64, u64)
}
pub type CacheEntry<'a, T> =
Entry<'a, u64, WeakNode<T>, BuildHasherDefault<PassThroughHasher>>;
pub trait CacheAcceptor<T> {
fn accept(self, weak: WeakNode<T>);
}
impl<'a, T> CacheAcceptor<T> for CacheEntry<'a, T> {
fn accept(self, weak: WeakNode<T>) {
*(self.or_default()) = weak;
}
}
impl<T> CacheAcceptor<T> for () {
fn accept(self, _weak: WeakNode<T>) {}
}
impl<T> NodeCache<T, GlobalCache> {
pub fn with_keys(keys: (u64, u64)) -> NodeCache<T> {
NodeCache { map: DashMap::default(), keys }
}
pub fn new() -> NodeCache<T> { Self::with_keys((0, 0)) }
pub fn cached_entry<'a, K>(&'a self, key: &K) -> Either<Node<T>, CacheEntry<'a, T>>
where K: Hash {
let hash = {
let mut hasher = AHasher::new_with_keys(self.keys.0, self.keys.1);
key.hash(&mut hasher);
hasher.finish()
};
let entry = self.map.entry(hash);
if let Entry::Occupied(occupied) = &entry {
if let Some(node) = occupied.get().upgrade() {
return Either::Left(node)
}
}
Either::Right(entry)
}
pub fn cached_constructor<D, E, F>(&self, data: D, constructor: F) -> Result<Node<T>, E>
where D: Hash, F: FnOnce(D) -> Result<Node<T>, E> {
let entry = match self.cached_entry(&data) {
Either::Left(node) => return Ok(node),
Either::Right(entry) => entry
};
let node = constructor(data)?;
*(entry.or_default()) = node.downgrade();
Ok(node)
}
pub fn remove<K>(&self, key: &K) -> Option<(u64, WeakNode<T>)> where K: Hash {
let mut hasher = AHasher::new_with_keys(self.keys.0, self.keys.1);
key.hash(&mut hasher);
let cache_key = hasher.finish();
self.map.remove(&cache_key)
}
#[inline(always)] pub fn cached_desc<D>(&self, desc: D) -> Result<Node<T>, T::CacheError>
where D: Hash, T: CacheBy<D> {
self.cached_constructor(desc, T::node_to_cache)
}
}
pub trait CacheBy<K: Hash> {
type CacheError;
fn node_to_cache(key: K) -> Result<Node<Self>, Self::CacheError>;
}
impl<V> CacheBy<V> for ValueData where V: ValueDesc + Hash {
type CacheError = V::Err;
#[inline(always)] fn node_to_cache(key: V) -> Result<ValId, V::Err> { key.to_node() }
}
impl<T: Hash> CacheBy<Data<T>> for Data<T> {
type CacheError = Infallible;
#[inline(always)] fn node_to_cache(key: Data<T>) -> Result<Node<Data<T>>, Infallible> {
Ok(Node::new(key))
}
}
#[cfg(test)]
mod tests {
use crate::util::AlwaysOk;
use super::*;
#[test]
fn bools_cache_properly() {
let cache = NodeCache::<Data<bool>>::new();
let dt = Data(true);
let df = Data(false);
let nt = cache.cached_desc(dt).aok();
let pass = cache.cached_constructor(Data(true), |_| Err(()))
.expect("Valid key, should not generate");
assert_eq!(nt, pass);
cache.cached_constructor(Data(false), |_| Err(()))
.expect_err("Invalid key, generator always errors");
let nf = cache.cached_desc(df).aok();
let pass = cache.cached_constructor(Data(false), |_| Err(()))
.expect("Valid key, should not generate");
assert_eq!(nf, pass);
let nt2 = cache.cached_desc(dt).aok();
let nf2 = cache.cached_desc(df).aok();
assert_eq!(nt, nt2);
assert_eq!(nf, nf2);
assert_ne!(nt, nf);
let mnt = Node::new(dt);
let mnf = Node::new(df);
assert_ne!(nt, mnt);
assert_ne!(nf, mnt);
assert_ne!(nt, mnf);
assert_ne!(nf, mnf);
let (_hash, ntr) = cache.remove(&dt).expect("Data(true) was previously inserted");
assert_eq!(ntr.upgrade().expect("We hold a reference to Data(true)"), nt);
let nt3 = cache.cached_desc(dt).expect("Valid key");
let nf3 = cache.cached_desc(df).expect("Valid key");
assert_ne!(nt, nt3);
assert_eq!(nf, nf3);
}
}