Skip to content

Commit ad4981e

Browse files
ealmloffjkelleyrtp
andauthored
Switch to a const hash instead of comparing pointers for template diffing (#5276)
* switch to a const hash * fix formatting * fix id truncation * fix const * code nits * fix merge conflict issue causing hashing to fail * rollback serde thing --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
1 parent 4cdbddb commit ad4981e

23 files changed

Lines changed: 220 additions & 137 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ tmp/
4141

4242
# in debugging we frequently dump wasm to wat with `wasm-tools print`
4343
*.wat
44+
45+
# External macos drives have extra ._ files
46+
._*

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli-harnesses/harness-fullstack-with-optional-tokio/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,3 @@ default = []
1616
server = ["dioxus/server", "dep:tokio"]
1717
web = ["dioxus/web"]
1818

19-

packages/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ futures-util = { workspace = true, default-features = false, features = ["alloc"
2525
serde = { workspace = true, optional = true, features = ["derive"] }
2626
subsecond = { workspace = true }
2727
anyhow = { workspace = true }
28+
xxhash-rust = { workspace = true, features = ["const_xxh64"] }
2829

2930
[dev-dependencies]
3031
dioxus = { workspace = true }

packages/core/src/diff/iterator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ impl VNode {
483483
let mount = mounts.get(self.mount.get().0).unwrap();
484484

485485
template
486-
.roots
486+
.roots()
487487
.iter()
488488
.enumerate()
489489
.map(

packages/core/src/diff/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ impl VirtualDom {
9898
#[allow(dead_code)]
9999
fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
100100
let template = node.template;
101-
let path = template.node_paths[idx];
101+
let path = template.node_paths()[idx];
102102

103103
// use a loop to index every static node's children until the path has run out
104104
// only break if the last path index is a dynamic node
105-
let mut static_node = &template.roots[path[0] as usize];
105+
let mut static_node = &template.roots()[path[0] as usize];
106106

107107
for i in 1..path.len() - 1 {
108108
match static_node {

packages/core/src/diff/node.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl VNode {
143143
&self,
144144
root_idx: usize,
145145
) -> Option<(usize, &DynamicNode)> {
146-
self.template.roots[root_idx]
146+
self.template.roots()[root_idx]
147147
.dynamic_id()
148148
.map(|id| (id, &self.dynamic_nodes[id]))
149149
}
@@ -180,7 +180,7 @@ impl VNode {
180180

181181
pub(crate) fn find_last_element(&self, dom: &VirtualDom) -> ElementId {
182182
let mount_id = self.mount.get();
183-
let last_root_index = self.template.roots.len() - 1;
183+
let last_root_index = self.template.roots().len() - 1;
184184
let last = match self.get_dynamic_root_node_and_id(last_root_index) {
185185
// This node is static, just get the root id
186186
None => dom.get_mounted_root_node(mount_id, last_root_index),
@@ -306,7 +306,7 @@ impl VNode {
306306
destroy_component_state: bool,
307307
replace_with: Option<usize>,
308308
) {
309-
let roots = self.template.roots;
309+
let roots = self.template.roots();
310310
for (idx, node) in roots.iter().enumerate() {
311311
let last_node = idx == roots.len() - 1;
312312
if let Some(id) = node.dynamic_id() {
@@ -343,7 +343,7 @@ impl VNode {
343343
) {
344344
let template = self.template;
345345
for (idx, dyn_node) in self.dynamic_nodes.iter().enumerate() {
346-
let path_len = template.node_paths.get(idx).map(|path| path.len());
346+
let path_len = template.node_paths().get(idx).map(|path| path.len());
347347
// Roots are cleaned up automatically above and nodes with a empty path are placeholders
348348
if let Some(2..) = path_len {
349349
self.remove_dynamic_node(
@@ -398,7 +398,7 @@ impl VNode {
398398

399399
pub(super) fn reclaim_attributes(&self, mount: MountId, dom: &mut VirtualDom) {
400400
let mut next_id = None;
401-
for (idx, path) in self.template.attr_paths.iter().enumerate() {
401+
for (idx, path) in self.template.attr_paths().iter().enumerate() {
402402
// We clean up the roots in the next step, so don't worry about them here
403403
if path.len() <= 1 {
404404
continue;
@@ -429,7 +429,7 @@ impl VNode {
429429
let mut old_attributes_iter = old_attrs.iter().peekable();
430430
let mut new_attributes_iter = new_attrs.iter().peekable();
431431
let attribute_id = dom.get_mounted_dyn_attr(mount_id, idx);
432-
let path = self.template.attr_paths[idx];
432+
let path = self.template.attr_paths()[idx];
433433

434434
loop {
435435
match (old_attributes_iter.peek(), new_attributes_iter.peek()) {
@@ -558,18 +558,18 @@ impl VNode {
558558
entry.insert(VNodeMount {
559559
node: self.clone(),
560560
parent,
561-
root_ids: vec![ElementId(0); template.roots.len()].into_boxed_slice(),
562-
mounted_attributes: vec![ElementId(0); template.attr_paths.len()]
561+
root_ids: vec![ElementId(0); template.roots().len()].into_boxed_slice(),
562+
mounted_attributes: vec![ElementId(0); template.attr_paths().len()]
563563
.into_boxed_slice(),
564-
mounted_dynamic_nodes: vec![usize::MAX; template.node_paths.len()]
564+
mounted_dynamic_nodes: vec![usize::MAX; template.node_paths().len()]
565565
.into_boxed_slice(),
566566
});
567567
}
568568

569569
// Walk the roots, creating nodes and assigning IDs
570570
// nodes in an iterator of (dynamic_node_index, path) and attrs in an iterator of (attr_index, path)
571-
let mut nodes = template.node_paths.iter().copied().enumerate().peekable();
572-
let mut attrs = template.attr_paths.iter().copied().enumerate().peekable();
571+
let mut nodes = template.node_paths().iter().copied().enumerate().peekable();
572+
let mut attrs = template.attr_paths().iter().copied().enumerate().peekable();
573573

574574
// Get the mounted id of this block
575575
// At this point, we should have already mounted the block
@@ -588,7 +588,7 @@ impl VNode {
588588
// Go through each root node and create the node, adding it to the stack.
589589
// Each node already exists in the template, so we can just clone it from the template
590590
let nodes_created = template
591-
.roots
591+
.roots()
592592
.iter()
593593
.enumerate()
594594
.map(|(root_idx, root)| {
@@ -646,7 +646,7 @@ impl VNode {
646646
fn reference_to_dynamic_node(&self, mount: MountId, dynamic_node_id: usize) -> ElementRef {
647647
ElementRef {
648648
path: ElementPath {
649-
path: self.template.node_paths[dynamic_node_id],
649+
path: self.template.node_paths()[dynamic_node_id],
650650
},
651651
mount,
652652
}
@@ -772,7 +772,7 @@ impl VNode {
772772
// If we actually created real new nodes, we need to replace the placeholder for this dynamic node with the new dynamic nodes
773773
if m > 0 {
774774
// The path is one shorter because the top node is the root
775-
let path = &self.template.node_paths[dynamic_node_id][1..];
775+
let path = &self.template.node_paths()[dynamic_node_id][1..];
776776
to.replace_placeholder_with_nodes(path, m);
777777
}
778778
}

packages/core/src/error_boundary.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ impl<F: Fn(ErrorContext) -> Element + 'static> From<F> for ErrorHandler {
153153
}
154154

155155
fn default_handler(errors: ErrorContext) -> Element {
156-
static TEMPLATE: Template = Template {
157-
roots: &[TemplateNode::Element {
156+
static TEMPLATE: Template = Template::new(
157+
&[TemplateNode::Element {
158158
tag: "div",
159159
namespace: None,
160160
attrs: &[TemplateAttribute::Static {
@@ -164,29 +164,29 @@ fn default_handler(errors: ErrorContext) -> Element {
164164
}],
165165
children: &[TemplateNode::Dynamic { id: 0usize }],
166166
}],
167-
node_paths: &[&[0u8, 0u8]],
168-
attr_paths: &[],
169-
};
167+
&[&[0u8, 0u8]],
168+
&[],
169+
);
170170
std::result::Result::Ok(VNode::new(
171171
None,
172172
TEMPLATE,
173173
Box::new([errors
174174
.error()
175175
.iter()
176176
.map(|e| {
177-
static TEMPLATE: Template = Template {
178-
roots: &[TemplateNode::Element {
177+
static INNER_TEMPLATE: Template = Template::new(
178+
&[TemplateNode::Element {
179179
tag: "pre",
180180
namespace: None,
181181
attrs: &[],
182182
children: &[TemplateNode::Dynamic { id: 0usize }],
183183
}],
184-
node_paths: &[&[0u8, 0u8]],
185-
attr_paths: &[],
186-
};
184+
&[&[0u8, 0u8]],
185+
&[],
186+
);
187187
VNode::new(
188188
None,
189-
TEMPLATE,
189+
INNER_TEMPLATE,
190190
Box::new([e.to_string().into_dyn_node()]),
191191
Default::default(),
192192
)
@@ -320,11 +320,8 @@ pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
320320
(props.handle_error.0)(error_boundary.clone())
321321
} else {
322322
std::result::Result::Ok({
323-
static TEMPLATE: Template = Template {
324-
roots: &[TemplateNode::Dynamic { id: 0usize }],
325-
node_paths: &[&[0u8]],
326-
attr_paths: &[],
327-
};
323+
static TEMPLATE: Template =
324+
Template::new(&[TemplateNode::Dynamic { id: 0usize }], &[&[0u8]], &[]);
328325
VNode::new(
329326
None,
330327
TEMPLATE,

packages/core/src/hotreload_utils.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,7 @@ impl HotReloadedTemplate {
373373
let node_paths = Self::node_paths(roots);
374374
let attr_paths = Self::attr_paths(roots);
375375

376-
let template = Template {
377-
roots,
378-
node_paths,
379-
attr_paths,
380-
};
376+
let template = Template::new(roots, node_paths, attr_paths);
381377
Self {
382378
key,
383379
dynamic_nodes,

0 commit comments

Comments
 (0)