diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index abc6626da..1771c1972 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -90,6 +90,7 @@ use crate::innerlude::*; use fxhash::{FxHashMap, FxHashSet}; +use smallvec::{smallvec, SmallVec}; use DomEdit::*; /// Our DiffMachine is an iterative tree differ. @@ -124,6 +125,117 @@ impl<'bump> DiffState<'bump> { } } +/// The stack instructions we use to diff and create new nodes. +#[derive(Debug)] +pub(crate) enum DiffInstruction<'a> { + Diff { + old: &'a VNode<'a>, + new: &'a VNode<'a>, + }, + + Create { + node: &'a VNode<'a>, + }, + + /// pushes the node elements onto the stack for use in mount + PrepareMove { + node: &'a VNode<'a>, + }, + + Mount { + and: MountType<'a>, + }, + + PopScope, +} + +#[derive(Debug, Clone, Copy)] +pub enum MountType<'a> { + Absorb, + Append, + Replace { old: &'a VNode<'a> }, + InsertAfter { other_node: &'a VNode<'a> }, + InsertBefore { other_node: &'a VNode<'a> }, +} + +pub(crate) struct DiffStack<'bump> { + pub(crate) instructions: Vec>, + nodes_created_stack: SmallVec<[usize; 10]>, + pub scope_stack: SmallVec<[ScopeId; 5]>, +} + +impl<'bump> DiffStack<'bump> { + pub fn new() -> Self { + Self { + instructions: Vec::with_capacity(1000), + nodes_created_stack: smallvec![], + scope_stack: smallvec![], + } + } + + pub fn pop(&mut self) -> Option> { + self.instructions.pop() + } + + pub fn pop_off_scope(&mut self) { + self.scope_stack.pop(); + } + + pub fn push(&mut self, instruction: DiffInstruction<'bump>) { + self.instructions.push(instruction) + } + + pub fn create_children(&mut self, children: &'bump [VNode<'bump>], and: MountType<'bump>) { + self.nodes_created_stack.push(0); + self.instructions.push(DiffInstruction::Mount { and }); + + for child in children.iter().rev() { + self.instructions + .push(DiffInstruction::Create { node: child }); + } + } + + pub fn push_subtree(&mut self) { + self.nodes_created_stack.push(0); + self.instructions.push(DiffInstruction::Mount { + and: MountType::Append, + }); + } + + pub fn push_nodes_created(&mut self, count: usize) { + self.nodes_created_stack.push(count); + } + + pub fn create_node(&mut self, node: &'bump VNode<'bump>, and: MountType<'bump>) { + self.nodes_created_stack.push(0); + self.instructions.push(DiffInstruction::Mount { and }); + self.instructions.push(DiffInstruction::Create { node }); + } + + pub fn add_child_count(&mut self, count: usize) { + *self.nodes_created_stack.last_mut().unwrap() += count; + } + + pub fn pop_nodes_created(&mut self) -> usize { + self.nodes_created_stack.pop().unwrap() + } + + pub fn current_scope(&self) -> Option { + self.scope_stack.last().copied() + } + + pub fn create_component(&mut self, idx: ScopeId, node: &'bump VNode<'bump>) { + // Push the new scope onto the stack + self.scope_stack.push(idx); + + self.instructions.push(DiffInstruction::PopScope); + + // Run the creation algorithm with this scope on the stack + // ?? I think we treat components as fragments?? + self.instructions.push(DiffInstruction::Create { node }); + } +} + impl<'bump> DiffState<'bump> { pub fn diff_scope(&mut self, id: &ScopeId) { let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id)); diff --git a/packages/core/src/diff_stack.rs b/packages/core/src/diff_stack.rs deleted file mode 100644 index 8eaa42a5c..000000000 --- a/packages/core/src/diff_stack.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::innerlude::*; -use smallvec::{smallvec, SmallVec}; - -/// The stack instructions we use to diff and create new nodes. -#[derive(Debug)] -pub(crate) enum DiffInstruction<'a> { - Diff { - old: &'a VNode<'a>, - new: &'a VNode<'a>, - }, - - Create { - node: &'a VNode<'a>, - }, - - /// pushes the node elements onto the stack for use in mount - PrepareMove { - node: &'a VNode<'a>, - }, - - Mount { - and: MountType<'a>, - }, - - PopScope, -} - -#[derive(Debug, Clone, Copy)] -pub enum MountType<'a> { - Absorb, - Append, - Replace { old: &'a VNode<'a> }, - InsertAfter { other_node: &'a VNode<'a> }, - InsertBefore { other_node: &'a VNode<'a> }, -} - -pub(crate) struct DiffStack<'bump> { - pub(crate) instructions: Vec>, - nodes_created_stack: SmallVec<[usize; 10]>, - pub scope_stack: SmallVec<[ScopeId; 5]>, -} - -impl<'bump> DiffStack<'bump> { - pub fn new() -> Self { - Self { - instructions: Vec::with_capacity(1000), - nodes_created_stack: smallvec![], - scope_stack: smallvec![], - } - } - - pub fn pop(&mut self) -> Option> { - self.instructions.pop() - } - - pub fn pop_off_scope(&mut self) { - self.scope_stack.pop(); - } - - pub fn push(&mut self, instruction: DiffInstruction<'bump>) { - self.instructions.push(instruction) - } - - pub fn create_children(&mut self, children: &'bump [VNode<'bump>], and: MountType<'bump>) { - self.nodes_created_stack.push(0); - self.instructions.push(DiffInstruction::Mount { and }); - - for child in children.iter().rev() { - self.instructions - .push(DiffInstruction::Create { node: child }); - } - } - - pub fn push_subtree(&mut self) { - self.nodes_created_stack.push(0); - self.instructions.push(DiffInstruction::Mount { - and: MountType::Append, - }); - } - - pub fn push_nodes_created(&mut self, count: usize) { - self.nodes_created_stack.push(count); - } - - pub fn create_node(&mut self, node: &'bump VNode<'bump>, and: MountType<'bump>) { - self.nodes_created_stack.push(0); - self.instructions.push(DiffInstruction::Mount { and }); - self.instructions.push(DiffInstruction::Create { node }); - } - - pub fn add_child_count(&mut self, count: usize) { - *self.nodes_created_stack.last_mut().unwrap() += count; - } - - pub fn pop_nodes_created(&mut self) -> usize { - self.nodes_created_stack.pop().unwrap() - } - - pub fn current_scope(&self) -> Option { - self.scope_stack.last().copied() - } - - pub fn create_component(&mut self, idx: ScopeId, node: &'bump VNode<'bump>) { - // Push the new scope onto the stack - self.scope_stack.push(idx); - - self.instructions.push(DiffInstruction::PopScope); - - // Run the creation algorithm with this scope on the stack - // ?? I think we treat components as fragments?? - self.instructions.push(DiffInstruction::Create { node }); - } -} diff --git a/packages/core/src/hooklist.rs b/packages/core/src/hooklist.rs deleted file mode 100644 index d3b79ca48..000000000 --- a/packages/core/src/hooklist.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{ - any::Any, - cell::{Cell, RefCell}, -}; - -use bumpalo::Bump; - -/// An abstraction over internally stored data using a hook-based memory layout. -/// -/// Hooks are allocated using Boxes and then our stored references are given out. -/// -/// It's unsafe to "reset" the hooklist, but it is safe to add hooks into it. -/// -/// Todo: this could use its very own bump arena, but that might be a tad overkill -#[derive(Default)] -pub(crate) struct HookList { - arena: Bump, - vals: RefCell>, - idx: Cell, -} - -impl HookList { - pub(crate) fn next(&self) -> Option<&mut T> { - self.vals.borrow().get(self.idx.get()).and_then(|inn| { - self.idx.set(self.idx.get() + 1); - let raw_box = unsafe { &mut **inn }; - raw_box.downcast_mut::() - }) - } - - /// This resets the internal iterator count - /// It's okay that we've given out each hook, but now we have the opportunity to give it out again - /// Therefore, resetting is considered unsafe - /// - /// This should only be ran by Dioxus itself before "running scope". - /// Dioxus knows how to descend through the tree to prevent mutable aliasing. - pub(crate) unsafe fn reset(&self) { - self.idx.set(0); - } - - pub(crate) fn push_hook(&self, new: T) { - let val = self.arena.alloc(new); - self.vals.borrow_mut().push(val) - } - - pub(crate) fn len(&self) -> usize { - self.vals.borrow().len() - } - - pub(crate) fn cur_idx(&self) -> usize { - self.idx.get() - } - - pub(crate) fn at_end(&self) -> bool { - self.cur_idx() >= self.len() - } - - pub fn clear_hooks(&mut self) { - self.vals.borrow_mut().drain(..).for_each(|state| { - let as_mut = unsafe { &mut *state }; - let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) }; - drop(boxed); - }); - } - - /// Get the ammount of memory a hooklist uses - /// Used in heuristics - pub fn get_hook_arena_size(&self) -> usize { - self.arena.allocated_bytes() - } -} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 9988d9415..6d540bdfd 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -14,8 +14,6 @@ Some utilities */ pub(crate) mod component; pub(crate) mod diff; -pub(crate) mod diff_stack; -pub(crate) mod hooklist; pub(crate) mod lazynodes; pub(crate) mod mutations; pub(crate) mod nodes; @@ -25,9 +23,7 @@ pub(crate) mod virtual_dom; pub(crate) mod innerlude { pub use crate::component::*; - pub(crate) use crate::diff::*; - pub use crate::diff_stack::*; - pub(crate) use crate::hooklist::*; + pub use crate::diff::*; pub use crate::lazynodes::*; pub use crate::mutations::*; pub use crate::nodes::*; diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 5fabc4724..30ae15e7b 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -528,6 +528,71 @@ impl BumpFrame { } } +/// An abstraction over internally stored data using a hook-based memory layout. +/// +/// Hooks are allocated using Boxes and then our stored references are given out. +/// +/// It's unsafe to "reset" the hooklist, but it is safe to add hooks into it. +/// +/// Todo: this could use its very own bump arena, but that might be a tad overkill +#[derive(Default)] +pub(crate) struct HookList { + arena: Bump, + vals: RefCell>, + idx: Cell, +} + +impl HookList { + pub(crate) fn next(&self) -> Option<&mut T> { + self.vals.borrow().get(self.idx.get()).and_then(|inn| { + self.idx.set(self.idx.get() + 1); + let raw_box = unsafe { &mut **inn }; + raw_box.downcast_mut::() + }) + } + + /// This resets the internal iterator count + /// It's okay that we've given out each hook, but now we have the opportunity to give it out again + /// Therefore, resetting is considered unsafe + /// + /// This should only be ran by Dioxus itself before "running scope". + /// Dioxus knows how to descend through the tree to prevent mutable aliasing. + pub(crate) unsafe fn reset(&self) { + self.idx.set(0); + } + + pub(crate) fn push_hook(&self, new: T) { + let val = self.arena.alloc(new); + self.vals.borrow_mut().push(val) + } + + pub(crate) fn len(&self) -> usize { + self.vals.borrow().len() + } + + pub(crate) fn cur_idx(&self) -> usize { + self.idx.get() + } + + pub(crate) fn at_end(&self) -> bool { + self.cur_idx() >= self.len() + } + + pub fn clear_hooks(&mut self) { + self.vals.borrow_mut().drain(..).for_each(|state| { + let as_mut = unsafe { &mut *state }; + let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) }; + drop(boxed); + }); + } + + /// Get the ammount of memory a hooklist uses + /// Used in heuristics + pub fn get_hook_arena_size(&self) -> usize { + self.arena.allocated_bytes() + } +} + #[test] fn sizeof() { dbg!(std::mem::size_of::());