use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use xxhash_rust::xxh3::Xxh3; use graphene_core::Node; /// Caches the output of a given Node and acts as a proxy #[derive(Default)] pub struct CacheNode { // We have to use an append only data structure to make sure the references // to the cache entries are always valid cache: boxcar::Vec<(u64, T)>, node: CachedNode, } impl<'i, T: 'i, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode where CachedNode: for<'any_input> Node<'any_input, I, Output = T>, { type Output = &'i T; fn eval<'s: 'i>(&'s self, input: I) -> Self::Output { let mut hasher = Xxh3::new(); input.hash(&mut hasher); let hash = hasher.finish(); if let Some((_, cached_value)) = self.cache.iter().find(|(h, _)| *h == hash) { return cached_value; } else { trace!("Cache miss"); let output = self.node.eval(input); let index = self.cache.push((hash, output)); return &self.cache[index].1; } } } impl CacheNode { pub fn new(node: CachedNode) -> CacheNode { CacheNode { cache: boxcar::Vec::new(), node } } } /// Caches the output of a given Node and acts as a proxy /// It provides two modes of operation, it can either be set /// when calling the node with a `Some` variant or the last /// value that was added is returned when calling it with `None` #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct LetNode { // We have to use an append only data structure to make sure the references // to the cache entries are always valid // TODO: We only ever access the last value so there is not really a reason for us // to store the previous entries. This should be reworked in the future cache: boxcar::Vec<(u64, T)>, } impl<'i, T: 'i + Hash> Node<'i, Option> for LetNode { type Output = &'i T; fn eval<'s: 'i>(&'s self, input: Option) -> Self::Output { match input { Some(input) => { let mut hasher = Xxh3::new(); input.hash(&mut hasher); let hash = hasher.finish(); if let Some((cached_hash, cached_value)) = self.cache.iter().last() { if hash == *cached_hash { return cached_value; } } trace!("Cache miss"); let index = self.cache.push((hash, input)); return &self.cache[index].1; } None => &self.cache.iter().last().expect("Let node was not initialized").1, } } } impl LetNode { pub fn new() -> LetNode { LetNode { cache: boxcar::Vec::new() } } } /// Caches the output of a given Node and acts as a proxy #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct EndLetNode { input: Input, } impl<'i, T: 'i, Input> Node<'i, &'i T> for EndLetNode where Input: Node<'i, ()>, { type Output = ::Output; fn eval<'s: 'i>(&'s self, _: &'i T) -> Self::Output { self.input.eval(()) } } impl EndLetNode { pub const fn new(input: Input) -> EndLetNode { EndLetNode { input } } } pub use graphene_core::ops::SomeNode as InitNode; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct RefNode { let_node: Let, _t: PhantomData, } impl<'i, T: 'i, Let> Node<'i, ()> for RefNode where Let: for<'a> Node<'a, Option, Output = &'a T>, { type Output = &'i T; fn eval<'s: 'i>(&'s self, _: ()) -> Self::Output { self.let_node.eval(None) } } impl RefNode { pub const fn new(let_node: Let) -> RefNode { RefNode { let_node, _t: PhantomData } } }