From 562217015d165f033d68d3161cdfb72190597f15 Mon Sep 17 00:00:00 2001 From: TrueDoctor Date: Sat, 15 Oct 2022 03:02:58 +0200 Subject: [PATCH] Rework DynAnyNode design to work with the borrow stack (#796) --- libraries/dyn-any/src/lib.rs | 45 ++++++++++++++- node-graph/borrow_stack/src/lib.rs | 19 ++++--- node-graph/gcore/src/lib.rs | 28 +++++++++ node-graph/gcore/src/ops.rs | 2 +- node-graph/gcore/src/structural.rs | 39 +++---------- node-graph/graph-craft/src/lib.rs | 52 ++++++++++------- node-graph/gstd/src/any.rs | 91 ++++++++++++++++++++++++------ node-graph/gstd/src/raster.rs | 2 +- 8 files changed, 198 insertions(+), 80 deletions(-) diff --git a/libraries/dyn-any/src/lib.rs b/libraries/dyn-any/src/lib.rs index 122dd7c4..2758b21d 100644 --- a/libraries/dyn-any/src/lib.rs +++ b/libraries/dyn-any/src/lib.rs @@ -25,12 +25,20 @@ pub fn downcast_ref<'a, V: StaticType>(i: &'a dyn DynAny<'a>) -> Option<&'a V> { None } } + pub fn downcast<'a, V: StaticType>(i: Box + 'a>) -> Option> { - if i.type_id() == std::any::TypeId::of::<::Static>() { + let type_id = DynAny::type_id(i.as_ref()); + if type_id == std::any::TypeId::of::<::Static>() { // SAFETY: caller guarantees that T is the correct type let ptr = Box::into_raw(i) as *mut dyn DynAny<'a> as *mut V; Some(unsafe { Box::from_raw(ptr) }) } else { + // TODO: add log error + if type_id == std::any::TypeId::of::<&dyn DynAny<'static>>() { + panic!("downcast error: type_id == std::any::TypeId::of::>()"); + } + println!("expected: {:?}", std::any::TypeId::of::<::Static>()); + println!("actual one: {:?}", type_id); None } } @@ -94,13 +102,29 @@ impl<'a> StaticType for &'a str { impl StaticType for () { type Static = (); } -impl<'a, T: 'a + StaticType> StaticType for &'a T { +impl<'a, T: 'a + StaticType + ?Sized> StaticType for &'a T { type Static = &'static ::Static; } impl StaticType for [T; N] { type Static = [::Static; N]; } +impl<'a> StaticType for dyn DynAny<'a> + '_ { + type Static = dyn DynAny<'static>; +} +pub trait IntoDynAny<'n>: Sized + StaticType + 'n { + fn into_dyn(self) -> Box + 'n> { + Box::new(self) + } +} +impl<'n, T: StaticType + 'n> IntoDynAny<'n> for T {} + +impl From<()> for Box> { + fn from(_: ()) -> Box> { + Box::new(()) + } +} + use core::{ cell::{Cell, RefCell, UnsafeCell}, iter::Empty, @@ -117,12 +141,21 @@ use std::{ impl_type!(Option,Result,Cell,UnsafeCell,RefCell,MaybeUninit, Vec, String, BTreeMap,BTreeSet, LinkedList, VecDeque, - BinaryHeap, Box, ManuallyDrop, PhantomData, PhantomPinned,Empty, + BinaryHeap, ManuallyDrop, PhantomData, PhantomPinned,Empty, Wrapping, Duration, Once, Mutex, RwLock, bool, f32, f64, char, u8, AtomicU8, u16,AtomicU16, u32,AtomicU32, u64,AtomicU64, usize,AtomicUsize, i8,AtomicI8, i16,AtomicI16, i32,AtomicI32, i64,AtomicI64, isize,AtomicIsize, i128, u128, AtomicBool, AtomicPtr ); + +impl crate::StaticType for Box { + type Static = Box<::Static>; +} +fn test() { + let foo = (Box::new(&1 as &dyn DynAny<'static>), Box::new(&2 as &dyn DynAny<'static>)); + //let bar = &foo as &dyn DynAny<'static>; +} + macro_rules! impl_tuple { (@rec $t:ident) => { }; (@rec $_:ident $($t:ident)+) => { @@ -148,3 +181,9 @@ fn simple_downcast() { let x = Box::new(3_u32) as Box; assert_eq!(*downcast::(x).unwrap(), 3_u32); } +#[test] +#[should_panic] +fn simple_downcast_panic() { + let x = Box::new(3_i32) as Box; + assert_eq!(*downcast::(x).expect("attempted to perform invalid downcast"), 3_u32); +} diff --git a/node-graph/borrow_stack/src/lib.rs b/node-graph/borrow_stack/src/lib.rs index 90fcaf4f..6f4328c6 100644 --- a/node-graph/borrow_stack/src/lib.rs +++ b/node-graph/borrow_stack/src/lib.rs @@ -8,11 +8,11 @@ use std::{ pub trait BorrowStack<'n> { type Item; /// # Safety - unsafe fn push(&'n self, value: Self::Item); + unsafe fn push(&self, value: Self::Item); /// # Safety - unsafe fn pop(&'n self); + unsafe fn pop(&self); /// # Safety - unsafe fn get(&'n self) -> &'n [Self::Item]; + unsafe fn get(&self) -> &'n [Self::Item]; } #[derive(Debug)] @@ -23,7 +23,7 @@ pub struct FixedSizeStack<'n, T> { _phantom: PhantomData<&'n ()>, } -impl<'n, T: Unpin> FixedSizeStack<'n, T> { +impl<'n, T: Unpin + 'n> FixedSizeStack<'n, T> { pub fn new(capacity: usize) -> Self { let layout = std::alloc::Layout::array::>(capacity).unwrap(); let array = unsafe { std::alloc::alloc(layout) }; @@ -44,12 +44,15 @@ impl<'n, T: Unpin> FixedSizeStack<'n, T> { pub fn is_empty(&self) -> bool { self.len.load(Ordering::SeqCst) == 0 } + pub fn push_fn(&self, f: impl FnOnce(&'n [T]) -> T) { + unsafe { self.push(f(self.get())) } + } } impl<'n, T> BorrowStack<'n> for FixedSizeStack<'n, T> { type Item = T; - unsafe fn push(&'n self, value: Self::Item) { + unsafe fn push(&self, value: Self::Item) { let len = self.len.load(Ordering::SeqCst); assert!(len < self.capacity); let ptr = self.data[len].as_ptr(); @@ -57,13 +60,13 @@ impl<'n, T> BorrowStack<'n> for FixedSizeStack<'n, T> { self.len.fetch_add(1, Ordering::SeqCst); } - unsafe fn pop(&'n self) { + unsafe fn pop(&self) { let ptr = self.data[self.len.load(Ordering::SeqCst)].as_ptr(); - Box::from_raw(ptr as *mut T); + let _ = Box::from_raw(ptr as *mut T); self.len.fetch_sub(1, Ordering::SeqCst); } - unsafe fn get(&'n self) -> &'n [Self::Item] { + unsafe fn get(&self) -> &'n [Self::Item] { std::slice::from_raw_parts(self.data.as_ptr() as *const T, self.len.load(Ordering::SeqCst)) } } diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index 6f34c850..845564b6 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -48,6 +48,34 @@ where } } +pub trait AsBoxNode<'n, T> +where + &'n Self: Node, + Self: 'n, +{ + type Output; + fn eval_box(&'n self, input: T) -> ::Output; +} + +impl<'n, N: 'n, I> AsBoxNode<'n, I> for N +where + &'n N: Node, + N: Node, + Self: 'n, +{ + type Output = <&'n N as Node>::Output; + fn eval_box(&'n self, input: I) -> ::Output { + self.eval(input) + } +} + +impl<'n, T> Node for &'n (dyn AsBoxNode<'n, T, Output = T> + 'n) { + type Output = T; + fn eval(self, input: T) -> Self::Output { + self.eval_box(input) + } +} + #[cfg(feature = "async")] #[async_trait] pub trait AsyncNode { diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index c7e9fb5a..c994c9d5 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -240,7 +240,7 @@ mod test { pub fn add_node() { let a = ValueNode(42u32); let b = ValueNode(6u32); - let cons_a = ConsNode(a); + let cons_a = ConsNode(a, PhantomData); let sum = b.then(cons_a).then(AddNode); diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index 226853a7..57b08e02 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -115,49 +115,24 @@ pub trait ThenBox { #[cfg(feature = "async")] impl<'n, First: 'n, Inter, Input> ThenBox for alloc::boxed::Box where &'n alloc::boxed::Box: Node {} -pub struct ConsNode(pub Root); +pub struct ConsNode>(pub Root, pub PhantomData); -impl Node for ConsNode +impl> Node for ConsNode where - Root: Node<()>, + Root: Node, { - type Output = (Input, >::Output); + type Output = (Input, >::Output); fn eval(self, input: Input) -> Self::Output { - let arg = self.0.eval(()); + let arg = self.0.eval(().into()); (input, arg) } } -impl<'n, Root: Node<()> + Copy, Input> Node for &'n ConsNode { +impl<'n, Root: Node + Copy, T: From<()>, Input> Node for &'n ConsNode { type Output = (Input, Root::Output); fn eval(self, input: Input) -> Self::Output { - let arg = (&self.0).eval(()); + let arg = self.0.eval(().into()); (input, arg) } } - -pub struct ConsPassInputNode(pub Root); - -impl Node<(L, R)> for ConsPassInputNode -where - Root: Node, -{ - type Output = (L, >::Output); - - fn eval(self, input: (L, R)) -> Self::Output { - let arg = self.0.eval(input.1); - (input.0, arg) - } -} -impl<'n, Root, L, R> Node<(L, R)> for &'n ConsPassInputNode -where - &'n Root: Node, -{ - type Output = (L, <&'n Root as Node>::Output); - - fn eval(self, input: (L, R)) -> Self::Output { - let arg = (&self.0).eval(input.1); - (input.0, arg) - } -} diff --git a/node-graph/graph-craft/src/lib.rs b/node-graph/graph-craft/src/lib.rs index a83aa5d7..8ffc072f 100644 --- a/node-graph/graph-craft/src/lib.rs +++ b/node-graph/graph-craft/src/lib.rs @@ -1,39 +1,53 @@ #[cfg(test)] mod tests { - use graphene_core::structural::*; + use std::marker::PhantomData; + use graphene_core::value::ValueNode; + use graphene_core::{structural::*, RefNode}; use borrow_stack::BorrowStack; - use graphene_std::any::{Any, DynAnyNode, DynAnyNodeTrait}; + use dyn_any::{downcast, DynAny, IntoDynAny}; + use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode}; use graphene_std::ops::AddNode; #[test] fn borrow_stack() { let stack = borrow_stack::FixedSizeStack::new(256); unsafe { - let dynanynode: DynAnyNode<'_, _, ()> = DynAnyNode::new(&ValueNode(2_u32)); - let refn = Box::new(dynanynode) as Box; - stack.push(refn); - } - unsafe { - let dynanynode: DynAnyNode<'_, _, &u32> = DynAnyNode::new(&ConsNode(ValueNode(2_u32))); - let refn = Box::new(dynanynode) as Box; - stack.push(refn); - } - unsafe { - let dynanynode: DynAnyNode<'_, _, (&u32, u32)> = DynAnyNode::new(&AddNode); - let refn = Box::new(dynanynode) as Box; - stack.push(refn); + let dynanynode: DynAnyNode<_, (), _, _> = DynAnyNode::new(ValueNode(2_u32)); + stack.push(dynanynode.into_box()); } + stack.push_fn(|nodes| { + let downcast: DowncastNode<_, &u32> = DowncastNode::new(&nodes[0]); + let dynanynode: DynAnyNode>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData)); + dynanynode.into_box() + }); + stack.push_fn(|_| { + let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode); + dynanynode.into_box() + }); + stack.push_fn(|nodes| { + let compose_node = nodes[1].after(&nodes[2]); + TypeErasedNode(Box::new(compose_node)) + }); - let mut input = Box::new(()) as Any; + let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); + assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32)); + let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); + let add = unsafe { &stack.get()[2] }.eval_ref(result); + assert_eq!(*downcast::(add).unwrap(), 6_u32); + let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn()); + assert_eq!(*downcast::(add).unwrap(), 6_u32); + + /* for i in 0..3 { + println!("node_id: {}", i); let value = unsafe { &stack.get()[i] }; - input = value.eval_ref_dispatch(input); - } + input = value.eval_ref(input); + }*/ - assert_eq!(*dyn_any::downcast::(input).unwrap(), 4) + //assert_eq!(*dyn_any::downcast::(result).unwrap(), 4) //assert_eq!(4, *dyn_any::downcast::(DynamicAddNode.eval((Box::new(2_u32) as Dynamic, Box::new(2_u32) as Dynamic))).unwrap()); } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index 8195980c..a6916f1a 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -6,7 +6,7 @@ fn fmt_error() -> String { format!("DynAnyNode: input is not of correct type, expected {}", std::any::type_name::()) } -pub struct DynAnyNode<'n, N: RefNode, I>(pub N, pub PhantomData<&'n I>); +pub struct DynAnyNode(pub N, PhantomData<(I, O, ORef)>); /*impl<'n, I: StaticType, N: RefNode<'n, &'n I, Output = O> + 'n, O: 'n + StaticType> Node<&'n dyn DynAny<'n>> for DynAnyNode<'n, N, I> { type Output = Box + 'n>; fn eval(self, input: &'n dyn DynAny<'n>) -> Self::Output { @@ -29,22 +29,42 @@ impl<'n, I: StaticType, N: RefNode<'n, I, Output = O> + 'n, O: 'n + StaticType> Box::new(self.0.eval_ref(*input)) } }*/ -impl<'n, I: StaticType, N: RefNode + Copy + 'n, O: 'n + StaticType> Node> for &'n DynAnyNode<'n, N, I> { +impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node> for DynAnyNode +where + N: Node, +{ type Output = Any<'n>; fn eval(self, input: Any<'n>) -> Self::Output { let input: Box = dyn_any::downcast(input).unwrap_or_else(|| panic!("{}", fmt_error::())); - Box::new(self.0.eval_ref(*input)) + Box::new(self.0.eval(*input)) + } +} +impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node> for &'n DynAnyNode +where + &'n N: Node, +{ + type Output = Any<'n>; + fn eval(self, input: Any<'n>) -> Self::Output { + let input: Box = dyn_any::downcast(input).unwrap_or_else(|| panic!("{}", fmt_error::())); + Box::new((&self.0).eval_ref(*input)) + } +} +pub struct TypeErasedNode<'n>(pub Box, Output = Any<'n>>>); +impl<'n> Node> for &'n TypeErasedNode<'n> { + type Output = Any<'n>; + fn eval(self, input: Any<'n>) -> Self::Output { + self.0.eval_box(input) } } -impl<'n, I: StaticType, N: RefNode + 'n + Copy, O: 'n + StaticType> DynAnyNode<'n, N, I> +impl<'n, I: StaticType + 'n, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> DynAnyNode where - N::Output: StaticType, + &'n N: Node, { pub fn new(n: N) -> Self { DynAnyNode(n, PhantomData) } - pub fn into_erased(&'n self) -> impl RefNode, Output = Any<'n>> { + pub fn into_impl(&'n self) -> impl RefNode, Output = Any<'n>> { self } /*pub fn as_ref(&'n self) -> &'n AnyNode<'n> { @@ -53,22 +73,61 @@ where pub fn into_ref_box(self) -> Box + 'n)>, Output = Box<(dyn DynAny<'n> + 'n)>> + 'n> { Box::new(self) }*/ - pub fn into_ref(self: &'n &'n Self) -> &'n (dyn RefNode, Output = Any<'n>> + 'n) { + pub fn as_ref(self: &'n &'n Self) -> &'n (dyn RefNode, Output = Any<'n>> + 'n) { self } + pub fn into_box<'a: 'n>(self) -> TypeErasedNode<'n> + where + Self: 'a, + N: Node, + { + TypeErasedNode(Box::new(self)) + } } + +pub struct DowncastNode(pub N, PhantomData); +impl Clone for DowncastNode { + fn clone(&self) -> Self { + Self(self.0, self.1) + } +} +impl Copy for DowncastNode {} + +impl<'n, N, O: 'n + StaticType> Node> for DowncastNode +where + N: Node, Output = Any<'n>>, +{ + type Output = O; + fn eval(self, input: Any<'n>) -> Self::Output { + let output = self.0.eval(input); + *dyn_any::downcast(output).unwrap_or_else(|| panic!("DowncastNode: {}", fmt_error::())) + } +} +impl<'n, N, I: StaticType> DowncastNode +where + N: Node>, +{ + pub fn new(n: N) -> Self { + DowncastNode(n, PhantomData) + } +} + +/* /// If we store a `Box` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference) /// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value pub trait DynAnyNodeTrait<'n> { fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n>; } -impl<'n, I: StaticType, O: 'n + StaticType, Node: RefNode + Copy + 'n> DynAnyNodeTrait<'n> for DynAnyNode<'n, Node, I> { +impl<'n, I: StaticType, O: 'n + StaticType, Node: 'n> DynAnyNodeTrait<'n> for DynAnyNode +where + &'n Node: RefNode, +{ fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n> { self.eval_ref(input) } -} +}*/ -use graphene_core::ops::Dynamic; +use graphene_core::{ops::Dynamic, AsBoxNode}; pub struct BoxedComposition<'a, Second> { pub first: Box>>, pub second: Second, @@ -138,9 +197,9 @@ mod test { #[test] #[should_panic] pub fn dyn_input_invalid_eval_panic() { - static ADD: &DynAnyNode<&AddNode, (u32, u32)> = &DynAnyNode(&AddNode, PhantomData); + static ADD: &DynAnyNode = &DynAnyNode(AddNode, PhantomData); - let add = ADD.into_ref(); + let add = ADD.as_ref(); add.eval_ref(Box::new(&("32", 32u32))); } /*#[test] @@ -161,11 +220,11 @@ mod test { // If we put this until the push in a new scope then it failes to compile due to lifetime errors which I'm struggling to fix. - let value: &DynAnyNode<&ValueNode<(u32, u32)>, ()> = &DynAnyNode(&ValueNode((3u32, 4u32)), PhantomData); - let add: &DynAnyNode<&AddNode, &(u32, u32)> = &DynAnyNode(&AddNode, PhantomData); + let value: &DynAnyNode, (), &(u32, u32), _> = &DynAnyNode(ValueNode((3u32, 4u32)), PhantomData); + let add: &DynAnyNode = &DynAnyNode(AddNode, PhantomData); - let value_ref = (&value).into_ref(); - let add_ref = (&add).into_ref(); + let value_ref = value.as_ref(); + let add_ref = add.as_ref(); vec.push(value_ref); vec.push(add_ref); diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 2c684604..04922661 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -111,7 +111,7 @@ impl<'a> IntoIterator for &'a Image { pub fn file_node<'n, P: AsRef + 'n>() -> impl Node, Error>> { let fs = ValueNode(StdFs).clone(); - let fs = ConsNode(fs); + let fs = ConsNode(fs, PhantomData); let file: ComposeNode<_, _, P> = fs.then(FileNode(PhantomData)); file.then(FlatMapResultNode::new(BufferNode))