diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index 9d723d89c..b89afb4cd 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -126,11 +126,9 @@ impl VirtualDom { } }); - for root in node.root_ids { - if let Some(id) = root.get() { - if id.0 != 0 { - self.try_reclaim(id); - } + for id in &node.root_ids { + if id.0 != 0 { + self.try_reclaim(id); } } } diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index e4d3e08c3..65dccc482 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -25,6 +25,10 @@ impl<'b> VirtualDom { /// Create this template and write its mutations pub(crate) fn create(&mut self, node: &'b VNode<'b>) -> usize { + // Intialize the root nodes slice + node.root_ids + .intialize(vec![ElementId(0); node.template.get().roots.len()].into_boxed_slice()); + // The best renderers will have templates prehydrated and registered // Just in case, let's create the template using instructions anyways if !self.templates.contains_key(&node.template.get().name) { @@ -213,7 +217,7 @@ impl<'b> VirtualDom { fn load_template_root(&mut self, template: &VNode, root_idx: usize) -> ElementId { // Get an ID for this root since it's a real root let this_id = self.next_root(template, root_idx); - template.root_ids[root_idx].set(Some(this_id)); + template.root_ids.set(root_idx, this_id); self.mutations.push(LoadTemplate { name: template.template.get().name, diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 3082c34a4..6cbad2828 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -108,11 +108,7 @@ impl<'b> VirtualDom { }); // Make sure the roots get transferred over while we're here - left_template - .root_ids - .iter() - .zip(right_template.root_ids.iter()) - .for_each(|(left, right)| right.set(left.get())); + right_template.root_ids.transfer(&left_template.root_ids); } fn diff_dynamic_node( @@ -662,7 +658,7 @@ impl<'b> VirtualDom { Some(node) => node, None => { self.mutations.push(Mutation::PushRoot { - id: node.root_ids[idx].get().unwrap(), + id: node.root_ids.get(idx).unwrap(), }); return 1; } @@ -793,7 +789,7 @@ impl<'b> VirtualDom { if let Some(dy) = node.dynamic_root(idx) { self.remove_dynamic_node(dy, gen_muts); } else { - let id = node.root_ids[idx].get().unwrap(); + let id = node.root_ids.get(idx).unwrap(); if gen_muts { self.mutations.push(Mutation::Remove { id }); } @@ -889,7 +885,7 @@ impl<'b> VirtualDom { fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId { match node.dynamic_root(0) { - None => node.root_ids[0].get().unwrap(), + None => node.root_ids.get(0).unwrap(), Some(Text(t)) => t.id.get().unwrap(), Some(Fragment(t)) => self.find_first_element(&t[0]), Some(Placeholder(t)) => t.id.get().unwrap(), @@ -905,7 +901,7 @@ impl<'b> VirtualDom { fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId { match node.dynamic_root(node.template.get().roots.len() - 1) { - None => node.root_ids.last().unwrap().get().unwrap(), + None => node.root_ids.last().unwrap(), Some(Text(t)) => t.id.get().unwrap(), Some(Fragment(t)) => self.find_last_element(t.last().unwrap()), Some(Placeholder(t)) => t.id.get().unwrap(), diff --git a/packages/core/src/fragment.rs b/packages/core/src/fragment.rs index 623f9b340..a9e919bfa 100644 --- a/packages/core/src/fragment.rs +++ b/packages/core/src/fragment.rs @@ -32,7 +32,7 @@ pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element { key: children.key, parent: children.parent, template: children.template.clone(), - root_ids: children.root_ids, + root_ids: children.root_ids.clone(), dynamic_nodes: children.dynamic_nodes, dynamic_attrs: children.dynamic_attrs, }) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 5a325d2b9..41b312b3d 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -5,7 +5,7 @@ use bumpalo::boxed::Box as BumpBox; use bumpalo::Bump; use std::{ any::{Any, TypeId}, - cell::{Cell, RefCell}, + cell::{Cell, RefCell, UnsafeCell}, fmt::Arguments, future::Future, }; @@ -46,7 +46,7 @@ pub struct VNode<'a> { /// The IDs for the roots of this template - to be used when moving the template around and removing it from /// the actual Dom - pub root_ids: bumpalo::collections::Vec<'a, Cell>>, + pub root_ids: BoxedCellSlice, /// The dynamic parts of the template pub dynamic_nodes: &'a [DynamicNode<'a>], @@ -55,13 +55,106 @@ pub struct VNode<'a> { pub dynamic_attrs: &'a [Attribute<'a>], } +// Saftey: There is no way to get references to the internal data of this struct so no refrences will be invalidated by mutating the data with a immutable reference (The same principle behind Cell) +#[derive(Debug, Default)] +pub struct BoxedCellSlice(UnsafeCell>>); + +impl Clone for BoxedCellSlice { + fn clone(&self) -> Self { + Self(UnsafeCell::new(unsafe { (*self.0.get()).clone() })) + } +} + +impl BoxedCellSlice { + pub fn last(&self) -> Option { + unsafe { + (*self.0.get()) + .as_ref() + .and_then(|inner| inner.as_ref().last().copied()) + } + } + + pub fn get(&self, idx: usize) -> Option { + unsafe { + (*self.0.get()) + .as_ref() + .and_then(|inner| inner.as_ref().get(idx).copied()) + } + } + + pub unsafe fn get_unchecked(&self, idx: usize) -> Option { + (*self.0.get()) + .as_ref() + .and_then(|inner| inner.as_ref().get(idx).copied()) + } + + pub fn set(&self, idx: usize, new: ElementId) { + unsafe { + if let Some(inner) = &mut *self.0.get() { + inner[idx] = new; + } + } + } + + pub fn intialize(&self, contents: Box<[ElementId]>) { + unsafe { + *self.0.get() = Some(contents); + } + } + + pub fn transfer(&self, other: &Self) { + unsafe { + *self.0.get() = (*other.0.get()).take(); + } + } + + pub fn len(&self) -> usize { + unsafe { + (*self.0.get()) + .as_ref() + .map(|inner| inner.len()) + .unwrap_or(0) + } + } +} + +impl<'a> IntoIterator for &'a BoxedCellSlice { + type Item = ElementId; + + type IntoIter = BoxedCellSliceIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + BoxedCellSliceIter { + index: 0, + borrow: self, + } + } +} + +pub struct BoxedCellSliceIter<'a> { + index: usize, + borrow: &'a BoxedCellSlice, +} + +impl Iterator for BoxedCellSliceIter<'_> { + type Item = ElementId; + + fn next(&mut self) -> Option { + let result = self.borrow.get(self.index); + if result.is_some() { + self.index += 1; + } + result + } +} + impl<'a> VNode<'a> { /// Create a template with no nodes that will be skipped over during diffing pub fn empty() -> Element<'a> { Some(VNode { key: None, parent: None, - root_ids: bumpalo::collections::Vec::new_in(&Bump::new()), + root_ids: BoxedCellSlice::default(), dynamic_nodes: &[], dynamic_attrs: &[], template: Cell::new(Template { @@ -572,7 +665,7 @@ impl<'a> IntoDynNode<'a> for &'a VNode<'a> { DynamicNode::Fragment(_cx.bump().alloc([VNode { parent: self.parent, template: self.template.clone(), - root_ids: self.root_ids, + root_ids: self.root_ids.clone(), key: self.key, dynamic_nodes: self.dynamic_nodes, dynamic_attrs: self.dynamic_attrs, diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index f39f3eb9f..9f68c8f6a 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -212,7 +212,7 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> { parent: None, key: #key_tokens, template: std::cell::Cell::new(TEMPLATE), - root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut _).as_slice_of_cells(), + root_ids: Default::default(), // root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut [::dioxus::core::ElementId]).as_slice_of_cells(), dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]), dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),