use anymap::any; use anymap::AnyMap; use rustc_hash::FxHashMap; use rustc_hash::FxHashSet; use std::any::{Any, TypeId}; use std::collections::BTreeMap; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use crate::node::Node; use crate::node_ref::NodeView; use crate::state::State; use crate::tree::{Tree, TreeView}; use crate::{FxDashMap, FxDashSet, SendAnyMap}; use crate::{NodeId, NodeMask}; pub trait Pass: Any { /// This is a tuple of (T: Any, ..) type ParentDependencies: Dependancy; /// This is a tuple of (T: Any, ..) type ChildDependencies: Dependancy; /// This is a tuple of (T: Any, ..) type NodeDependencies: Dependancy; /// This is a tuple of (T: Any, ..) type Ctx: Dependancy; const MASK: NodeMask; fn pass<'a>( &mut self, node_view: NodeView, node: ::ElementBorrowed<'a>, parent: Option<::ElementBorrowed<'a>>, children: Option< impl Iterator::ElementBorrowed<'a>>, >, context: ::ElementBorrowed<'a>, ) -> bool; fn is_valid(&self) -> bool { // no type can be a child and parent dependency for type_id in Self::parent_type_ids() { for type_id2 in Self::child_type_ids() { if type_id == type_id2 { return false; } } } // this type should not be a node dependency for type_id in Self::node_type_ids() { if type_id == TypeId::of::() { return false; } } // no states have the same type id if Self::all_dependanices() .into_iter() .collect::>() .len() != Self::all_dependanices().len() { return false; } true } fn to_type_erased() -> TypeErasedPass where Self: Sized, { TypeErasedPass { this_type_id: TypeId::of::(), parent_type_ids: Self::parent_type_ids().into_iter().collect(), child_type_ids: Self::child_type_ids().into_iter().collect(), node_type_ids: Self::node_type_ids().into_iter().collect(), combined_dependancy_type_ids: Self::all_dependanices().into_iter().collect(), dependants: FxHashSet::default(), mask: Self::MASK, pass_direction: Self::pass_direction(), pass: Box::new( |node_id: NodeId, any_map: &mut Tree>, context: &SendAnyMap| { let (current_node, parent, children) = any_map .node_parent_children_mut(node_id) .expect("tried to run pass on node that does not exist"); let current_node_raw = current_node as *mut Node; let node = Self::NodeDependencies::borrow_elements_from(¤t_node.state) .expect("tried to get a pass that does not exist"); let parent = parent.map(|parent| { Self::ParentDependencies::borrow_elements_from(&parent.state) .expect("tried to get a pass that does not exist") }); let children = children.map(|children| { children.map(|child| { Self::ChildDependencies::borrow_elements_from(&child.state) .expect("tried to get a pass that does not exist") }) }); // safety: we have varified the pass is valid in the is_valid function let myself: &mut Self = unsafe { (*current_node_raw) .state .get_mut() .expect("tried to get a pass that does not exist") }; let context = Self::Ctx::borrow_elements_from(context) .expect("tried to get a pass that does not exist"); myself.pass( NodeView::new(¤t_node.node_data, Self::MASK), node, parent, children, context, ) }, ) as Box>, &SendAnyMap) -> bool + Send + Sync>, } } fn parent_type_ids() -> Vec { Self::ParentDependencies::type_ids() } fn child_type_ids() -> Vec { Self::ChildDependencies::type_ids() } fn node_type_ids() -> Vec { Self::NodeDependencies::type_ids() } fn all_dependanices() -> Vec { let mut dependencies = Self::parent_type_ids(); dependencies.extend(Self::child_type_ids()); dependencies.extend(Self::node_type_ids()); dependencies } fn pass_direction() -> PassDirection { if Self::child_type_ids() .into_iter() .any(|type_id| type_id == TypeId::of::()) { PassDirection::ChildToParent } else if Self::parent_type_ids() .into_iter() .any(|type_id| type_id == TypeId::of::()) { PassDirection::ParentToChild } else { PassDirection::AnyOrder } } } pub struct TypeErasedPass { pub(crate) this_type_id: TypeId, parent_type_ids: FxHashSet, child_type_ids: FxHashSet, node_type_ids: FxHashSet, combined_dependancy_type_ids: FxHashSet, pub(crate) dependants: FxHashSet, pub(crate) mask: NodeMask, pass: PassCallback, pass_direction: PassDirection, } impl TypeErasedPass { fn resolve( &self, tree: &mut Tree>, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, nodes_updated: &FxDashSet, ctx: &SendAnyMap, ) { match self.pass_direction { PassDirection::ParentToChild => { while let Some(id) = dirty.pop_front() { if (self.pass)(id, tree, ctx) { nodes_updated.insert(id); for id in tree.children_ids(id).unwrap() { for dependant in &self.dependants { dirty_states.insert(*dependant, *id); } let height = tree.height(*id).unwrap(); dirty.insert(height, *id); } } } } PassDirection::ChildToParent => { while let Some(id) = dirty.pop_back() { if (self.pass)(id, tree, ctx) { nodes_updated.insert(id); if let Some(id) = tree.parent_id(id) { for dependant in &self.dependants { dirty_states.insert(*dependant, id); } let height = tree.height(id).unwrap(); dirty.insert(height, id); } } } } PassDirection::AnyOrder => { while let Some(id) = dirty.pop_back() { if (self.pass)(id, tree, ctx) { nodes_updated.insert(id); for dependant in &self.dependants { dirty_states.insert(*dependant, id); } } } } } } } pub enum PassDirection { ParentToChild, ChildToParent, AnyOrder, } type PassCallback = Box>, &SendAnyMap) -> bool + Send + Sync>; pub trait AnyMapLike { fn get(&self) -> Option<&T>; fn get_mut(&mut self) -> Option<&mut T>; } impl AnyMapLike for AnyMap { fn get(&self) -> Option<&T> { self.get() } fn get_mut(&mut self) -> Option<&mut T> { self.get_mut() } } impl AnyMapLike for SendAnyMap { fn get(&self) -> Option<&T> { todo!() } fn get_mut(&mut self) -> Option<&mut T> { todo!() } } pub trait Dependancy { type ElementBorrowed<'a> where Self: 'a; fn borrow_elements(&self) -> Self::ElementBorrowed<'_>; fn borrow_elements_from(map: &T) -> Option>; fn type_ids() -> Vec; } macro_rules! impl_dependancy { ($($t:ident),*) => { impl< $($t: Any),* > Dependancy for ($($t,)*) { type ElementBorrowed<'a> = ($(&'a $t,)*) where Self: 'a; #[allow(clippy::unused_unit, non_snake_case)] fn borrow_elements<'a>(&'a self) -> Self::ElementBorrowed<'a> { let ($($t,)*) = self; ($($t,)*) } #[allow(unused_variables, clippy::unused_unit, non_snake_case)] fn borrow_elements_from(map: &T) -> Option> { Some(($(map.get::<$t>()?,)*)) } fn type_ids() -> Vec { vec![$(TypeId::of::<$t>()),*] } } }; } impl_dependancy!(); impl_dependancy!(A); impl_dependancy!(A, B); impl_dependancy!(A, B, C); impl_dependancy!(A, B, C, D); impl_dependancy!(A, B, C, D, E); impl_dependancy!(A, B, C, D, E, F); impl_dependancy!(A, B, C, D, E, F, G); impl_dependancy!(A, B, C, D, E, F, G, H); impl_dependancy!(A, B, C, D, E, F, G, H, I); impl_dependancy!(A, B, C, D, E, F, G, H, I, J); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct DirtyNodes { map: BTreeMap>, } impl DirtyNodes { pub fn insert(&mut self, depth: u16, node_id: NodeId) { self.map .entry(depth) .or_insert_with(FxHashSet::default) .insert(node_id); } fn pop_front(&mut self) -> Option { let (&depth, values) = self.map.iter_mut().next()?; let key = *values.iter().next()?; let node_id = values.take(&key)?; if values.is_empty() { self.map.remove(&depth); } Some(node_id) } fn pop_back(&mut self) -> Option { let (&depth, values) = self.map.iter_mut().rev().next()?; let key = *values.iter().next()?; let node_id = values.take(&key)?; if values.is_empty() { self.map.remove(&depth); } Some(node_id) } } #[test] fn dirty_nodes() { let mut dirty_nodes = DirtyNodes::default(); dirty_nodes.insert(1, NodeId(1)); dirty_nodes.insert(0, NodeId(0)); dirty_nodes.insert(2, NodeId(3)); dirty_nodes.insert(1, NodeId(2)); assert_eq!(dirty_nodes.pop_front(), Some(NodeId(0))); assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2)))); assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2)))); assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3))); } #[derive(Default)] pub struct DirtyNodeStates { dirty: FxDashMap>, } impl DirtyNodeStates { pub fn new(starting_nodes: FxHashMap>) -> Self { let this = Self::default(); for (node, nodes) in starting_nodes { for pass_id in nodes { this.insert(pass_id, node); } } this } pub fn insert(&self, pass_id: TypeId, node_id: NodeId) { if let Some(mut dirty) = self.dirty.get_mut(&node_id) { dirty.insert(pass_id); } else { let mut v = FxHashSet::default(); v.insert(pass_id); self.dirty.insert(node_id, v); } } fn all_dirty(&self, pass_id: TypeId, dirty_nodes: &mut DirtyNodes, tree: &impl TreeView) { for entry in self.dirty.iter() { let node_id = entry.key(); let dirty = entry.value(); if dirty.contains(&pass_id) { dirty_nodes.insert(tree.height(*node_id).unwrap(), *node_id); } } } } pub fn resolve_passes( tree: &mut Tree>, dirty_nodes: DirtyNodeStates, mut passes: &[TypeErasedPass], ctx: SendAnyMap, ) -> FxDashSet { let dirty_states = Arc::new(dirty_nodes); let mut resolved_passes: FxHashSet = FxHashSet::default(); let mut resolving = Vec::new(); let nodes_updated = Arc::new(FxDashSet::default()); let ctx = Arc::new(ctx); let mut pass_indexes_remaining: Vec<_> = (0..passes.len()).collect::>(); while !pass_indexes_remaining.is_empty() { let mut currently_in_use = FxHashSet::::default(); std::thread::scope(|s| { let mut i = 0; while i < pass_indexes_remaining.len() { let passes_idx = pass_indexes_remaining[i]; let pass = &passes[passes_idx]; let pass_id = pass.this_type_id; // check if the pass is ready to be run if pass .combined_dependancy_type_ids .iter() .all(|d| resolved_passes.contains(d) || *d == pass_id) { pass_indexes_remaining.remove(i); resolving.push(pass_id); currently_in_use.insert(pass.this_type_id); // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass let tree_unbounded_mut = unsafe { &mut *(tree as *mut _) }; let dirty_states = dirty_states.clone(); let nodes_updated = nodes_updated.clone(); let ctx = ctx.clone(); s.spawn(move || { let mut dirty = DirtyNodes::default(); dirty_states.all_dirty(pass_id, &mut dirty, tree_unbounded_mut); pass.resolve( tree_unbounded_mut, dirty, &dirty_states, &nodes_updated, &ctx, ); }); } else { i += 1; } } // all passes are resolved at the end of the scope }); resolved_passes.extend(resolving.iter().copied()); resolving.clear() } std::sync::Arc::try_unwrap(nodes_updated).unwrap() } #[test] fn node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl NodePass for AddPass { fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } } let add_pass = AnyPass::Node(&AddPass); let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); } #[test] fn dependant_node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[PassId(1)] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl NodePass for AddPass { fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } } struct SubtractPass; impl Pass for SubtractPass { fn pass_id(&self) -> PassId { PassId(1) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[PassId(0)] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl NodePass for SubtractPass { fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node -= 1; true } } let add_pass = AnyPass::Node(&AddPass); let subtract_pass = AnyPass::Node(&SubtractPass); let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(*tree.get(tree.root()).unwrap(), 0); } #[test] fn independant_node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new((0, 0)); struct AddPass1; impl Pass for AddPass1 { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl NodePass<(i32, i32)> for AddPass1 { fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.0 += 1; true } } struct AddPass2; impl Pass for AddPass2 { fn pass_id(&self) -> PassId { PassId(1) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(1) } } impl NodePass<(i32, i32)> for AddPass2 { fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.1 += 1; true } } let add_pass1 = AnyPass::Node(&AddPass1); let add_pass2 = AnyPass::Node(&AddPass2); let passes = vec![&add_pass1, &add_pass2]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1)); } #[test] fn down_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(1); let parent = tree.root(); let child1 = tree.create_node(1); tree.add_child(parent, child1); let grandchild1 = tree.create_node(1); tree.add_child(child1, grandchild1); let child2 = tree.create_node(1); tree.add_child(parent, child2); let grandchild2 = tree.create_node(1); tree.add_child(child2, grandchild2); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl DownwardPass for AddPass { fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { *node += *parent; } PassReturn { progress: true, mark_dirty: true, } } } let add_pass = AnyPass::Downward(&AddPass); let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); assert_eq!(tree.get(child1).unwrap(), &2); assert_eq!(tree.get(grandchild1).unwrap(), &3); assert_eq!(tree.get(child2).unwrap(), &2); assert_eq!(tree.get(grandchild2).unwrap(), &3); } #[test] fn dependant_down_pass() { use crate::tree::{Tree, TreeLike}; // 0 let mut tree = Tree::new(1); let parent = tree.root(); // 1 let child1 = tree.create_node(1); tree.add_child(parent, child1); // 2 let grandchild1 = tree.create_node(1); tree.add_child(child1, grandchild1); // 3 let child2 = tree.create_node(1); tree.add_child(parent, child2); // 4 let grandchild2 = tree.create_node(1); tree.add_child(child2, grandchild2); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[PassId(1)] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl DownwardPass for AddPass { fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { *node += *parent; } else { } PassReturn { progress: true, mark_dirty: true, } } } struct SubtractPass; impl Pass for SubtractPass { fn pass_id(&self) -> PassId { PassId(1) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[PassId(0)] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl DownwardPass for SubtractPass { fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { *node -= *parent; } else { } PassReturn { progress: true, mark_dirty: true, } } } let add_pass = AnyPass::Downward(&AddPass); let subtract_pass = AnyPass::Downward(&SubtractPass); let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 1=\ // 1=\ // 1 // 1=\ // 1 // Tree after subtract: // 1=\ // 0=\ // 1 // 0=\ // 1 // Tree after add: // 1=\ // 1=\ // 2 // 1=\ // 2 assert_eq!(tree.get(tree.root()).unwrap(), &1); assert_eq!(tree.get(child1).unwrap(), &1); assert_eq!(tree.get(grandchild1).unwrap(), &2); assert_eq!(tree.get(child2).unwrap(), &1); assert_eq!(tree.get(grandchild2).unwrap(), &2); } #[test] fn up_pass() { use crate::tree::{Tree, TreeLike}; // Tree before: // 0=\ // 0=\ // 1 // 0=\ // 1 // Tree after: // 2=\ // 1=\ // 1 // 1=\ // 1 let mut tree = Tree::new(0); let parent = tree.root(); let child1 = tree.create_node(0); tree.add_child(parent, child1); let grandchild1 = tree.create_node(1); tree.add_child(child1, grandchild1); let child2 = tree.create_node(0); tree.add_child(parent, child2); let grandchild2 = tree.create_node(1); tree.add_child(child2, grandchild2); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl UpwardPass for AddPass { fn pass<'a>( &self, node: &mut i32, children: &mut dyn Iterator, _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { progress: true, mark_dirty: true, } } } let add_pass = AnyPass::Upward(&AddPass); let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), grandchild1); dirty_nodes.insert(PassId(0), grandchild2); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &2); assert_eq!(tree.get(child1).unwrap(), &1); assert_eq!(tree.get(grandchild1).unwrap(), &1); assert_eq!(tree.get(child2).unwrap(), &1); assert_eq!(tree.get(grandchild2).unwrap(), &1); } #[test] fn dependant_up_pass() { use crate::tree::{Tree, TreeLike}; // 0 let mut tree = Tree::new(0); let parent = tree.root(); // 1 let child1 = tree.create_node(0); tree.add_child(parent, child1); // 2 let grandchild1 = tree.create_node(1); tree.add_child(child1, grandchild1); // 3 let child2 = tree.create_node(0); tree.add_child(parent, child2); // 4 let grandchild2 = tree.create_node(1); tree.add_child(child2, grandchild2); struct AddPass; impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } fn dependancies(&self) -> &'static [PassId] { &[PassId(1)] } fn dependants(&self) -> &'static [PassId] { &[] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl UpwardPass for AddPass { fn pass<'a>( &self, node: &mut i32, children: &mut dyn Iterator, _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { progress: true, mark_dirty: true, } } } struct SubtractPass; impl Pass for SubtractPass { fn pass_id(&self) -> PassId { PassId(1) } fn dependancies(&self) -> &'static [PassId] { &[] } fn dependants(&self) -> &'static [PassId] { &[PassId(0)] } fn mask(&self) -> MemberMask { MemberMask(0) } } impl UpwardPass for SubtractPass { fn pass<'a>( &self, node: &mut i32, children: &mut dyn Iterator, _: &SendAnyMap, ) -> PassReturn { *node -= children.map(|i| *i).sum::(); PassReturn { progress: true, mark_dirty: true, } } } let add_pass = AnyPass::Upward(&AddPass); let subtract_pass = AnyPass::Upward(&SubtractPass); let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), grandchild1); dirty_nodes.insert(PassId(1), grandchild2); resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 0=\ // 0=\ // 1 // 0=\ // 1 // Tree after subtract: // 2=\ // -1=\ // 1 // -1=\ // 1 // Tree after add: // 2=\ // 0=\ // 1 // 0=\ // 1 assert_eq!(tree.get(tree.root()).unwrap(), &2); assert_eq!(tree.get(child1).unwrap(), &0); assert_eq!(tree.get(grandchild1).unwrap(), &1); assert_eq!(tree.get(child2).unwrap(), &0); assert_eq!(tree.get(grandchild2).unwrap(), &1); }