diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index f3a3fc43..ceb6cfaf 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -18,7 +18,7 @@ use super::{ }; use hash_db::{HashDBRef, Hasher}; -use crate::{rstd::boxed::Box, MerkleValue, TrieDBBuilder}; +use crate::{rstd::boxed::Box, triedb::TrieDBDoubleEndedIterator, MerkleValue, TrieDBBuilder}; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. @@ -109,6 +109,22 @@ where trie: &'db TrieDB<'db, 'cache, L>, } +/// Double ended iterator over inserted pairs of key values. +pub struct FatDBDoubleEndedIterator<'db, 'cache, L> +where + L: TrieLayout, +{ + trie_iterator: TrieDBDoubleEndedIterator<'db, 'cache, L>, + trie: &'db TrieDB<'db, 'cache, L>, +} + +impl<'a, 'cache, L: TrieLayout> FatDBDoubleEndedIterator<'a, 'cache, L> { + /// Create a new double ended iterator. + pub fn new(db: &'a TrieDB<'a, 'cache, L>) -> Result, CError> { + Ok(Self { trie_iterator: TrieDBDoubleEndedIterator::new(db)?, trie: db }) + } +} + impl<'db, 'cache, L> FatDBIterator<'db, 'cache, L> where L: TrieLayout, @@ -148,6 +164,52 @@ where } } +impl<'db, 'cache, L> TrieIterator for FatDBDoubleEndedIterator<'db, 'cache, L> +where + L: TrieLayout, +{ + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + let hashed_key = L::Hash::hash(key); + self.trie_iterator.seek(hashed_key.as_ref()) + } +} + +impl<'db, 'cache, L> Iterator for FatDBDoubleEndedIterator<'db, 'cache, L> +where + L: TrieLayout, +{ + type Item = TrieItem, CError>; + + fn next(&mut self) -> Option { + self.trie_iterator.next().map(|res| { + res.map(|(hash, value)| { + let aux_hash = L::Hash::hash(&hash); + ( + self.trie.db().get(&aux_hash, Default::default()).expect("Missing fatdb hash"), + value, + ) + }) + }) + } +} + +impl<'db, 'cache, L> DoubleEndedIterator for FatDBDoubleEndedIterator<'db, 'cache, L> +where + L: TrieLayout, +{ + fn next_back(&mut self) -> Option { + self.trie_iterator.next_back().map(|res| { + res.map(|(hash, value)| { + let aux_hash = L::Hash::hash(&hash); + ( + self.trie.db().get(&aux_hash, Default::default()).expect("Missing fatdb hash"), + value, + ) + }) + }) + } +} + /// Iterator over inserted keys. pub struct FatDBKeyIterator<'db, 'cache, L> where diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index ca382b70..1d3319ed 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -17,7 +17,7 @@ use crate::{ nibble::{nibble_ops, NibbleSlice, NibbleVec}, node::{Node, NodeHandle, NodePlan, OwnedNode, Value}, triedb::TrieDB, - TrieError, TrieItem, TrieKeyItem, + TrieDoubleEndedIterator, TrieError, TrieItem, TrieKeyItem, }; use hash_db::{Hasher, Prefix, EMPTY_PREFIX}; @@ -30,6 +30,7 @@ enum Status { At, AtChild(usize), Exiting, + AftExiting, } #[cfg_attr(feature = "std", derive(Debug))] @@ -41,18 +42,28 @@ struct Crumb { } impl Crumb { - /// Move on to next status in the node's sequence. - fn increment(&mut self) { + /// Move on to the next status in the node's sequence in a direction. + fn step(&mut self, fwd: bool) { self.status = match (self.status, self.node.node_plan()) { (Status::Entering, NodePlan::Extension { .. }) => Status::At, (Status::Entering, NodePlan::Branch { .. }) | (Status::Entering, NodePlan::NibbledBranch { .. }) => Status::At, (Status::At, NodePlan::Branch { .. }) | - (Status::At, NodePlan::NibbledBranch { .. }) => Status::AtChild(0), + (Status::At, NodePlan::NibbledBranch { .. }) => + if fwd { + Status::AtChild(0) + } else { + Status::AtChild(nibble_ops::NIBBLE_LENGTH - 1) + }, (Status::AtChild(x), NodePlan::Branch { .. }) | (Status::AtChild(x), NodePlan::NibbledBranch { .. }) - if x < (nibble_ops::NIBBLE_LENGTH - 1) => + if fwd && x < (nibble_ops::NIBBLE_LENGTH - 1) => Status::AtChild(x + 1), + (Status::AtChild(x), NodePlan::Branch { .. }) | + (Status::AtChild(x), NodePlan::NibbledBranch { .. }) + if !fwd && x > 0 => + Status::AtChild(x - 1), + (Status::Exiting, _) => Status::AftExiting, _ => Status::Exiting, } } @@ -60,7 +71,9 @@ impl Crumb { /// Iterator for going through all nodes in the trie in pre-order traversal order. pub struct TrieDBRawIterator { + /// Forward trail of nodes to visit. trail: Vec>, + /// Forward iteration key nibbles of the current node. key_nibbles: NibbleVec, } @@ -80,6 +93,7 @@ impl TrieDBRawIterator { EMPTY_PREFIX, true, )?; + r.descend(root_node, root_hash); Ok(r) } @@ -87,7 +101,7 @@ impl TrieDBRawIterator { /// Create a new iterator, but limited to a given prefix. pub fn new_prefixed(db: &TrieDB, prefix: &[u8]) -> Result, CError> { let mut iter = TrieDBRawIterator::new(db)?; - iter.prefix(db, prefix)?; + iter.prefix(db, prefix, true)?; Ok(iter) } @@ -105,7 +119,7 @@ impl TrieDBRawIterator { Ok(iter) } - /// Descend into a payload. + /// Descend into a node. fn descend(&mut self, node: OwnedNode, node_hash: Option>) { self.trail .push(Crumb { hash: node_hash, status: Status::Entering, node: Arc::new(node) }); @@ -132,6 +146,7 @@ impl TrieDBRawIterator { &mut self, db: &TrieDB, key: &[u8], + fwd: bool, ) -> Result, CError> { self.trail.clear(); self.key_nibbles.clear(); @@ -149,7 +164,7 @@ impl TrieDBRawIterator { let (next_node, next_node_hash) = { self.descend(node, node_hash); let crumb = self.trail.last_mut().expect( - "descend_into_node pushes a crumb onto the trial; \ + "descend pushes a crumb onto the trail; \ thus the trail is non-empty; qed", ); let node_data = crumb.node.data(); @@ -157,21 +172,21 @@ impl TrieDBRawIterator { match crumb.node.node_plan() { NodePlan::Leaf { partial: partial_plan, .. } => { let slice = partial_plan.build(node_data); - if slice < partial { + if (fwd && slice < partial) || (!fwd && slice > partial) { crumb.status = Status::Exiting; - return Ok(false) + return Ok(false); } - return Ok(slice.starts_with(&partial)) + return Ok(slice.starts_with(&partial)); }, NodePlan::Extension { partial: partial_plan, child } => { let slice = partial_plan.build(node_data); if !partial.starts_with(&slice) { - if slice < partial { + if (fwd && slice < partial) || (!fwd && slice > partial) { crumb.status = Status::Exiting; self.key_nibbles.append_partial(slice.right()); - return Ok(false) + return Ok(false); } - return Ok(slice.starts_with(&partial)) + return Ok(slice.starts_with(&partial)); } full_key_nibbles += slice.len(); @@ -189,7 +204,7 @@ impl TrieDBRawIterator { }, NodePlan::Branch { value: _, children } => { if partial.is_empty() { - return Ok(true) + return Ok(true); } let i = partial.at(0); @@ -208,26 +223,26 @@ impl TrieDBRawIterator { true, )? } else { - return Ok(false) + return Ok(false); } }, NodePlan::NibbledBranch { partial: partial_plan, value: _, children } => { let slice = partial_plan.build(node_data); if !partial.starts_with(&slice) { - if slice < partial { + if (fwd && slice < partial) || (!fwd && slice > partial) { crumb.status = Status::Exiting; self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push((nibble_ops::NIBBLE_LENGTH - 1) as u8); - return Ok(false) + return Ok(false); } - return Ok(slice.starts_with(&partial)) + return Ok(slice.starts_with(&partial)); } full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); if partial.is_empty() { - return Ok(true) + return Ok(true); } let i = partial.at(0); @@ -247,15 +262,15 @@ impl TrieDBRawIterator { true, )? } else { - return Ok(false) + return Ok(false); } }, NodePlan::Empty => { if !partial.is_empty() { crumb.status = Status::Exiting; - return Ok(false) + return Ok(false); } - return Ok(true) + return Ok(true); }, } }; @@ -267,8 +282,13 @@ impl TrieDBRawIterator { /// Advance the iterator into a prefix, no value out of the prefix will be accessed /// or returned after this operation. - fn prefix(&mut self, db: &TrieDB, prefix: &[u8]) -> Result<(), TrieHash, CError> { - if self.seek(db, prefix)? { + fn prefix( + &mut self, + db: &TrieDB, + prefix: &[u8], + fwd: bool, + ) -> Result<(), TrieHash, CError> { + if self.seek(db, prefix, fwd)? { if let Some(v) = self.trail.pop() { self.trail.clear(); self.trail.push(v); @@ -290,31 +310,31 @@ impl TrieDBRawIterator { ) -> Result<(), TrieHash, CError> { if prefix.is_empty() { // There's no prefix, so just seek. - return self.seek(db, seek).map(|_| ()) + return self.seek(db, seek, true).map(|_| ()); } if seek.is_empty() || seek <= prefix { // Either we're not supposed to seek anywhere, // or we're supposed to seek *before* the prefix, // so just directly go to the prefix. - return self.prefix(db, prefix) + return self.prefix(db, prefix, true); } if !seek.starts_with(prefix) { // We're supposed to seek *after* the prefix, // so just return an empty iterator. self.trail.clear(); - return Ok(()) + return Ok(()); } - if !self.seek(db, prefix)? { + if !self.seek(db, prefix, true)? { // The database doesn't have a key with such a prefix. self.trail.clear(); - return Ok(()) + return Ok(()); } // Now seek forward again. - self.seek(db, seek)?; + self.seek(db, seek, true)?; let prefix_len = prefix.len() * crate::nibble::nibble_ops::NIBBLE_PER_BYTE; let mut len = 0; @@ -338,7 +358,7 @@ impl TrieDBRawIterator { } if len > prefix_len { self.trail = self.trail.split_off(i); - return Ok(()) + return Ok(()); } } @@ -349,9 +369,12 @@ impl TrieDBRawIterator { /// Fetches the next raw item. // /// Must be called with the same `db` as when the iterator was created. + /// + /// Specify `fwd` to indicate the direction of the iteration (`true` for forward). pub(crate) fn next_raw_item( &mut self, db: &TrieDB, + fwd: bool, ) -> Option< Result< (&NibbleVec, Option<&TrieHash>, &Arc>), @@ -364,11 +387,17 @@ impl TrieDBRawIterator { let node_data = crumb.node.data(); match (crumb.status, crumb.node.node_plan()) { - (Status::Entering, _) => { - // This is only necessary due to current borrow checker's limitation. - let crumb = self.trail.last_mut().expect("we've just fetched the last element using `last_mut` so this cannot fail; qed"); - crumb.increment(); - return Some(Ok((&self.key_nibbles, crumb.hash.as_ref(), &crumb.node))) + (Status::Entering, _) => + if fwd { + let crumb = self.trail.last_mut().expect("we've just fetched the last element using `last_mut` so this cannot fail; qed"); + crumb.step(fwd); + return Some(Ok((&self.key_nibbles, crumb.hash.as_ref(), &crumb.node))); + } else { + crumb.step(fwd); + }, + (Status::AftExiting, _) => { + self.trail.pop().expect("we've just fetched the last element using `last_mut` so this cannot fail; qed"); + self.trail.last_mut()?.step(fwd); }, (Status::Exiting, node) => { match node { @@ -383,8 +412,11 @@ impl TrieDBRawIterator { self.key_nibbles.drop_lasts(partial.len() + 1); }, } - self.trail.pop().expect("we've just fetched the last element using `last_mut` so this cannot fail; qed"); - self.trail.last_mut()?.increment(); + self.trail.last_mut()?.step(fwd); + if !fwd { + let crumb = self.trail.last_mut().expect("we've just fetched the last element using `last_mut` so this cannot fail; qed"); + return Some(Ok((&self.key_nibbles, crumb.hash.as_ref(), &crumb.node))); + } }, (Status::At, NodePlan::Extension { partial: partial_plan, child }) => { let partial = partial_plan.build(node_data); @@ -400,20 +432,28 @@ impl TrieDBRawIterator { self.descend(node, node_hash); }, Err(err) => { - crumb.increment(); - return Some(Err(err)) + crumb.step(fwd); + return Some(Err(err)); }, } }, (Status::At, NodePlan::Branch { .. }) => { - self.key_nibbles.push(0); - crumb.increment(); + self.key_nibbles.push(if fwd { + 0 + } else { + (nibble_ops::NIBBLE_LENGTH - 1) as u8 + }); + crumb.step(fwd); }, (Status::At, NodePlan::NibbledBranch { partial: partial_plan, .. }) => { let partial = partial_plan.build(node_data); self.key_nibbles.append_partial(partial.right()); - self.key_nibbles.push(0); - crumb.increment(); + self.key_nibbles.push(if fwd { + 0 + } else { + (nibble_ops::NIBBLE_LENGTH - 1) as u8 + }); + crumb.step(fwd); }, (Status::AtChild(i), NodePlan::Branch { children, .. }) | (Status::AtChild(i), NodePlan::NibbledBranch { children, .. }) => { @@ -431,16 +471,16 @@ impl TrieDBRawIterator { self.descend(node, node_hash); }, Err(err) => { - crumb.increment(); - return Some(Err(err)) + crumb.step(fwd); + return Some(Err(err)); }, } } else { - crumb.increment(); + crumb.step(fwd); } }, _ => panic!( - "Crumb::increment and TrieDBNodeIterator are implemented so that \ + "Crumb::step and TrieDBNodeIterator are implemented so that \ the above arms are the only possible states" ), } @@ -451,47 +491,54 @@ impl TrieDBRawIterator { /// /// Must be called with the same `db` as when the iterator was created. pub fn next_item(&mut self, db: &TrieDB) -> Option, CError>> { - while let Some(raw_item) = self.next_raw_item(db) { - let (prefix, _, node) = match raw_item { - Ok(raw_item) => raw_item, - Err(err) => return Some(Err(err)), + while let Some(raw_item) = self.next_raw_item(db, true) { + let (key, maybe_extra_nibble, value) = match Self::extract_key_from_raw_item(raw_item) { + Some(Ok(k)) => k, + Some(Err(err)) => return Some(Err(err)), + None => continue, }; - let mut prefix = prefix.clone(); - let value = match node.node() { - Node::Leaf(partial, value) => { - prefix.append_partial(partial.right()); - value - }, - Node::Branch(_, value) => match value { - Some(value) => value, - None => continue, - }, - Node::NibbledBranch(partial, _, value) => { - prefix.append_partial(partial.right()); - match value { - Some(value) => value, - None => continue, - } + if let Some(extra_nibble) = maybe_extra_nibble { + return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))); + } + + let value = match value { + Value::Node(hash) => match Self::fetch_value(db, &hash, (key.as_slice(), None)) { + Ok(value) => value, + Err(err) => return Some(Err(err)), }, - _ => continue, + Value::Inline(value) => value.to_vec(), + }; + + return Some(Ok((key, value))); + } + None + } + + /// Fetches the previous trie item. + /// + /// Must be called with the same `db` as when the iterator was created. + pub fn prev_item(&mut self, db: &TrieDB) -> Option, CError>> { + while let Some(raw_item) = self.next_raw_item(db, false) { + let (key, maybe_extra_nibble, value) = match Self::extract_key_from_raw_item(raw_item) { + Some(Ok(k)) => k, + Some(Err(err)) => return Some(Err(err)), + None => continue, }; - let (key_slice, maybe_extra_nibble) = prefix.as_prefix(); - let key = key_slice.to_vec(); if let Some(extra_nibble) = maybe_extra_nibble { - return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))) + return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))); } let value = match value { - Value::Node(hash) => match Self::fetch_value(db, &hash, (key_slice, None)) { + Value::Node(hash) => match Self::fetch_value(db, &hash, (key.as_slice(), None)) { Ok(value) => value, Err(err) => return Some(Err(err)), }, Value::Inline(value) => value.to_vec(), }; - return Some(Ok((key, value))) + return Some(Ok((key, value))); } None } @@ -500,43 +547,89 @@ impl TrieDBRawIterator { /// /// Must be called with the same `db` as when the iterator was created. pub fn next_key(&mut self, db: &TrieDB) -> Option, CError>> { - while let Some(raw_item) = self.next_raw_item(db) { - let (prefix, _, node) = match raw_item { - Ok(raw_item) => raw_item, - Err(err) => return Some(Err(err)), + while let Some(raw_item) = self.next_raw_item(db, true) { + let (key, maybe_extra_nibble, _) = match Self::extract_key_from_raw_item(raw_item) { + Some(Ok(k)) => k, + Some(Err(err)) => return Some(Err(err)), + None => continue, }; - let mut prefix = prefix.clone(); - match node.node() { - Node::Leaf(partial, _) => { - prefix.append_partial(partial.right()); - }, - Node::Branch(_, value) => - if value.is_none() { - continue - }, - Node::NibbledBranch(partial, _, value) => { - prefix.append_partial(partial.right()); - if value.is_none() { - continue - } - }, - _ => continue, + if let Some(extra_nibble) = maybe_extra_nibble { + return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))); + } + + return Some(Ok(key)); + } + None + } + + /// Fetches the previous key. + /// + /// Must be called with the same `db` as when the iterator was created. + pub fn prev_key(&mut self, db: &TrieDB) -> Option, CError>> { + while let Some(raw_item) = self.next_raw_item(db, false) { + let (key, maybe_extra_nibble, _) = match Self::extract_key_from_raw_item(raw_item) { + Some(Ok(k)) => k, + Some(Err(err)) => return Some(Err(err)), + None => continue, }; - let (key_slice, maybe_extra_nibble) = prefix.as_prefix(); - let key = key_slice.to_vec(); if let Some(extra_nibble) = maybe_extra_nibble { - return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))) + return Some(Err(Box::new(TrieError::ValueAtIncompleteKey(key, extra_nibble)))); } - return Some(Ok(key)) + return Some(Ok(key)); } None } + + /// Extracts the key from the result of a raw item retrieval. + /// + /// Given a raw item, it extracts the key information, including the key bytes, an optional + /// extra nibble (prefix padding), and the node value. + fn extract_key_from_raw_item<'a>( + raw_item: Result< + (&NibbleVec, Option<&TrieHash>, &'a Arc>), + TrieHash, + CError, + >, + ) -> Option, Option, Value<'a>), TrieHash, CError>> { + let (prefix, _, node) = match raw_item { + Ok(raw_item) => raw_item, + Err(err) => return Some(Err(err)), + }; + + let mut prefix = prefix.clone(); + let value = match node.node() { + Node::Leaf(partial, value) => { + prefix.append_partial(partial.right()); + value + }, + Node::Branch(_, value) => match value { + Some(value) => value, + None => return None, + }, + Node::NibbledBranch(partial, _, value) => { + prefix.append_partial(partial.right()); + match value { + Some(value) => value, + None => return None, + } + }, + _ => return None, + }; + + let (key_slice, maybe_extra_nibble) = prefix.as_prefix(); + + Some(Ok((key_slice.to_vec(), maybe_extra_nibble, value))) + } } /// Iterator for going through all nodes in the trie in pre-order traversal order. +/// +/// You can reduce the number of iterations and simultaneously iterate in both directions with two +/// cursors by using `TrieDBNodeDoubleEndedIterator`. You can convert this iterator into a double +/// ended iterator with `into_double_ended_iter`. pub struct TrieDBNodeIterator<'a, 'cache, L: TrieLayout> { db: &'a TrieDB<'a, 'cache, L>, raw_iter: TrieDBRawIterator, @@ -570,7 +663,7 @@ impl<'a, 'cache, L: TrieLayout> TrieDBNodeIterator<'a, 'cache, L> { /// Advance the iterator into a prefix, no value out of the prefix will be accessed /// or returned after this operation. pub fn prefix(&mut self, prefix: &[u8]) -> Result<(), TrieHash, CError> { - self.raw_iter.prefix(self.db, prefix) + self.raw_iter.prefix(self.db, prefix, true) } /// Advance the iterator into a prefix, no value out of the prefix will be accessed @@ -591,7 +684,7 @@ impl<'a, 'cache, L: TrieLayout> TrieDBNodeIterator<'a, 'cache, L> { impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, 'cache, L> { fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { - self.raw_iter.seek(self.db, key).map(|_| ()) + self.raw_iter.seek(self.db, key, true).map(|_| ()) } } @@ -600,7 +693,106 @@ impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, 'cache, L> { Result<(NibbleVec, Option>, Arc>), TrieHash, CError>; fn next(&mut self) -> Option { - self.raw_iter.next_raw_item(self.db).map(|result| { + self.raw_iter.next_raw_item(self.db, true).map(|result| { + result.map(|(nibble, hash, node)| (nibble.clone(), hash.cloned(), node.clone())) + }) + } +} + +/// Double ended iterator for going through all nodes in the trie in pre-order traversal order. +pub struct TrieDBNodeDoubleEndedIterator<'a, 'cache, L: TrieLayout> { + db: &'a TrieDB<'a, 'cache, L>, + raw_iter: TrieDBRawIterator, + back_raw_iter: TrieDBRawIterator, +} + +impl<'a, 'cache, L: TrieLayout> TrieDBNodeDoubleEndedIterator<'a, 'cache, L> { + /// Create a new double ended iterator. + pub fn new(db: &'a TrieDB<'a, 'cache, L>) -> Result, CError> { + Ok(Self { + db, + raw_iter: TrieDBRawIterator::new(db)?, + back_raw_iter: TrieDBRawIterator::new(db)?, + }) + } + + /// Restore an iterator from a raw iterators. + pub fn from_raw( + db: &'a TrieDB<'a, 'cache, L>, + raw_iter: TrieDBRawIterator, + back_raw_iter: TrieDBRawIterator, + ) -> Self { + Self { db, raw_iter, back_raw_iter } + } + + /// Convert the iterator to a raw forward iterator. + pub fn into_raw(self) -> TrieDBRawIterator { + self.raw_iter + } + + /// Convert the iterator to a raw backward iterator. + pub fn into_raw_back(self) -> TrieDBRawIterator { + self.back_raw_iter + } + + /// Fetch value by hash at a current node height + pub fn fetch_value( + &self, + key: &[u8], + prefix: Prefix, + ) -> Result, CError> { + TrieDBRawIterator::fetch_value(self.db, key, prefix) + } + + /// Advance the iterator into a prefix, no value out of the prefix will be accessed + /// or returned after this operation. + pub fn prefix(&mut self, prefix: &[u8]) -> Result<(), TrieHash, CError> { + self.raw_iter.prefix(self.db, prefix, true)?; + self.back_raw_iter.prefix(self.db, prefix, false) + } + + /// Advance the iterator into a prefix, no value out of the prefix will be accessed + /// or returned after this operation. + pub fn prefix_then_seek( + &mut self, + prefix: &[u8], + seek: &[u8], + ) -> Result<(), TrieHash, CError> { + self.raw_iter.prefix_then_seek(self.db, prefix, seek)?; + self.back_raw_iter.prefix_then_seek(self.db, prefix, seek) + } + + /// Access inner hash db. + pub fn db(&self) -> &dyn hash_db::HashDBRef { + self.db.db() + } +} + +impl TrieDoubleEndedIterator for TrieDBNodeDoubleEndedIterator<'_, '_, L> {} + +impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBNodeDoubleEndedIterator<'a, 'cache, L> { + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + self.raw_iter.seek(self.db, key, true).map(|_| ())?; + self.back_raw_iter.seek(self.db, key, false).map(|_| ()) + } +} + +impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBNodeDoubleEndedIterator<'a, 'cache, L> { + type Item = + Result<(NibbleVec, Option>, Arc>), TrieHash, CError>; + + fn next(&mut self) -> Option { + self.raw_iter.next_raw_item(self.db, true).map(|result| { + result.map(|(nibble, hash, node)| (nibble.clone(), hash.cloned(), node.clone())) + }) + } +} + +impl<'a, 'cache, L: TrieLayout> DoubleEndedIterator + for TrieDBNodeDoubleEndedIterator<'a, 'cache, L> +{ + fn next_back(&mut self) -> Option { + self.back_raw_iter.next_raw_item(self.db, false).map(|result| { result.map(|(nibble, hash, node)| (nibble.clone(), hash.cloned(), node.clone())) }) } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 5ddab42f..2c091eaa 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -25,7 +25,7 @@ mod rstd { collections::{BTreeMap, VecDeque}, convert, error::Error, - fmt, hash, iter, marker, mem, ops, rc, result, sync, vec, + fmt, hash, iter, marker, mem, ops, result, sync, vec, }; } @@ -46,6 +46,7 @@ use self::rstd::{fmt, Error}; use self::rstd::{boxed::Box, vec::Vec}; use hash_db::MaybeDebug; +pub use iterator::TrieDBNodeDoubleEndedIterator; use node::NodeOwned; pub mod node; @@ -358,6 +359,9 @@ pub trait TrieIterator: Iterator { fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError>; } +/// Extending the `TrieIterator` trait with `DoubleEndedIterator` trait. +pub trait TrieDoubleEndedIterator: TrieIterator + DoubleEndedIterator {} + /// Trie types #[derive(PartialEq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 9a1f51b3..22faf448 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -236,7 +236,7 @@ where // iteration of the loop below, the stack always has at least one entry and the bottom (front) // of the stack is the root node, which is not inline. Furthermore, the iterator is not empty, // so at least one iteration always occurs. - while let Some(item) = iter.next_raw_item(db) { + while let Some(item) = iter.next_raw_item(db, true) { match item { Ok((prefix, node_hash, node)) => { // Skip inline nodes, as they cannot contain hash references to other nodes by diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 310deff3..d027688c 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,13 +13,15 @@ // limitations under the License. use crate::{ - iterator::TrieDBRawIterator, + fatdb::FatDBDoubleEndedIterator, + iterator::{TrieDBNodeDoubleEndedIterator, TrieDBRawIterator}, lookup::Lookup, nibble::NibbleSlice, node::{decode_hash, NodeHandle, OwnedNode}, rstd::boxed::Box, - CError, DBValue, MerkleValue, Query, Result, Trie, TrieAccess, TrieCache, TrieError, TrieHash, - TrieItem, TrieIterator, TrieKeyItem, TrieLayout, TrieRecorder, + CError, DBValue, MerkleValue, Query, Result, Trie, TrieAccess, TrieCache, + TrieDoubleEndedIterator, TrieError, TrieHash, TrieItem, TrieIterator, TrieKeyItem, TrieLayout, + TrieRecorder, }; #[cfg(feature = "std")] use crate::{ @@ -136,6 +138,34 @@ where self.db } + /// Create `TrieDBDoubleEndedIterator` from `TrieDB`. + pub fn into_double_ended_iter( + &'db self, + ) -> Result, TrieHash, CError> { + TrieDBDoubleEndedIterator::new(&self) + } + + /// Create `TrieDBNodeDoubleEndedIterator` from `TrieDB`. + pub fn into_node_double_ended_iter( + &'db self, + ) -> Result, TrieHash, CError> { + TrieDBNodeDoubleEndedIterator::new(&self) + } + + /// create `TrieDBKeyDoubleEndedIterator` from `TrieDB`. + pub fn into_key_double_ended_iter( + &'db self, + ) -> Result, TrieHash, CError> { + TrieDBKeyDoubleEndedIterator::new(&self) + } + + /// create `FatDBDoubleEndedIterator` from `TrieDB`. + pub fn into_fat_double_ended_iter( + &'db self, + ) -> Result, TrieHash, CError> { + FatDBDoubleEndedIterator::new(&self) + } + /// Given some node-describing data `node`, and node key return the actual node RLP. /// This could be a simple identity operation in the case that the node is sufficiently small, /// but may require a database lookup. @@ -431,6 +461,45 @@ pub struct TrieDBKeyIterator<'a, 'cache, L: TrieLayout> { raw_iter: TrieDBRawIterator, } +/// Double ended iterator for going through all of key with values in the trie in pre-order +/// traversal order. +pub struct TrieDBKeyDoubleEndedIterator<'a, 'cache, L: TrieLayout> { + db: &'a TrieDB<'a, 'cache, L>, + raw_iter: TrieDBRawIterator, + back_raw_iter: TrieDBRawIterator, +} + +impl<'a, 'cache, L: TrieLayout> TrieDBKeyDoubleEndedIterator<'a, 'cache, L> { + /// Create a new double ended iterator. + pub fn new(db: &'a TrieDB<'a, 'cache, L>) -> Result, CError> { + Ok(Self { + db, + raw_iter: TrieDBRawIterator::new(db)?, + back_raw_iter: TrieDBRawIterator::new(db)?, + }) + } +} + +/// Double ended iterator for going through all values in the trie in pre-order traversal order. +pub struct TrieDBDoubleEndedIterator<'a, 'cache, L: TrieLayout> { + db: &'a TrieDB<'a, 'cache, L>, + raw_iter: TrieDBRawIterator, + back_raw_iter: TrieDBRawIterator, +} + +impl<'a, 'cache, L: TrieLayout> TrieDBDoubleEndedIterator<'a, 'cache, L> { + /// Create a new double ended iterator. + pub fn new(db: &'a TrieDB<'a, 'cache, L>) -> Result, CError> { + Ok(Self { + db, + raw_iter: TrieDBRawIterator::new(db)?, + back_raw_iter: TrieDBRawIterator::new(db)?, + }) + } +} + +impl TrieDoubleEndedIterator for TrieDBDoubleEndedIterator<'_, '_, L> {} + impl<'a, 'cache, L: TrieLayout> TrieDBIterator<'a, 'cache, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB<'a, 'cache, L>) -> Result, CError> { @@ -470,7 +539,36 @@ impl<'a, 'cache, L: TrieLayout> TrieDBIterator<'a, 'cache, L> { impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBIterator<'a, 'cache, L> { /// Position the iterator on the first element with key >= `key` fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { - self.raw_iter.seek(self.db, key).map(|_| ()) + self.raw_iter.seek(self.db, key, true).map(|_| ()) + } +} + +impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBIterator<'a, 'cache, L> { + type Item = TrieItem, CError>; + + fn next(&mut self) -> Option { + self.raw_iter.next_item(self.db) + } +} + +impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBDoubleEndedIterator<'a, 'cache, L> { + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + self.raw_iter.seek(self.db, key, true).map(|_| ())?; + self.back_raw_iter.seek(self.db, key, false).map(|_| ()) + } +} + +impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBDoubleEndedIterator<'a, 'cache, L> { + type Item = TrieItem, CError>; + + fn next(&mut self) -> Option { + self.raw_iter.next_item(self.db) + } +} + +impl<'a, 'cache, L: TrieLayout> DoubleEndedIterator for TrieDBDoubleEndedIterator<'a, 'cache, L> { + fn next_back(&mut self) -> Option { + self.back_raw_iter.prev_item(self.db) } } @@ -513,22 +611,38 @@ impl<'a, 'cache, L: TrieLayout> TrieDBKeyIterator<'a, 'cache, L> { impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBKeyIterator<'a, 'cache, L> { /// Position the iterator on the first element with key >= `key` fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { - self.raw_iter.seek(self.db, key).map(|_| ()) + self.raw_iter.seek(self.db, key, true).map(|_| ()) } } -impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBIterator<'a, 'cache, L> { - type Item = TrieItem, CError>; +impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBKeyIterator<'a, 'cache, L> { + type Item = TrieKeyItem, CError>; fn next(&mut self) -> Option { - self.raw_iter.next_item(self.db) + self.raw_iter.next_key(self.db) } } -impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBKeyIterator<'a, 'cache, L> { +impl<'a, 'cache, L: TrieLayout> TrieIterator for TrieDBKeyDoubleEndedIterator<'a, 'cache, L> { + /// Position the iterator on the first element with key >= `key` + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + self.raw_iter.seek(self.db, key, true).map(|_| ())?; + self.back_raw_iter.seek(self.db, key, false).map(|_| ()) + } +} + +impl<'a, 'cache, L: TrieLayout> Iterator for TrieDBKeyDoubleEndedIterator<'a, 'cache, L> { type Item = TrieKeyItem, CError>; fn next(&mut self) -> Option { self.raw_iter.next_key(self.db) } } + +impl<'a, 'cache, L: TrieLayout> DoubleEndedIterator + for TrieDBKeyDoubleEndedIterator<'a, 'cache, L> +{ + fn next_back(&mut self) -> Option { + self.back_raw_iter.prev_key(self.db) + } +} diff --git a/trie-db/test/src/double_ended_iterator.rs b/trie-db/test/src/double_ended_iterator.rs new file mode 100644 index 00000000..59c06700 --- /dev/null +++ b/trie-db/test/src/double_ended_iterator.rs @@ -0,0 +1,408 @@ +// Copyright 2017, 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use hash_db::Hasher; +use hex_literal::hex; +use reference_trie::test_layouts; +use trie_db::{ + node::Node, NibbleSlice, TrieDBBuilder, TrieDBNodeDoubleEndedIterator, TrieDoubleEndedIterator, + TrieLayout, +}; + +use crate::iterator::{build_trie_db, nibble_vec}; + +test_layouts!(node_double_ended_iterator_works, node_double_ended_iterator); +fn node_double_ended_iterator() { + let pairs = vec![ + (hex!("01").to_vec(), b"aaaa".to_vec()), + (hex!("0123").to_vec(), b"bbbb".to_vec()), + (hex!("02").to_vec(), vec![1; 32]), + ]; + + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + + if T::USE_EXTENSION { + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("02"), 2)); + match node.node() { + Node::Leaf(partial, _) => assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("00"), 1)); + match node.node() { + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::Extension(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + assert!(iter.next_back().is_none()); + } else { + let can_expand = + T::MAX_INLINE_VALUE.unwrap_or(T::Hash::LENGTH as u32) < T::Hash::LENGTH as u32; + + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("02"), 2)); + match node.node() { + Node::Leaf(partial, _) => assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + assert!(iter.next_back().is_none()); + } +} + +test_layouts!(seek_back_over_empty_works, seek_back_over_empty_works_internal); +fn seek_back_over_empty_works_internal() { + let (memdb, root) = build_trie_db::(&[]); + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + + >::seek(&mut iter, &hex!("")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::Empty => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + assert!(iter.next_back().is_none()); + + >::seek(&mut iter, &hex!("00")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::Empty => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } +} + +test_layouts!(seek_back_works, seek_back_works_internal); +fn seek_back_works_internal() { + let pairs = vec![ + (hex!("01").to_vec(), b"aaaa".to_vec()), + (hex!("0123").to_vec(), b"bbbb".to_vec()), + (hex!("0122").to_vec(), b"cccc".to_vec()), + (hex!("02").to_vec(), vec![1; 32]), + ]; + + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + + >::seek(&mut iter, &hex!("")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => assert_eq!(prefix, nibble_vec(hex!(""), 0)), + _ => panic!("unexpected item"), + } + + >::seek(&mut iter, &hex!("03")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => assert_eq!(prefix, nibble_vec(hex!("02"), 2)), + _ => panic!("unexpected item"), + } + + >::seek(&mut iter, &hex!("02")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => assert_eq!(prefix, nibble_vec(hex!("02"), 2)), + _ => panic!("unexpected item"), + } + + >::seek(&mut iter, &hex!("01")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0123"), 4)); + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0122"), 4)); + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + }, + _ => panic!("unexpected item"), + } + + >::seek(&mut iter, &hex!("0125")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0123"), 4)); + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0122"), 4)); + }, + _ => panic!("unexpected item"), + } + + >::seek(&mut iter, &hex!("0120")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, _, _))) => { + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + }, + _ => panic!("unexpected item"), + } +} + +test_layouts!(prefix_back_works, prefix_back_works_internal); +fn prefix_back_works_internal() { + let can_expand = T::MAX_INLINE_VALUE.unwrap_or(T::Hash::LENGTH as u32) < T::Hash::LENGTH as u32; + let pairs = vec![ + (hex!("01").to_vec(), b"aaaa".to_vec()), + (hex!("0123").to_vec(), b"bbbb".to_vec()), + (hex!("0122").to_vec(), b"cccc".to_vec()), + (hex!("02").to_vec(), vec![1; 32]), + ]; + + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + + iter.prefix(&hex!("01").to_vec()[..]).unwrap(); + + if T::USE_EXTENSION { + match iter.next_back() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("0123"), 4)); + match node.node() { + Node::Leaf(partial, _) => { + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)) + }, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + } else { + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("0123"), 4)); + match node.node() { + Node::Leaf(partial, _) => { + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)) + }, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + } + + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("0122"), 4)); + match node.node() { + Node::Leaf(partial, _) => { + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)) + }, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)), + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + match iter.next_back() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)), + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + assert!(iter.next_back().is_none()); + + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + iter.prefix(&hex!("0010").to_vec()[..]).unwrap(); + assert!(iter.next_back().is_none()); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + iter.prefix(&hex!("10").to_vec()[..]).unwrap(); + assert!(iter.next_back().is_none()); +} + +test_layouts!(prefix_over_empty_works, prefix_over_empty_works_internal); +fn prefix_over_empty_works_internal() { + let (memdb, root) = build_trie_db::(&[]); + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + iter.prefix(&hex!("")[..]).unwrap(); + match iter.next_back() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::Empty => {}, + _ => panic!("unexpected node"), + } + }, + _ => panic!("unexpected item"), + } + + assert!(iter.next_back().is_none()); + + let mut iter = TrieDBNodeDoubleEndedIterator::new(&trie).unwrap(); + iter.prefix(&hex!("00")[..]).unwrap(); + assert!(iter.next_back().is_none()); +} diff --git a/trie-db/test/src/iterator.rs b/trie-db/test/src/iterator.rs index e36ef6d0..961a5eb9 100644 --- a/trie-db/test/src/iterator.rs +++ b/trie-db/test/src/iterator.rs @@ -27,7 +27,7 @@ type MemoryDB = memory_db::MemoryDB< DBValue, >; -fn build_trie_db( +pub(crate) fn build_trie_db( pairs: &[(Vec, Vec)], ) -> (MemoryDB, ::Out) { let mut memdb = MemoryDB::::default(); @@ -41,7 +41,7 @@ fn build_trie_db( (memdb, root) } -fn nibble_vec>(bytes: T, len: usize) -> NibbleVec { +pub(crate) fn nibble_vec>(bytes: T, len: usize) -> NibbleVec { let slice = NibbleSlice::new(bytes.as_ref()); let mut v = NibbleVec::new(); diff --git a/trie-db/test/src/lib.rs b/trie-db/test/src/lib.rs index 7802247e..9f0eebab 100644 --- a/trie-db/test/src/lib.rs +++ b/trie-db/test/src/lib.rs @@ -14,6 +14,8 @@ //! Tests for trie-db crate. +#[cfg(test)] +mod double_ended_iterator; #[cfg(test)] mod fatdb; #[cfg(test)] diff --git a/trie-db/test/src/triedb.rs b/trie-db/test/src/triedb.rs index 5f2fa699..6589d99d 100644 --- a/trie-db/test/src/triedb.rs +++ b/trie-db/test/src/triedb.rs @@ -94,6 +94,41 @@ fn iterator_seek_works_internal() { ); } +test_layouts!(double_ended_iterator, double_ended_iterator_internal); +fn double_ended_iterator_internal() { + let pairs = vec![ + (hex!("01").to_vec(), hex!("01").to_vec()), + (hex!("02").to_vec(), hex!("02").to_vec()), + (hex!("03").to_vec(), hex!("03").to_vec()), + (hex!("10").to_vec(), hex!("10").to_vec()), + (hex!("11").to_vec(), hex!("11").to_vec()), + ]; + + let mut memdb = MemoryDB::<::Hash, PrefixedKey<_>, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = TrieDBMutBuilder::::new(&mut memdb, &mut root).build(); + for (x, y) in &pairs { + t.insert(x, y).unwrap(); + } + } + + let t = TrieDBBuilder::::new(&memdb, &root).build(); + assert_eq!(pairs, t.iter().unwrap().map(|x| x.unwrap()).collect::>()); + + let mut iter = t.into_double_ended_iter().unwrap(); + + for i in 0..pairs.len() { + assert_eq!(iter.next().unwrap().unwrap(), pairs[i].clone()); + } + assert!(iter.next().is_none()); + + for i in (0..pairs.len()).rev() { + assert_eq!(iter.next_back().unwrap().unwrap(), pairs[i].clone()); + } + assert!(iter.next_back().is_none()); +} + test_layouts!(iterator, iterator_internal); fn iterator_internal() { let d = vec![b"A".to_vec(), b"AA".to_vec(), b"AB".to_vec(), b"B".to_vec()]; diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index e184cfbb..b827abb6 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -23,11 +23,7 @@ extern crate alloc; #[cfg(feature = "std")] mod rstd { - pub use std::{ - cmp, - collections::{BTreeMap, VecDeque}, - vec::Vec, - }; + pub use std::{cmp, collections::BTreeMap, vec::Vec}; } #[cfg(not(feature = "std"))]