diff --git a/client/finality-grandpa/src/authorities.rs b/client/finality-grandpa/src/authorities.rs index 0803e6b3c2931..c2d44371986ca 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/finality-grandpa/src/authorities.rs @@ -229,7 +229,7 @@ where where F: Fn(&H, &H) -> Result, { - let filter = |node_hash: &H, node_num: &N, _: &PendingChange| { + let mut filter = |node_hash: &H, node_num: &N, _: &PendingChange| { if number >= *node_num && (is_descendent_of(node_hash, &hash).unwrap_or_default() || *node_hash == hash) { @@ -245,7 +245,7 @@ where }; // Remove standard changes. - let _ = self.pending_standard_changes.drain_filter(&filter); + let _ = self.pending_standard_changes.drain_filter(&mut filter); // Remove forced changes. self.pending_forced_changes diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index 45957f4390532..b4985b77294b2 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -81,7 +81,6 @@ pub enum FilterAction { } /// A tree data structure that stores several nodes across multiple branches. -/// /// Top-level branches are called roots. The tree has functionality for /// finalizing nodes, which means that that node is traversed, and all competing /// branches are pruned. It also guarantees that nodes in the tree are finalized @@ -94,6 +93,90 @@ pub struct ForkTree { best_finalized_number: Option, } +impl ForkTree +where + H: PartialEq + Clone, + N: Ord + Clone, + V: Clone, +{ + /// Prune the tree, removing all non-canonical nodes. We find the node in the + /// tree that is the deepest ancestor of the given hash and that passes the + /// given predicate. If such a node exists, we re-root the tree to this + /// node. Otherwise the tree remains unchanged. The given function + /// `is_descendent_of` should return `true` if the second hash (target) is a + /// descendent of the first hash (base). + /// + /// Returns all pruned node data. + pub fn prune( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let new_root_index = + self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + let removed = if let Some(mut root_index) = new_root_index { + let mut old_roots = std::mem::take(&mut self.roots); + + let mut root = None; + let mut cur_children = Some(&mut old_roots); + + while let Some(cur_index) = root_index.pop() { + if let Some(children) = cur_children.take() { + if root_index.is_empty() { + root = Some(children.remove(cur_index)); + } else { + cur_children = Some(&mut children[cur_index].children); + } + } + } + + let mut root = root.expect( + "find_node_index_where will return array with at least one index; \ + this results in at least one item in removed; qed", + ); + + let mut removed = old_roots; + + // we found the deepest ancestor of the finalized block, so we prune + // out any children that don't include the finalized block. + let root_children = std::mem::take(&mut root.children); + let mut is_first = true; + + for child in root_children { + if is_first && + (child.number == *number && child.hash == *hash || + child.number < *number && is_descendent_of(&child.hash, hash)?) + { + root.children.push(child); + // assuming that the tree is well formed only one child should pass this + // requirement due to ancestry restrictions (i.e. they must be different forks). + is_first = false; + } else { + removed.push(child); + } + } + + self.roots = vec![root]; + + removed + } else { + Vec::new() + }; + + self.rebalance(); + + Ok(RemovedIterator { stack: removed }) + } +} + impl ForkTree where H: PartialEq, @@ -104,19 +187,19 @@ where ForkTree { roots: Vec::new(), best_finalized_number: None } } - /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). + /// Rebalance the tree, i.e. sort child nodes by max branch depth + /// (decreasing). /// /// Most operations in the tree are performed with depth-first search /// starting from the leftmost node at every level, since this tree is meant /// to be used in a blockchain context, a good heuristic is that the node - /// we'll be looking for at any point will likely be in one of the deepest chains - /// (i.e. the longest ones). + /// we'll be looking + /// for at any point will likely be in one of the deepest chains (i.e. the + /// longest ones). pub fn rebalance(&mut self) { self.roots.sort_by_key(|n| Reverse(n.max_depth())); - let mut stack: Vec<_> = self.roots.iter_mut().collect(); - while let Some(node) = stack.pop() { - node.children.sort_by_key(|n| Reverse(n.max_depth())); - stack.extend(node.children.iter_mut()); + for root in &mut self.roots { + root.rebalance(); } } @@ -128,9 +211,9 @@ where /// Returns `true` if the imported node is a root. pub fn import( &mut self, - hash: H, - number: N, - data: V, + mut hash: H, + mut number: N, + mut data: V, is_descendent_of: &F, ) -> Result> where @@ -143,33 +226,29 @@ where } } - let mut children = &mut self.roots; - let mut i = 0; - while i < children.len() { - let child = &children[i]; - if child.hash == hash { + for root in self.roots.iter_mut() { + if root.hash == hash { return Err(Error::Duplicate) } - if child.number < number && is_descendent_of(&child.hash, &hash)? { - children = &mut children[i].children; - i = 0; - } else { - i += 1; + + match root.import(hash, number, data, is_descendent_of)? { + Some((h, n, d)) => { + hash = h; + number = n; + data = d; + }, + None => { + self.rebalance(); + return Ok(false) + }, } } - let is_first = children.is_empty(); - children.push(Node { data, hash, number, children: Default::default() }); + self.roots.push(Node { data, hash, number, children: Vec::new() }); - // Quick way to check if the pushed node is a root - let is_root = children.as_ptr() == self.roots.as_ptr(); - - if is_first { - // Rebalance is required only if we've extended the branch depth. - self.rebalance(); - } + self.rebalance(); - Ok(is_root) + Ok(true) } /// Iterates over the existing roots in the tree. @@ -188,53 +267,6 @@ where self.node_iter().map(|node| (&node.hash, &node.number, &node.data)) } - /// Map fork tree into values of new types. - /// - /// Tree traversal technique (e.g. BFS vs DFS) is left as not specified and - /// may be subject to change in the future. In other words, your predicates - /// should not rely on the observed traversal technique currently in use. - pub fn map(self, f: &mut F) -> ForkTree - where - F: FnMut(&H, &N, V) -> VT, - { - let mut queue: Vec<_> = - self.roots.into_iter().rev().map(|node| (usize::MAX, node)).collect(); - let mut next_queue = Vec::new(); - let mut output = Vec::new(); - - while !queue.is_empty() { - for (parent_index, node) in queue.drain(..) { - let new_data = f(&node.hash, &node.number, node.data); - let new_node = Node { - hash: node.hash, - number: node.number, - data: new_data, - children: Vec::with_capacity(node.children.len()), - }; - - let node_id = output.len(); - output.push((parent_index, new_node)); - - for child in node.children.into_iter().rev() { - next_queue.push((node_id, child)); - } - } - - std::mem::swap(&mut queue, &mut next_queue); - } - - let mut roots = Vec::new(); - while let Some((parent_index, new_node)) = output.pop() { - if parent_index == usize::MAX { - roots.push(new_node); - } else { - output[parent_index].1.children.push(new_node); - } - } - - ForkTree { roots, best_finalized_number: self.best_finalized_number } - } - /// Find a node in the tree that is the deepest ancestor of the given /// block hash and which passes the given predicate. The given function /// `is_descendent_of` should return `true` if the second hash (target) @@ -251,12 +283,27 @@ where F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, { - let maybe_path = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; - Ok(maybe_path.map(|path| { - let children = - path.iter().take(path.len() - 1).fold(&self.roots, |curr, &i| &curr[i].children); - &children[path[path.len() - 1]] - })) + // search for node starting from all roots + for root in self.roots.iter() { + let node = root.find_node_where(hash, number, is_descendent_of, predicate)?; + + // found the node, early exit + if let FindOutcome::Found(node) = node { + return Ok(Some(node)) + } + } + + Ok(None) + } + + /// Map fork tree into values of new types. + pub fn map(self, f: &mut F) -> ForkTree + where + F: FnMut(&H, &N, V) -> VT, + { + let roots = self.roots.into_iter().map(|root| root.map(f)).collect(); + + ForkTree { roots, best_finalized_number: self.best_finalized_number } } /// Same as [`find_node_where`](ForkTree::find_node_where), but returns mutable reference. @@ -272,121 +319,44 @@ where F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, { - let maybe_path = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; - Ok(maybe_path.map(|path| { - let children = path - .iter() - .take(path.len() - 1) - .fold(&mut self.roots, |curr, &i| &mut curr[i].children); - &mut children[path[path.len() - 1]] - })) - } + // search for node starting from all roots + for root in self.roots.iter_mut() { + let node = root.find_node_where_mut(hash, number, is_descendent_of, predicate)?; - /// Same as [`find_node_where`](ForkTree::find_node_where), but returns indices. - /// - /// The returned indices represent the full path to reach the matching node starting - /// from first to last, i.e. the earliest index in the traverse path goes first, and the final - /// index in the traverse path goes last. If a node is found that matches the predicate - /// the returned path should always contain at least one index, otherwise `None` is - /// returned. - pub fn find_node_index_where( - &self, - hash: &H, - number: &N, - is_descendent_of: &F, - predicate: &P, - ) -> Result>, Error> - where - E: std::error::Error, - F: Fn(&H, &H) -> Result, - P: Fn(&V) -> bool, - { - let mut path = vec![]; - let mut children = &self.roots; - let mut i = 0; - let mut best_depth = 0; - - while i < children.len() { - let node = &children[i]; - if node.number < *number && is_descendent_of(&node.hash, hash)? { - path.push(i); - if predicate(&node.data) { - best_depth = path.len(); - } - i = 0; - children = &node.children; - } else { - i += 1; + // found the node, early exit + if let FindOutcome::Found(node) = node { + return Ok(Some(node)) } } - Ok(if best_depth == 0 { - None - } else { - path.truncate(best_depth); - Some(path) - }) + Ok(None) } - /// Prune the tree, removing all non-canonical nodes. We find the node in the - /// tree that is the deepest ancestor of the given hash and that passes the - /// given predicate. If such a node exists, we re-root the tree to this - /// node. Otherwise the tree remains unchanged. The given function - /// `is_descendent_of` should return `true` if the second hash (target) is a - /// descendent of the first hash (base). - /// - /// Returns all pruned node data. - pub fn prune( - &mut self, + /// Same as [`find_node_where`](ForkTree::find_node_where), but returns indexes. + pub fn find_node_index_where( + &self, hash: &H, number: &N, is_descendent_of: &F, predicate: &P, - ) -> Result, Error> + ) -> Result>, Error> where E: std::error::Error, F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, { - let root_index = - match self.find_node_index_where(hash, number, is_descendent_of, predicate)? { - Some(idx) => idx, - None => return Ok(RemovedIterator { stack: Vec::new() }), - }; - - let mut old_roots = std::mem::take(&mut self.roots); - - let curr_children = root_index - .iter() - .take(root_index.len() - 1) - .fold(&mut old_roots, |curr, idx| &mut curr[*idx].children); - let mut root = curr_children.remove(root_index[root_index.len() - 1]); - - let mut removed = old_roots; - - // we found the deepest ancestor of the finalized block, so we prune - // out any children that don't include the finalized block. - let root_children = std::mem::take(&mut root.children); - let mut is_first = true; - - for child in root_children { - if is_first && - (child.number == *number && child.hash == *hash || - child.number < *number && is_descendent_of(&child.hash, hash)?) - { - root.children.push(child); - // assuming that the tree is well formed only one child should pass this - // requirement due to ancestry restrictions (i.e. they must be different forks). - is_first = false; - } else { - removed.push(child); + // search for node starting from all roots + for (index, root) in self.roots.iter().enumerate() { + let node = root.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + // found the node, early exit + if let FindOutcome::Found(mut node) = node { + node.push(index); + return Ok(Some(node)) } } - self.roots = vec![root]; - self.rebalance(); - - Ok(RemovedIterator { stack: removed }) + Ok(None) } /// Finalize a root in the tree and return it, return `None` in case no root @@ -668,71 +638,44 @@ where } /// Remove from the tree some nodes (and their subtrees) using a `filter` predicate. - /// /// The `filter` is called over tree nodes and returns a filter action: /// - `Remove` if the node and its subtree should be removed; /// - `KeepNode` if we should maintain the node and keep processing the tree. /// - `KeepTree` if we should maintain the node and its entire subtree. - /// /// An iterator over all the pruned nodes is returned. - pub fn drain_filter(&mut self, filter: F) -> impl Iterator + pub fn drain_filter(&mut self, mut filter: F) -> impl Iterator where - F: Fn(&H, &N, &V) -> FilterAction, + F: FnMut(&H, &N, &V) -> FilterAction, { - let mut removed = vec![]; - let mut retained = Vec::new(); - - let mut queue: Vec<_> = std::mem::take(&mut self.roots) - .into_iter() - .rev() - .map(|node| (usize::MAX, node)) - .collect(); - let mut next_queue = Vec::new(); - - while !queue.is_empty() { - for (parent_idx, mut node) in queue.drain(..) { - match filter(&node.hash, &node.number, &node.data) { - FilterAction::KeepNode => { - let node_idx = retained.len(); - let children = std::mem::take(&mut node.children); - retained.push((parent_idx, node)); - for child in children.into_iter().rev() { - next_queue.push((node_idx, child)); - } - }, - FilterAction::KeepTree => { - retained.push((parent_idx, node)); - }, - FilterAction::Remove => { - removed.push(node); - }, - } - } - - std::mem::swap(&mut queue, &mut next_queue); - } - - while let Some((parent_idx, node)) = retained.pop() { - if parent_idx == usize::MAX { - self.roots.push(node); + let mut removed = Vec::new(); + let mut i = 0; + while i < self.roots.len() { + if self.roots[i].drain_filter(&mut filter, &mut removed) { + removed.push(self.roots.remove(i)); } else { - retained[parent_idx].1.children.push(node); + i += 1; } } - - if !removed.is_empty() { - self.rebalance(); - } + self.rebalance(); RemovedIterator { stack: removed } } } // Workaround for: https://github.com/rust-lang/rust/issues/34537 -use node_implementation::Node; - mod node_implementation { use super::*; + /// The outcome of a search within a node. + pub enum FindOutcome { + // this is the node we were looking for. + Found(T), + // not the node we're looking for. contains a flag indicating + // whether the node was a descendent. true implies the predicate failed. + Failure(bool), + // Abort search. + Abort, + } + #[derive(Clone, Debug, Decode, Encode, PartialEq)] pub struct Node { pub hash: H, @@ -742,21 +685,239 @@ mod node_implementation { } impl Node { + /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). + pub fn rebalance(&mut self) { + self.children.sort_by_key(|n| Reverse(n.max_depth())); + for child in &mut self.children { + child.rebalance(); + } + } + /// Finds the max depth among all branches descendent from this node. pub fn max_depth(&self) -> usize { - let mut max: usize = 0; - let mut stack = vec![(self, 0)]; - while let Some((node, height)) = stack.pop() { - if height > max { - max = height; + let mut max = 0; + + for node in &self.children { + max = node.max_depth().max(max) + } + + max + 1 + } + + /// Map node data into values of new types. + pub fn map(self, f: &mut F) -> Node + where + F: FnMut(&H, &N, V) -> VT, + { + let children = self.children.into_iter().map(|node| node.map(f)).collect(); + + let vt = f(&self.hash, &self.number, self.data); + Node { hash: self.hash, number: self.number, data: vt, children } + } + + pub fn import( + &mut self, + mut hash: H, + mut number: N, + mut data: V, + is_descendent_of: &F, + ) -> Result, Error> + where + E: fmt::Debug, + F: Fn(&H, &H) -> Result, + { + if self.hash == hash { + return Err(Error::Duplicate) + }; + + if number <= self.number { + return Ok(Some((hash, number, data))) + } + + for node in self.children.iter_mut() { + match node.import(hash, number, data, is_descendent_of)? { + Some((h, n, d)) => { + hash = h; + number = n; + data = d; + }, + None => return Ok(None), } - node.children.iter().for_each(|n| stack.push((n, height + 1))); } - max + + if is_descendent_of(&self.hash, &hash)? { + self.children.push(Node { data, hash, number, children: Vec::new() }); + + Ok(None) + } else { + Ok(Some((hash, number, data))) + } + } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash which also passes the given predicate, backtracking + /// when the predicate fails. + /// The given function `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + /// + /// The returned indices are from last to first. The earliest index in the traverse path + /// goes last, and the final index in the traverse path goes first. An empty list means + /// that the current node is the result. + pub fn find_node_index_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + // stop searching this branch + if *number < self.number { + return Ok(FindOutcome::Failure(false)) + } + + let mut known_descendent_of = false; + + // continue depth-first search through all children + for (i, node) in self.children.iter().enumerate() { + // found node, early exit + match node.find_node_index_where(hash, number, is_descendent_of, predicate)? { + FindOutcome::Abort => return Ok(FindOutcome::Abort), + FindOutcome::Found(mut x) => { + x.push(i); + return Ok(FindOutcome::Found(x)) + }, + FindOutcome::Failure(true) => { + // if the block was a descendent of this child, + // then it cannot be a descendent of any others, + // so we don't search them. + known_descendent_of = true; + break + }, + FindOutcome::Failure(false) => {}, + } + } + + // node not found in any of the descendents, if the node we're + // searching for is a descendent of this node then we will stop the + // search here, since there aren't any more children and we found + // the correct node so we don't want to backtrack. + let is_descendent_of = known_descendent_of || is_descendent_of(&self.hash, hash)?; + if is_descendent_of { + // if the predicate passes we return the node + if predicate(&self.data) { + return Ok(FindOutcome::Found(Vec::new())) + } + } + + // otherwise, tell our ancestor that we failed, and whether + // the block was a descendent. + Ok(FindOutcome::Failure(is_descendent_of)) + } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash which also passes the given predicate, backtracking + /// when the predicate fails. + /// The given function `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let outcome = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + match outcome { + FindOutcome::Abort => Ok(FindOutcome::Abort), + FindOutcome::Failure(f) => Ok(FindOutcome::Failure(f)), + FindOutcome::Found(mut indexes) => { + let mut cur = self; + + while let Some(i) = indexes.pop() { + cur = &cur.children[i]; + } + Ok(FindOutcome::Found(cur)) + }, + } + } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash which also passes the given predicate, backtracking + /// when the predicate fails. + /// The given function `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where_mut( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let outcome = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + match outcome { + FindOutcome::Abort => Ok(FindOutcome::Abort), + FindOutcome::Failure(f) => Ok(FindOutcome::Failure(f)), + FindOutcome::Found(mut indexes) => { + let mut cur = self; + + while let Some(i) = indexes.pop() { + cur = &mut cur.children[i]; + } + Ok(FindOutcome::Found(cur)) + }, + } + } + + /// Calls a `filter` predicate for the given node. + /// The `filter` is called over tree nodes and returns a filter action: + /// - `Remove` if the node and its subtree should be removed; + /// - `KeepNode` if we should maintain the node and keep processing the tree; + /// - `KeepTree` if we should maintain the node and its entire subtree. + /// Pruned subtrees are added to the `removed` list. + /// Returns a booleans indicateing if this node (and its subtree) should be removed. + pub fn drain_filter(&mut self, filter: &mut F, removed: &mut Vec>) -> bool + where + F: FnMut(&H, &N, &V) -> FilterAction, + { + match filter(&self.hash, &self.number, &self.data) { + FilterAction::KeepNode => { + let mut i = 0; + while i < self.children.len() { + if self.children[i].drain_filter(filter, removed) { + removed.push(self.children.remove(i)); + } else { + i += 1; + } + } + false + }, + FilterAction::KeepTree => false, + FilterAction::Remove => true, + } } } } +// Workaround for: https://github.com/rust-lang/rust/issues/34537 +use node_implementation::{FindOutcome, Node}; + struct ForkTreeIterator<'a, H, N, V> { stack: Vec<&'a Node>, } @@ -824,7 +985,7 @@ mod test { // / / // A - F - H - I // \ \ - // \ - L - M - N + // \ - L - M // \ \ // \ - O // - J - K @@ -835,7 +996,7 @@ mod test { // diagram above. the children will be ordered by subtree depth and the longest branches // will be on the leftmost side of the tree. let is_descendent_of = |base: &&str, block: &&str| -> Result { - let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]; + let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"]; match (*base, *block) { ("A", b) => Ok(letters.into_iter().any(|n| n == b)), ("B", b) => Ok(b == "C" || b == "D" || b == "E"), @@ -843,14 +1004,14 @@ mod test { ("D", b) => Ok(b == "E"), ("E", _) => Ok(false), ("F", b) => - Ok(b == "G" || b == "H" || b == "I" || b == "L" || b == "M" || b == "N" || b == "O"), + Ok(b == "G" || b == "H" || b == "I" || b == "L" || b == "M" || b == "O"), ("G", _) => Ok(false), - ("H", b) => Ok(b == "I" || b == "L" || b == "M" || b == "N" || b == "O"), + ("H", b) => Ok(b == "I" || b == "L" || b == "M" || b == "O"), ("I", _) => Ok(false), ("J", b) => Ok(b == "K"), ("K", _) => Ok(false), - ("L", b) => Ok(b == "M" || b == "O" || b == "N"), - ("M", b) => Ok(b == "N"), + ("L", b) => Ok(b == "M" || b == "O"), + ("M", _) => Ok(false), ("O", _) => Ok(false), ("0", _) => Ok(true), _ => Ok(false), @@ -1076,7 +1237,7 @@ mod test { // // Nodes B, C, F and G are not part of the tree. match (*base, *block) { - ("A0", b) => Ok(b == "B" || b == "C" || b == "D" || b == "E" || b == "G"), + ("A0", b) => Ok(b == "B" || b == "C" || b == "D" || b == "G"), ("A1", _) => Ok(false), ("C", b) => Ok(b == "D"), ("D", b) => Ok(b == "E" || b == "F" || b == "G"), @@ -1085,16 +1246,10 @@ mod test { } }; - let is_root = tree.import("A0", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); - assert!(is_root); - let is_root = tree.import("A1", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); - assert!(is_root); - let is_root = - tree.import("D", 10, Change { effective: 10 }, &is_descendent_of).unwrap(); - assert!(!is_root); - let is_root = - tree.import("E", 15, Change { effective: 50 }, &is_descendent_of).unwrap(); - assert!(!is_root); + tree.import("A0", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); + tree.import("A1", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); + tree.import("D", 10, Change { effective: 10 }, &is_descendent_of).unwrap(); + tree.import("E", 15, Change { effective: 50 }, &is_descendent_of).unwrap(); (tree, is_descendent_of) }; @@ -1261,25 +1416,20 @@ mod test { } #[test] - fn map_works() { - let (mut tree, _) = test_fork_tree(); + fn find_node_works() { + let (tree, is_descendent_of) = test_fork_tree(); - // Extend the single root fork-tree to also excercise the roots order during map. - let is_descendent_of = |_: &&str, _: &&str| -> Result { Ok(false) }; - let is_root = tree.import("A1", 1, (), &is_descendent_of).unwrap(); - assert!(is_root); - let is_root = tree.import("A2", 1, (), &is_descendent_of).unwrap(); - assert!(is_root); + let node = tree.find_node_where(&"D", &4, &is_descendent_of, &|_| true).unwrap().unwrap(); - let old_tree = tree.clone(); - let new_tree = tree.map(&mut |hash, _, _| hash.to_owned()); + assert_eq!(node.hash, "C"); + assert_eq!(node.number, 3); + } - // Check content and order - assert!(new_tree.iter().all(|(hash, _, data)| hash == data)); - assert_eq!( - old_tree.iter().map(|(hash, _, _)| *hash).collect::>(), - new_tree.iter().map(|(hash, _, _)| *hash).collect::>(), - ); + #[test] + fn map_works() { + let (tree, _is_descendent_of) = test_fork_tree(); + + let _tree = tree.map(&mut |_, _, _| ()); } #[test] @@ -1339,7 +1489,7 @@ mod test { } #[test] - fn rebalance_works() { + fn tree_rebalance() { let (mut tree, _) = test_fork_tree(); // the tree is automatically rebalanced on import, therefore we should iterate in preorder @@ -1353,7 +1503,7 @@ mod test { // let's add a block "P" which is a descendent of block "O" let is_descendent_of = |base: &&str, block: &&str| -> Result { match (*base, *block) { - (b, "P") => Ok(vec!["A", "F", "H", "L", "O"].into_iter().any(|n| n == b)), + (b, "P") => Ok(vec!["A", "F", "L", "O"].into_iter().any(|n| n == b)), _ => Ok(false), } }; @@ -1370,7 +1520,7 @@ mod test { } #[test] - fn drain_filter_works() { + fn tree_drain_filter() { let (mut tree, _) = test_fork_tree(); let filter = |h: &&str, _: &u64, _: &()| match *h { @@ -1389,92 +1539,7 @@ mod test { assert_eq!( removed.map(|(h, _, _)| h).collect::>(), - ["H", "L", "M", "O", "I", "J", "K"] + ["J", "K", "H", "L", "M", "O", "I"] ); } - - #[test] - fn find_node_index_works() { - let (tree, is_descendent_of) = test_fork_tree(); - - let path = tree - .find_node_index_where(&"D", &4, &is_descendent_of, &|_| true) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 0, 0]); - - let path = tree - .find_node_index_where(&"O", &5, &is_descendent_of, &|_| true) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 1, 0, 0]); - - let path = tree - .find_node_index_where(&"N", &6, &is_descendent_of, &|_| true) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 1, 0, 0, 0]); - } - - #[test] - fn find_node_index_with_predicate_works() { - fn is_descendent_of(parent: &char, child: &char) -> Result { - match *parent { - 'A' => Ok(['B', 'C', 'D', 'E', 'F'].contains(child)), - 'B' => Ok(['C', 'D'].contains(child)), - 'C' => Ok(['D'].contains(child)), - 'E' => Ok(['F'].contains(child)), - 'D' | 'F' => Ok(false), - _ => unreachable!(), - } - } - - // A(t) --- B(f) --- C(t) --- D(f) - // \-- E(t) --- F(f) - let mut tree: ForkTree = ForkTree::new(); - tree.import('A', 1, true, &is_descendent_of).unwrap(); - tree.import('B', 2, false, &is_descendent_of).unwrap(); - tree.import('C', 3, true, &is_descendent_of).unwrap(); - tree.import('D', 4, false, &is_descendent_of).unwrap(); - - tree.import('E', 2, true, &is_descendent_of).unwrap(); - tree.import('F', 3, false, &is_descendent_of).unwrap(); - - let path = tree - .find_node_index_where(&'D', &4, &is_descendent_of, &|&value| !value) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 0]); - - let path = tree - .find_node_index_where(&'D', &4, &is_descendent_of, &|&value| value) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 0, 0]); - - let path = tree - .find_node_index_where(&'F', &3, &is_descendent_of, &|&value| !value) - .unwrap(); - assert_eq!(path, None); - - let path = tree - .find_node_index_where(&'F', &3, &is_descendent_of, &|&value| value) - .unwrap() - .unwrap(); - assert_eq!(path, [0, 1]); - } - - #[test] - fn find_node_works() { - let (tree, is_descendent_of) = test_fork_tree(); - - let node = tree.find_node_where(&"D", &4, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("C", 3)); - - let node = tree.find_node_where(&"O", &5, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("L", 4)); - - let node = tree.find_node_where(&"N", &6, &is_descendent_of, &|_| true).unwrap().unwrap(); - assert_eq!((node.hash, node.number), ("M", 5)); - } }