Browse Source

allow changing the number of root nodes

Evan Almloff 2 years ago
parent
commit
4c1fe1d9bb

+ 3 - 5
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);
             }
         }
     }

+ 5 - 1
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,

+ 5 - 9
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(),

+ 1 - 1
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,
     })

+ 97 - 4
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<Option<ElementId>>>,
+    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<Option<Box<[ElementId]>>>);
+
+impl Clone for BoxedCellSlice {
+    fn clone(&self) -> Self {
+        Self(UnsafeCell::new(unsafe { (*self.0.get()).clone() }))
+    }
+}
+
+impl BoxedCellSlice {
+    pub fn last(&self) -> Option<ElementId> {
+        unsafe {
+            (*self.0.get())
+                .as_ref()
+                .and_then(|inner| inner.as_ref().last().copied())
+        }
+    }
+
+    pub fn get(&self, idx: usize) -> Option<ElementId> {
+        unsafe {
+            (*self.0.get())
+                .as_ref()
+                .and_then(|inner| inner.as_ref().get(idx).copied())
+        }
+    }
+
+    pub unsafe fn get_unchecked(&self, idx: usize) -> Option<ElementId> {
+        (*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<Self::Item> {
+        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,

+ 1 - 1
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 ),* ]),