diff --git a/mast/src/node.rs b/mast/src/node.rs index a2fa481..fe29e97 100644 --- a/mast/src/node.rs +++ b/mast/src/node.rs @@ -6,7 +6,6 @@ use crate::{Hash, Hasher, HASH_LEN}; // TODO: room for improvement (pending actual benchmarks to justify): // - cache encoding -// - cache hashing // TODO: remove unwrap // TODO: KeyType and ValueType @@ -24,6 +23,10 @@ pub struct Node { // Metadata that should not be encoded. ref_count: u64, + + // Memoized hashes + /// The Hash of the node, if None then something changed, and the hash should be recomputed. + hash: Option, } #[derive(Debug)] @@ -47,6 +50,7 @@ impl Node { right: None, ref_count: 0, + hash: None, } } @@ -95,9 +99,13 @@ impl Node { } /// Returns the hash of the node. - pub fn hash(&self) -> Hash { - let encoded = self.canonical_encode(); - hash(&encoded) + pub fn hash(&mut self) -> Hash { + self.hash.unwrap_or_else(|| { + let encoded = self.canonical_encode(); + let hash = hash(&encoded); + self.hash = Some(hash); + hash + }) } // === Private Methods === @@ -105,6 +113,8 @@ impl Node { /// Set the value. pub(crate) fn set_value(&mut self, value: &[u8]) -> &mut Self { self.value = value.into(); + self.hash = None; + self } @@ -124,6 +134,7 @@ impl Node { Branch::Left => self.left = new_child, Branch::Right => self.right = new_child, }; + self.hash = None; self } @@ -250,5 +261,6 @@ fn decode_node(data: (u64, &[u8])) -> Node { right, ref_count, + hash: None, } } diff --git a/mast/src/operations/remove.rs b/mast/src/operations/remove.rs index b4020a4..a2c8bf0 100644 --- a/mast/src/operations/remove.rs +++ b/mast/src/operations/remove.rs @@ -26,8 +26,8 @@ pub(crate) fn remove<'a>( node.decrement_ref_count().save(nodes_table); match branch { - Branch::Left => node.set_left_child(root.map(|n| n.hash())), - Branch::Right => node.set_right_child(root.map(|n| n.hash())), + Branch::Left => node.set_left_child(root.map(|mut n| n.hash())), + Branch::Right => node.set_right_child(root.map(|mut n| n.hash())), }; node.increment_ref_count().save(nodes_table); @@ -107,7 +107,7 @@ fn zip_up( .decrement_ref_count() .save(nodes_table) // save new version - .set_left_child(previous.map(|n| n.hash())) + .set_left_child(previous.map(|mut n| n.hash())) .increment_ref_count() .save(nodes_table); @@ -127,7 +127,7 @@ fn zip_up( .decrement_ref_count() .save(nodes_table) // save new version - .set_right_child(previous.map(|n| n.hash())) + .set_right_child(previous.map(|mut n| n.hash())) .increment_ref_count() .save(nodes_table); diff --git a/mast/src/test.rs b/mast/src/test.rs index a4caee5..47af8af 100644 --- a/mast/src/test.rs +++ b/mast/src/test.rs @@ -150,7 +150,7 @@ fn verify_children_rank(treap: &HashTreap, node: Option) -> bool { fn assert_root(treap: &HashTreap, expected_root_hash: &str) { let root_hash = treap .root() - .map(|n| n.hash()) + .map(|mut n| n.hash()) .expect("Has root hash after insertion"); assert_eq!( @@ -167,8 +167,8 @@ fn into_mermaid_graph(treap: &HashTreap) -> String { graph.push_str("graph TD;\n"); - if let Some(root) = treap.root() { - build_graph_string(&treap, &root, &mut graph); + if let Some(mut root) = treap.root() { + build_graph_string(&treap, &mut root, &mut graph); } graph.push_str(&format!( @@ -178,28 +178,28 @@ fn into_mermaid_graph(treap: &HashTreap) -> String { graph } -fn build_graph_string(treap: &HashTreap, node: &Node, graph: &mut String) { +fn build_graph_string(treap: &HashTreap, node: &mut Node, graph: &mut String) { let key = format_key(node.key()); let node_label = format!("{}(({}))", node.hash(), key); // graph.push_str(&format!("## START node {}\n", node_label)); - if let Some(child) = treap.get_node(node.left()) { + if let Some(mut child) = treap.get_node(node.left()) { let key = format_key(child.key()); let child_label = format!("{}(({}))", child.hash(), key); graph.push_str(&format!(" {} --l--> {};\n", node_label, child_label)); - build_graph_string(&treap, &child, graph); + build_graph_string(&treap, &mut child, graph); } else { graph.push_str(&format!(" {} -.-> {}l((l));\n", node_label, node.hash())); graph.push_str(&format!(" class {}l null;\n", node.hash())); } - if let Some(child) = treap.get_node(node.right()) { + if let Some(mut child) = treap.get_node(node.right()) { let key = format_key(child.key()); let child_label = format!("{}(({}))", child.hash(), key); graph.push_str(&format!(" {} --r--> {};\n", node_label, child_label)); - build_graph_string(&treap, &child, graph); + build_graph_string(&treap, &mut child, graph); } else { graph.push_str(&format!(" {} -.-> {}r((r));\n", node_label, node.hash())); graph.push_str(&format!(" class {}r null;\n", node.hash())); diff --git a/mast/src/treap.rs b/mast/src/treap.rs index 3a33c88..626ba41 100644 --- a/mast/src/treap.rs +++ b/mast/src/treap.rs @@ -68,7 +68,7 @@ impl<'treap> HashTreap<'treap> { .root_hash_inner(&roots_table) .and_then(|hash| Node::open(&nodes_table, hash)); - let new_root = crate::operations::insert(&mut nodes_table, old_root, key, value); + let mut new_root = crate::operations::insert(&mut nodes_table, old_root, key, value); roots_table .insert(self.name.as_bytes(), new_root.hash().as_bytes().as_slice()) @@ -96,7 +96,7 @@ impl<'treap> HashTreap<'treap> { removed_node = old_node; - if let Some(new_root) = new_root { + if let Some(mut new_root) = new_root { roots_table .insert(self.name.as_bytes(), new_root.hash().as_bytes().as_slice()) .unwrap();