Seekable iterator

This commit is contained in:
arkpar 2016-11-24 11:44:24 +01:00
parent a7037f8e5b
commit 8b3cdc513b
5 changed files with 114 additions and 9 deletions

2
Cargo.lock generated
View File

@ -224,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "elastic-array"
version = "0.6.0"
source = "git+https://github.com/ethcore/elastic-array#70e4012e691b732c7c4cb04e9232799e6aa268bc"
source = "git+https://github.com/ethcore/elastic-array#346f1ba5982576dab9d0b8fa178b50e1db0a21cd"
dependencies = [
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -17,7 +17,7 @@
use hash::H256;
use sha3::Hashable;
use hashdb::{HashDB, DBValue};
use super::{TrieDB, Trie, TrieDBIterator, TrieItem, Recorder};
use super::{TrieDB, Trie, TrieDBIterator, TrieItem, Recorder, TrieIterator};
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval.
@ -46,7 +46,7 @@ impl<'db> FatDB<'db> {
}
impl<'db> Trie for FatDB<'db> {
fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> {
FatDBIterator::new(&self.raw).map(|iter| Box::new(iter) as Box<_>)
}
@ -81,6 +81,12 @@ impl<'db> FatDBIterator<'db> {
}
}
impl<'db> TrieIterator for FatDBIterator<'db> {
fn seek(&mut self, key: &[u8]) -> super::Result<()> {
self.trie_iterator.seek(&key.sha3())
}
}
impl<'db> Iterator for FatDBIterator<'db> {
type Item = TrieItem<'db>;

View File

@ -102,7 +102,7 @@ pub trait Trie {
where 'a: 'b, R: Recorder;
/// Returns an iterator over elements of trie.
fn iter<'a>(&'a self) -> Result<Box<Iterator<Item = TrieItem> + 'a>>;
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<Item = TrieItem> + 'a>>;
}
/// A key-value datastore implemented as a database-backed modified Merkle tree.
@ -130,6 +130,12 @@ pub trait TrieMut {
fn remove(&mut self, key: &[u8]) -> Result<()>;
}
/// A trie iterator that also supports random access.
pub trait TrieIterator : Iterator {
/// Position the iterator on the first element with key >= `key`
fn seek(&mut self, key: &[u8]) -> Result<()>;
}
/// Trie types
#[derive(Debug, PartialEq, Clone)]
pub enum TrieSpec {
@ -193,7 +199,7 @@ impl<'db> Trie for TrieKinds<'db> {
wrapper!(self, get_recorded, key, r)
}
fn iter<'a>(&'a self) -> Result<Box<Iterator<Item = TrieItem> + 'a>> {
fn iter<'a>(&'a self) -> Result<Box<TrieIterator<Item = TrieItem> + 'a>> {
wrapper!(self, iter,)
}
}

View File

@ -18,7 +18,7 @@ use hash::H256;
use sha3::Hashable;
use hashdb::{HashDB, DBValue};
use super::triedb::TrieDB;
use super::{Trie, TrieItem, Recorder};
use super::{Trie, TrieItem, Recorder, TrieIterator};
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
///
@ -49,7 +49,7 @@ impl<'db> SecTrieDB<'db> {
}
impl<'db> Trie for SecTrieDB<'db> {
fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> {
TrieDB::iter(&self.raw)
}

View File

@ -20,7 +20,7 @@ use nibbleslice::*;
use rlp::*;
use super::node::Node;
use super::recorder::{Recorder, NoOp};
use super::{Trie, TrieItem, TrieError};
use super::{Trie, TrieItem, TrieError, TrieIterator};
/// A `Trie` implementation using a generic `HashDB` backing database.
///
@ -295,6 +295,61 @@ impl<'a> TrieDBIterator<'a> {
Ok(r)
}
/// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no
/// value exists for the key.
///
/// Note: Not a public API; use Trie trait functions.
fn seek_recurse<'key> (
&mut self,
node: &[u8],
key: &NibbleSlice<'key>,
d: u32
) -> super::Result<()> {
let n = Node::decoded(node);
match Node::decoded(node) {
Node::Leaf(_, ref item) => {
self.descend(node);
Ok(())
},
Node::Extension(ref slice, ref item) => {
let slice = &NibbleSlice::from_encoded(slice).0;
if key.starts_with(slice) {
let mut r = NoOp;
self.trail.push(Crumb {
status: Status::At,
node: n,
});
self.key_nibbles.extend(slice.iter());
let data = try!(self.db.get_raw_or_lookup(&*item, &mut r, d));
self.seek_recurse(&data, &key.mid(slice.len()), d + 1)
} else {
self.descend(node);
Ok(())
}
},
Node::Branch(ref nodes, _) => match key.is_empty() {
true => {
self.trail.push(Crumb {
status: Status::Entering,
node: n,
});
Ok(())
},
false => {
let mut r = NoOp;
let node = try!(self.db.get_raw_or_lookup(&*nodes[key.at(0) as usize], &mut r, d));
self.trail.push(Crumb {
status: Status::AtChild(key.at(0) as usize),
node: n,
});
self.key_nibbles.push(key.at(0));
self.seek_recurse(&node, &key.mid(1), d + 1)
}
},
_ => Ok(())
}
}
/// Descend into a payload.
fn descend(&mut self, d: &[u8]) -> super::Result<()> {
self.trail.push(Crumb {
@ -316,6 +371,17 @@ impl<'a> TrieDBIterator<'a> {
}
}
impl<'a> TrieIterator for TrieDBIterator<'a> {
/// Position the iterator on the first element with key >= `key`
fn seek(&mut self, key: &[u8]) -> super::Result<()> {
self.trail.clear();
self.key_nibbles.clear();
let mut r = NoOp;
let root_rlp = try!(self.db.root_data(&mut r));
self.seek_recurse(&root_rlp, &NibbleSlice::new(key), 1)
}
}
impl<'a> Iterator for TrieDBIterator<'a> {
type Item = TrieItem<'a>;
@ -372,7 +438,7 @@ impl<'a> Iterator for TrieDBIterator<'a> {
}
impl<'db> Trie for TrieDB<'db> {
fn iter<'a>(&'a self) -> super::Result<Box<Iterator<Item = TrieItem> + 'a>> {
fn iter<'a>(&'a self) -> super::Result<Box<TrieIterator<Item = TrieItem> + 'a>> {
TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>)
}
@ -415,3 +481,30 @@ fn iterator() {
assert_eq!(d.iter().map(|i| i.clone().to_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>());
assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::<Vec<_>>());
}
#[test]
fn iterator_seek() {
use memorydb::*;
use super::TrieMut;
use super::triedbmut::*;
let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ];
let mut memdb = MemoryDB::new();
let mut root = H256::new();
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d {
t.insert(x, x).unwrap();
}
}
let t = TrieDB::new(&memdb, &root).unwrap();
let mut iter = t.iter().unwrap();
//assert_eq!(iter.next(), Some(Ok((b"A".to_vec(), DBValue::from_slice(b"A")))));
//iter.seek(b"!").unwrap();
//assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
let mut iter = t.iter().unwrap();
iter.seek(b"A").unwrap();
assert_eq!(d, iter.map(|x| x.unwrap().1).collect::<Vec<_>>());
}