use crate::{Node, WasmNotSend}; use dyn_any::DynFuture; use std::future::Future; use std::hash::DefaultHasher; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::sync::Arc; use std::sync::Mutex; /// Caches the output of a given Node and acts as a proxy #[derive(Default)] pub struct MemoNode { cache: Arc>>, node: CachedNode, } impl<'i, I: Hash + 'i, T: 'i + Clone + WasmNotSend, CachedNode: 'i> Node<'i, I> for MemoNode where CachedNode: for<'any_input> Node<'any_input, I>, for<'a> >::Output: Future + WasmNotSend, { // TODO: This should return a reference to the cached cached_value // but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD type Output = DynFuture<'i, T>; fn eval(&'i self, input: I) -> Self::Output { let mut hasher = DefaultHasher::new(); input.hash(&mut hasher); let hash = hasher.finish(); if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) { Box::pin(async move { data }) } else { let fut = self.node.eval(input); let cache = self.cache.clone(); Box::pin(async move { let value = fut.await; *cache.lock().unwrap() = Some((hash, value.clone())); value }) } } fn reset(&self) { self.cache.lock().unwrap().take(); } } impl MemoNode { pub fn new(node: CachedNode) -> MemoNode { MemoNode { cache: Default::default(), node } } } pub mod memo { pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::MemoNode"); } /// Caches the output of a given Node and acts as a proxy. /// In contrast to the regular `MemoNode`. This node ignores all input. /// Using this node might result in the document not updating properly, /// use with caution. #[derive(Default)] pub struct ImpureMemoNode { cache: Arc>>, node: CachedNode, _phantom: std::marker::PhantomData, } impl<'i, I: 'i, T: 'i + Clone + WasmNotSend, CachedNode: 'i> Node<'i, I> for ImpureMemoNode where CachedNode: for<'any_input> Node<'any_input, I>, for<'a> >::Output: Future + WasmNotSend, { // TODO: This should return a reference to the cached cached_value // but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD type Output = DynFuture<'i, T>; fn eval(&'i self, input: I) -> Self::Output { if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() { let data = cached_value.clone(); Box::pin(async move { data }) } else { let fut = self.node.eval(input); let cache = self.cache.clone(); Box::pin(async move { let value = fut.await; *cache.lock().unwrap() = Some(value.clone()); value }) } } fn reset(&self) { self.cache.lock().unwrap().take(); } } impl ImpureMemoNode { pub fn new(node: CachedNode) -> ImpureMemoNode { ImpureMemoNode { cache: Default::default(), node, _phantom: std::marker::PhantomData, } } } pub mod impure_memo { pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode"); } #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum IntrospectMode { Input, Data, } /// Caches the output of the last graph evaluation for introspection #[derive(Default)] pub struct MonitorNode { #[allow(clippy::type_complexity)] input: Arc>>>, output: Arc>>>, // Gets set to true by the editor when before evaluating the network, then reset when the monitor node is evaluated introspect_input: Arc>, introspect_output: Arc>, node: N, } impl<'i, I, O, N> Node<'i, I> for MonitorNode where I: Clone + 'static + Send + Sync, O: Clone + 'static + Send + Sync, for<'a> N: Node<'a, I, Output: Future + WasmNotSend> + Send + Sync + 'i, { type Output = DynFuture<'i, O>; fn eval(&'i self, input: I) -> Self::Output { Box::pin(async move { let output = self.node.eval(input.clone()).await; let mut introspect_input = self.introspect_input.lock().unwrap(); if *introspect_input { *self.input.lock().unwrap() = Some(Box::new(input)); *introspect_input = false; } let mut introspect_output = self.introspect_output.lock().unwrap(); if *introspect_output { *self.output.lock().unwrap() = Some(Box::new(output.clone())); *introspect_output = false; } output }) } // After introspecting, the input/output get set to None because the Arc is moved to the editor where it can be directly accessed. fn introspect(&self, introspect_mode: IntrospectMode) -> Option> { match introspect_mode { IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Box), IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Box), } } fn set_introspect(&self, introspect_mode: IntrospectMode) { match introspect_mode { IntrospectMode::Input => *self.introspect_input.lock().unwrap() = true, IntrospectMode::Data => *self.introspect_output.lock().unwrap() = true, } } } impl MonitorNode { pub fn new(node: N) -> MonitorNode { MonitorNode { input: Arc::new(Mutex::new(None)), output: Arc::new(Mutex::new(None)), introspect_input: Arc::new(Mutex::new(false)), introspect_output: Arc::new(Mutex::new(false)), node, } } } pub mod monitor { pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::MonitorNode"); } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct MemoHash { hash: u64, value: T, } impl<'de, T: serde::Deserialize<'de> + Hash> serde::Deserialize<'de> for MemoHash { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { T::deserialize(deserializer).map(|value| Self::new(value)) } } impl serde::Serialize for MemoHash { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { self.value.serialize(serializer) } } impl MemoHash { pub fn new(value: T) -> Self { let hash = Self::calc_hash(&value); Self { hash, value } } pub fn new_with_hash(value: T, hash: u64) -> Self { Self { hash, value } } fn calc_hash(data: &T) -> u64 { let mut hasher = DefaultHasher::new(); data.hash(&mut hasher); hasher.finish() } pub fn inner_mut(&mut self) -> MemoHashGuard<'_, T> { MemoHashGuard { inner: self } } pub fn into_inner(self) -> T { self.value } pub fn hash_code(&self) -> u64 { self.hash } } impl From for MemoHash { fn from(value: T) -> Self { Self::new(value) } } impl Hash for MemoHash { fn hash(&self, state: &mut H) { self.hash.hash(state) } } impl Deref for MemoHash { type Target = T; fn deref(&self) -> &Self::Target { &self.value } } pub struct MemoHashGuard<'a, T: Hash> { inner: &'a mut MemoHash, } impl Drop for MemoHashGuard<'_, T> { fn drop(&mut self) { let hash = MemoHash::::calc_hash(&self.inner.value); self.inner.hash = hash; } } impl Deref for MemoHashGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { &self.inner.value } } impl std::ops::DerefMut for MemoHashGuard<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner.value } }