From 776f324464241c7d2f1fc3a340547380c99c4dd0 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 15 Feb 2019 21:49:19 +0100 Subject: [PATCH 001/120] fuse commit of no extension branch. --- test-support/reference-trie/src/lib.rs | 395 ++++++++++- trie-db/Cargo.toml | 2 + trie-db/benches/bench.rs | 132 +++- trie-db/fuzz/Cargo.toml | 54 ++ trie-db/fuzz/afl_targets/trie_root_new.rs | 14 + trie-db/fuzz/fuzz_targets/no_ext_insert.rs | 9 + .../fuzz/fuzz_targets/no_ext_insert_rem.rs | 9 + trie-db/fuzz/fuzz_targets/trie_root.rs | 9 + .../fuzz/fuzz_targets/trie_root_fix_len.rs | 9 + trie-db/fuzz/fuzz_targets/trie_root_new.rs | 10 + .../fuzz/hongfuzz_targets/trie_root_new.rs | 12 + trie-db/fuzz/src/lib.rs | 163 +++++ trie-db/src/iter_build.rs | 646 ++++++++++++++++++ trie-db/src/lib.rs | 4 +- trie-db/src/lookup.rs | 18 +- trie-db/src/node.rs | 5 + trie-db/src/node_codec.rs | 13 +- trie-db/src/triedb.rs | 183 ++++- trie-db/src/triedbmut.rs | 573 +++++++++++++++- trie-db/testset0 | Bin 0 -> 5120 bytes trie-db/testset1 | Bin 0 -> 10240 bytes trie-db/testset2 | Bin 0 -> 102400 bytes trie-db/testset3 | Bin 0 -> 204800 bytes 23 files changed, 2193 insertions(+), 67 deletions(-) create mode 100644 trie-db/fuzz/Cargo.toml create mode 100644 trie-db/fuzz/afl_targets/trie_root_new.rs create mode 100644 trie-db/fuzz/fuzz_targets/no_ext_insert.rs create mode 100644 trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs create mode 100644 trie-db/fuzz/fuzz_targets/trie_root.rs create mode 100644 trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs create mode 100644 trie-db/fuzz/fuzz_targets/trie_root_new.rs create mode 100644 trie-db/fuzz/hongfuzz_targets/trie_root_new.rs create mode 100644 trie-db/fuzz/src/lib.rs create mode 100644 trie-db/src/iter_build.rs create mode 100644 trie-db/testset0 create mode 100644 trie-db/testset1 create mode 100644 trie-db/testset2 create mode 100644 trie-db/testset3 diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 9415a41f..050847db 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -25,14 +25,26 @@ use std::error::Error as StdError; use std::iter::once; use codec::{Decode, Input, Output, Encode, Compact}; use trie_root::Hasher; -use trie_db::{node::Node, triedbmut::ChildReference, DBValue}; +use trie_db::{ + node::Node, + triedbmut::ChildReference, + DBValue, + trie_visit, + trie_visit_no_ext, + ProcessEncodedNode, + TrieBuilder, + TrieRoot, +}; +use std::borrow::Borrow; use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record}; pub use trie_root::TrieStream; pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; +pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodecNoExt>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; +pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMutNoExt<'a, KeccakHasher, ReferenceNodeCodecNoExt>; pub type RefFatDB<'a> = trie_db::FatDB<'a, KeccakHasher, ReferenceNodeCodec>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, KeccakHasher, ReferenceNodeCodec>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; @@ -47,6 +59,16 @@ pub fn ref_trie_root(input: I) -> ::Out where trie_root::trie_root::(input) } +fn ref_trie_root_unhashed(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::unhashed_trie::(input) +} + + + const EMPTY_TRIE: u8 = 0; const LEAF_NODE_OFFSET: u8 = 1; const EXTENSION_NODE_OFFSET: u8 = 128; @@ -170,6 +192,10 @@ impl Decode for NodeHeader { #[derive(Default, Clone)] pub struct ReferenceNodeCodec; +/// Simple reference implementation of a `NodeCodec`. +#[derive(Default, Clone)] +pub struct ReferenceNodeCodecNoExt; + #[derive(Debug, PartialEq, Eq, Clone)] /// Error concerning the Parity-Codec based decoder. pub enum ReferenceError { @@ -291,23 +317,23 @@ impl NodeCodec for ReferenceNodeCodec { output } - fn branch_node(children: I, maybe_value: Option) -> Vec where - I: IntoIterator::Out>>> + Iterator::Out>>> - { + fn branch_node( + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>) -> Vec { let mut output = vec![0, 0, 0]; let have_value = if let Some(value) = maybe_value { - (&*value).encode_to(&mut output); + value.encode_to(&mut output); true } else { false }; - let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child { + let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true } - Some(ChildReference::Inline(inline_data, len)) => { - (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output); + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data.as_ref()[..len].encode_to(&mut output); true } None => false, @@ -315,4 +341,357 @@ impl NodeCodec for ReferenceNodeCodec { output[0..3].copy_from_slice(&prefix[..]); output } + + fn branch_node_nibbled( + _partial: &[u8], + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!() + } + } + +impl NodeCodec for ReferenceNodeCodecNoExt { + type Error = ReferenceError; + + fn hashed_null_node() -> ::Out { + ReferenceNodeCodec::hashed_null_node() + } + + fn decode(data: &[u8]) -> ::std::result::Result { + let input = &mut &*data; + match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { + NodeHeader::Null => Ok(Node::Empty), + NodeHeader::Branch(has_value) => { + let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; + let nibble_count = take(input, 1).ok_or(ReferenceError::BadFormat)?[0] as usize; + let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let value = if has_value { + let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + Some(take(input, count).ok_or(ReferenceError::BadFormat)?) + } else { + None + }; + let mut children = [None; 16]; + let mut pot_cursor = 1; + for i in 0..16 { + if bitmap & pot_cursor != 0 { + let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); + } + pot_cursor <<= 1; + } + Ok(Node::NibbledBranch(nibble_slice, children, value)) + } + NodeHeader::Extension(_) => unreachable!(), + NodeHeader::Leaf(nibble_count) => { + let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) + } + } + } + + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + ReferenceNodeCodec::try_decode_hash(data) + } + + fn is_empty_node(data: &[u8]) -> bool { + ReferenceNodeCodec::is_empty_node(data) + } + + fn empty_node() -> Vec { + ReferenceNodeCodec::empty_node() + } + + fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { + ReferenceNodeCodec::leaf_node(partial, value) + } + + fn ext_node(_partial: &[u8], _child: ChildReference<::Out>) -> Vec { + unreachable!() + } + + fn branch_node( + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!() + } + + fn branch_node_nibbled( + partial: &[u8], + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>) -> Vec { + let mut output = Vec::with_capacity(partial.len() + 4); // TODO choose a good capacity estimation value (here it is only partial) + for _ in 0..3 { + output.push(0); + } + + let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; + output.push(nibble_count as u8); + if nibble_count % 2 == 1 { + output.push(partial[0] & 0x0f); + } + output.extend_from_slice(&partial[1..]); + + let have_value = if let Some(value) = maybe_value { + value.encode_to(&mut output); + true + } else { + false + }; + let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child.borrow() { + Some(ChildReference::Hash(h)) => { + h.as_ref().encode_to(&mut output); + true + } + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data[..len].encode_to(&mut output); + true + } + None => false, + })); + output[0..3].copy_from_slice(&prefix[..]); + output + } + +} + +pub fn compare_impl( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + } + t.root().clone() + }; + if root != root_new { + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDB::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDB::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} + +pub fn compare_root( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = TrieRoot::::default(); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + } + t.root().clone() + }; + + assert_eq!(root, root_new); +} + +pub fn compare_unhashed( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = trie_db::TrieRootUnhashed::::default(); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = ref_trie_root_unhashed(data); + + assert_eq!(root, root_new); +} + + +pub fn calc_root( + data: I, +) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + let mut cb = TrieRoot::::default(); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) +} + +pub fn calc_root_no_ext( + data: I, +) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + let mut cb = TrieRoot::::default(); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) +} + +pub fn compare_impl_no_ext( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + } + t.root().clone() + }; + if root != root_new { + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} + +pub fn compare_impl_no_ext_unordered( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + let mut b_map = std::collections::btree_map::BTreeMap::new(); + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + b_map.insert(data[i].0.clone(),data[i].1.clone()); + } + t.root().clone() + }; + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + + if root != root_new { + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} +/* +pub fn compare_impl_no_ext_unordered_rem( + data: Vec<(Vec,Vec)>, + rem: &[(usize, usize)], + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + let mut b_map = std::collections::btree_map::BTreeMap::new(); + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut rem_ix = 0; + let mut has_rem = true; + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + b_map.insert(data[i].0.clone(),data[i].1.clone()); + if has_rem && i == rem[rem_ix].1 { + t.remove(&data[rem[rem_ix].0].0[..]).unwrap(); + b_map.remove(&data[rem[rem_ix].0].0[..]); + rem_ix += 1; + if rem_ix == rem.len() { has_rem = false } + } + } + t.root().clone() + }; + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + + if root != root_new { + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +}*/ diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index 856f2be6..e3f9a9b2 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -22,6 +22,8 @@ keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.11.0" } reference-trie = { path = "../test-support/reference-trie", version = "0.11.0" } hex-literal = "0.1" criterion = "0.2.8" +parity-codec = "3.0" +parity-codec-derive = "3.0" [[bench]] name = "bench" diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index fc419b77..080e7812 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -14,13 +14,17 @@ #[macro_use] extern crate criterion; -use criterion::{Criterion, black_box}; -criterion_group!(benches, nibble_common_prefix); +use criterion::{Criterion, black_box, Bencher}; +criterion_group!(benches, + nibble_common_prefix, + root_old, + root_new, +); criterion_main!(benches); extern crate trie_standardmap; extern crate trie_db; - +use std::io::Read; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; use trie_db::NibbleSlice; @@ -32,13 +36,119 @@ fn nibble_common_prefix(b: &mut Criterion) { value_mode: ValueMode::Mirror, count: 255, }; - let (keys, values): (Vec<_>, Vec<_>) = st.make().iter().cloned().unzip(); - let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { - (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) - }).collect(); - b.bench_function("nibble_common_prefix", |b| b.iter(&mut ||{ - for (left, right) in mixed.iter() { - let _ = black_box(left.common_prefix(&right)); + let (keys, values): (Vec<_>, Vec<_>) = st.make().into_iter().unzip(); + b.bench_function("nibble_common_prefix", move |b| { + let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { + (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) + }).collect(); + + b.iter(&mut ||{ + for (left, right) in mixed.iter() { + let _ = black_box(left.common_prefix(&right)); + } + }) + }); +} + +fn root_old(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input("./testset0"), // 286 vals + input("./testset1"), // 571 vals + input("./testset2"), // 5649 vals + input("./testset3"), // 11376 vals + ]; + + c.bench_function_over_inputs("root_old",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)); + + reference_trie::ref_trie_root(inputc); + }) + ,data); +} + + +fn root_new(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input("./testset0"), + input("./testset1"), + input("./testset2"), + input("./testset3"), + ]; + + c.bench_function_over_inputs("root_new",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + +fn fuzz_to_data(fp: &std::path::Path) -> Vec<(Vec,Vec)> { + let mut file = std::fs::File::open(fp).unwrap(); + let mut input = Vec::new(); + file.read_to_end(&mut input).unwrap(); + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let mut minkeylen = if let Some(v) = input.get(0) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + let mut maxkeylen = if let Some(v) = input.get(1) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + + if maxkeylen < minkeylen { + let v = minkeylen; + minkeylen = maxkeylen; + maxkeylen = v; } - })); + let mut ix = 2; + loop { + let keylen = if let Some(v) = input.get(ix) { + let mut v = *v & 31u8; + v = v + 1; + v = std::cmp::max(minkeylen, v); + v = std::cmp::min(maxkeylen, v); + v as usize + } else { break }; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix + 2].to_vec() + } else { break }; + ix += 2; + result.push((key,val)); + } + result +} + +fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { + let mut m = std::collections::BTreeMap::new(); + for (k,v) in input.into_iter() { + let _ = m.insert(k,v); // latest value for uniqueness + } + m.into_iter().collect() +} + +fn input(file: &str) -> Vec<(Vec,Vec)> { + let pb = std::path::PathBuf::from(file); + let data = data_sorted_unique(fuzz_to_data(&pb)); + data } diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml new file mode 100644 index 00000000..e95d917b --- /dev/null +++ b/trie-db/fuzz/Cargo.toml @@ -0,0 +1,54 @@ + +[package] +name = "trie-db-fuzz" +version = "0.0.1" +authors = [] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +memory-db = { path = "../../memory-db", version = "0.11.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.11.0" } +keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.11.0" } +afl = "0.4.3" +honggfuzz = "0.5" + +[dependencies.trie-db] +path = ".." +[dependencies.libfuzzer-sys] +git = "https://github.com/rust-fuzz/libfuzzer-sys.git" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "trie_root_new" +path = "fuzz_targets/trie_root_new.rs" + +[[bin]] +name = "trie_root" +path = "fuzz_targets/trie_root.rs" + +[[bin]] +name = "trie_root_fix_len" +path = "fuzz_targets/trie_root_fix_len.rs" + +[[bin]] +name = "no_ext_insert" +path = "fuzz_targets/no_ext_insert.rs" + +[[bin]] +name = "no_ext_insert_rem" +path = "fuzz_targets/no_ext_insert_rem.rs" + +[[bin]] +name = "afl_trie_root_new" +path = "afl_targets/trie_root_new.rs" + +[[bin]] +name = "hfuzz_trie_root_new" +path = "hongfuzz_targets/trie_root_new.rs" diff --git a/trie-db/fuzz/afl_targets/trie_root_new.rs b/trie-db/fuzz/afl_targets/trie_root_new.rs new file mode 100644 index 00000000..e6cea8a4 --- /dev/null +++ b/trie-db/fuzz/afl_targets/trie_root_new.rs @@ -0,0 +1,14 @@ + +// TODO this is currently borked behind linker sanitizer issues (unordered without gold or multiple +// definition with it). + +use afl::fuzz; + +use trie_db_fuzz::fuzz_that_compare_impl; + +fn main() { + fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_compare_impl(data); + }); +} diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs new file mode 100644 index 00000000..24237eb4 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs @@ -0,0 +1,9 @@ +#![no_main] + +use trie_db_fuzz::fuzz_that_no_ext_insert; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_no_ext_insert(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs new file mode 100644 index 00000000..387db148 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs @@ -0,0 +1,9 @@ +#![no_main] + +use trie_db_fuzz::fuzz_that_no_ext_insert_remove; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_no_ext_insert_remove(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs new file mode 100644 index 00000000..bb698909 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -0,0 +1,9 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_ref_trie_root; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + fuzz_that_ref_trie_root(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs new file mode 100644 index 00000000..4a75f81a --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -0,0 +1,9 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_ref_trie_root_fix_len; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + fuzz_that_ref_trie_root_fix_len(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_new.rs b/trie-db/fuzz/fuzz_targets/trie_root_new.rs new file mode 100644 index 00000000..281e2f7f --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root_new.rs @@ -0,0 +1,10 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_compare_impl; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_compare_impl(data); +}); diff --git a/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs new file mode 100644 index 00000000..78e327db --- /dev/null +++ b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs @@ -0,0 +1,12 @@ + +use honggfuzz::fuzz; +use trie_db_fuzz::fuzz_that_compare_impl; + +fn main() { + loop { + fuzz!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_compare_impl(data); + }); + } +} diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs new file mode 100644 index 00000000..4b352451 --- /dev/null +++ b/trie-db/fuzz/src/lib.rs @@ -0,0 +1,163 @@ + + +use memory_db::MemoryDB; +use reference_trie::{ + RefTrieDBMutNoExt, + RefTrieDBMut, + ref_trie_root, + calc_root_no_ext, +}; +use trie_db::{TrieMut, DBValue}; +use keccak_hasher::KeccakHasher; + + + +fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let mut minkeylen = if let Some(v) = input.get(0) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + let mut maxkeylen = if let Some(v) = input.get(1) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + + if maxkeylen < minkeylen { + let v = minkeylen; + minkeylen = maxkeylen; + maxkeylen = v; + } + let mut ix = 2; + loop { + let keylen = if let Some(v) = input.get(ix) { + let mut v = *v & 31u8; + v = v + 1; + v = std::cmp::max(minkeylen, v); + v = std::cmp::min(maxkeylen, v); + v as usize + } else { break }; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix+2].to_vec() + } else { break }; + result.push((key,val)); + } + result +} + + +pub fn fuzz_that_ref_trie_root(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data(input)); + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + assert_eq!(*t.root(), ref_trie_root(data)); +} + +pub fn fuzz_that_ref_trie_root_fix_len(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data_fix_len(input)); + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + assert_eq!(*t.root(), ref_trie_root(data)); +} + +fn fuzz_to_data_fix_len(input: &[u8]) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + let mut ix = 0; + loop { + let keylen = 32; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix+2].to_vec() + } else { break }; + result.push((key,val)); + } + result +} + + +fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { + let mut m = std::collections::BTreeMap::new(); + for (k,v) in input.into_iter() { + let _ = m.insert(k,v); // latest value for uniqueness + } + m.into_iter().collect() +} + + +pub fn fuzz_that_compare_impl(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data(input)); + let memdb = MemoryDB::default(); + let hashdb = MemoryDB::::default(); + reference_trie::compare_impl(data, memdb, hashdb); +} + +pub fn fuzz_that_no_ext_insert(input: &[u8]) { + let data = fuzz_to_data(input); + //println!("data{:?}", data); + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. + let data = data_sorted_unique(fuzz_to_data(input)); + //println!("data{:?}", data); + assert_eq!(*t.root(), calc_root_no_ext(data)); +} + +pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { + let data = fuzz_to_data(input); + let mut data2 = std::collections::BTreeMap::new(); + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut torem = None; +// println!("data{:?}", data); + for a in 0..data.len() { + if a % 7 == 6 { +// println!("remrand{:?}", a); + // a random removal some time + t.remove(&data[a].0[..]).unwrap(); + data2.remove(&data[a].0[..]); + } else { + if a % 5 == 0 { +// println!("rem{:?}", a); + torem = Some(data[a].0.to_vec()); + } + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + data2.insert(&data[a].0[..], &data[a].1[..]); + if a % 5 == 4 { + if let Some(v) = torem.take() { +// println!("remdoneaft {:?}", a); + t.remove(&v[..]); + data2.remove(&v[..]); + } + } + } + } + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. + //println!("data{:?}", data); + assert_eq!(*t.root(), calc_root_no_ext(data2)); +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs new file mode 100644 index 00000000..5930c497 --- /dev/null +++ b/trie-db/src/iter_build.rs @@ -0,0 +1,646 @@ +// Copyright 2017, 2019 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. + +//! Alternative tools for working with key value iterator without recursion. + +use elastic_array::ElasticArray36; +use hash_db::{Hasher, HashDB}; +use std::marker::PhantomData; +use crate::triedbmut::{ChildReference}; +use crate::nibbleslice::NibbleSlice; +use node_codec::NodeCodec; + + +fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { + // sorted assertion preventing out of bound + for a in 0..v1.len() { + if v1[a] == v2[a] { + } else { + if (v1[a] >> 4) == (v2[a] >> 4) { + return a * 2 + 1; + } else { + return a * 2; + } + } + } + return v1.len() * 2; +} + +// warn! start at 0 // TODO change biggest_depth?? +// warn! slow don't loop on that when possible +#[inline(always)] +fn nibble_at(v1: &[u8], ix: usize) -> u8 { + if ix % 2 == 0 { + v1[ix/2] >> 4 + } else { + v1[ix/2] & 15 + } +} + +/* +// TODO remove for nibbleslice api TODO can be variable size +fn encoded_nibble(ori: &[u8], is_leaf: bool) -> ElasticArray36 { + let l = ori.len(); + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 {0x10 + ori[0]} else {0} + if is_leaf {0x20} else {0}); + while i < l { + r.push(ori[i] * 16 + ori[i+1]); + i += 2; + } + r +} +*/ + +type CacheNode = Option>; + +// (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) +// TODO test others layout +// first usize to get nb of added value, second usize last added index +// second str is in branch value +struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE], bool, Option)>,PhantomData<(H,C)>); + +#[inline(always)] +fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { + [ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ] +} + +/// initially allocated cache +const INITIAL_DEPTH: usize = 10; +const NIBBLE_SIZE: usize = 16; +impl CacheAccum +where + H: Hasher, + C: NodeCodec, + V: AsRef<[u8]>, + { + + fn new() -> Self { + let mut v = Vec::with_capacity(INITIAL_DEPTH); + (0..INITIAL_DEPTH).for_each(|_|v.push((new_vec_slice_buff(), false, None))); + CacheAccum(v, PhantomData) + } + + #[inline(always)] + fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode) { + if depth >= self.0.len() { + for _i in self.0.len()..depth + 1 { + self.0.push((new_vec_slice_buff(), false, None)); + } + } + self.0[depth].0[nibble_ix] = node; + self.0[depth].1 = true; + } + + #[inline(always)] + fn touched(&self, depth:usize) -> bool { + self.0[depth].1 + } + + #[inline(always)] + fn reset_depth(&mut self, depth:usize) { + self.0[depth].1 = false; + for i in 0..NIBBLE_SIZE { + self.0[depth].0[i] = None; + } + } + + fn flush_val ( + &mut self, + cb_ext: &mut impl ProcessEncodedNode<::Out>, + target_depth: usize, + (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), + ) { + let nibble_value = nibble_at(&k2.as_ref()[..], target_depth); + // is it a branch value (two candidate same ix) + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],target_depth+1).encoded(true); + // Note: fwiu, having fixed key size, all values are in leaf (no value in + // branch). TODO run metrics on a node to count branch with values + let encoded = C::leaf_node(&nkey.as_ref()[..], &v2.as_ref()[..]); + let hash = cb_ext.process(encoded, false); + + // insert hash in branch (first level branch only at this point) + self.set_node(target_depth, nibble_value as usize, Some(hash)); + } + + fn flush_branch( + &mut self, + no_ext: bool, + cb_ext: &mut impl ProcessEncodedNode<::Out>, + ref_branch: impl AsRef<[u8]> + Ord, + new_depth: usize, + old_depth: usize, + is_last: bool, + ) { + let mut last_branch_ix = None; + for d in (new_depth..=old_depth).rev() { + + let touched = self.touched(d); + + if touched || d == new_depth { + if let Some(branch_d) = last_branch_ix.take() { + + let last_root = d == 0 && is_last; + // reduce slice for branch + let parent_branch = touched; + // TODO change this offset to not use nibble slice api (makes it hard to get the index + // thing) + let (slice_size, offset) = if parent_branch && last_root { + // corner branch last + (branch_d - d - 1, d + 1) + } else if last_root { + // corner case non branch last + (branch_d - d, d) + } else { + (branch_d - d - 1, d + 1) + }; + + let nkey = if slice_size > 0 { + Some(NibbleSlice::new_offset(&ref_branch.as_ref()[..],offset) + .encoded_leftmost(slice_size, false)) + } else { + None + }; + + let is_root = d == 0 && is_last && !parent_branch; + let h = if no_ext { + // enc branch + self.alt_no_ext(cb_ext, branch_d, is_root, nkey) + } else { + self.standard_ext(cb_ext, branch_d, is_root, nkey) + }; + // put hash in parent + let nibble: u8 = nibble_at(&ref_branch.as_ref()[..],d); + self.set_node(d, nibble as usize, Some(h)); + } + } + + + if d > new_depth || is_last { + if touched { + last_branch_ix = Some(d); + } + } + + } + if let Some(d) = last_branch_ix { + if no_ext { + self.alt_no_ext(cb_ext, d, true, None); + } else { + self.standard_ext(cb_ext, d, true, None); + } + } + } + + #[inline(always)] + fn standard_ext( + &mut self, + cb_ext: &mut impl ProcessEncodedNode<::Out>, + branch_d: usize, + is_root: bool, + nkey: Option>, + ) -> ChildReference<::Out> { + // enc branch + let v = self.0[branch_d].2.take(); + let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); + self.reset_depth(branch_d); + let branch_hash = cb_ext.process(encoded, is_root && nkey.is_none()); + + if let Some(nkey) = nkey { + let encoded = C::ext_node(&nkey[..], branch_hash); + let h = cb_ext.process(encoded, is_root); + h + } else { + branch_hash + } + } + + #[inline(always)] + fn alt_no_ext( + &mut self, + cb_ext: &mut impl ProcessEncodedNode<::Out>, + branch_d: usize, + is_root: bool, + nkey: Option>, + ) -> ChildReference<::Out> { + // enc branch + let v = self.0[branch_d].2.take(); + let encoded = C::branch_node_nibbled( + // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); + nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), + self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); + + self.reset_depth(branch_d); + cb_ext.process(encoded, is_root) + } + +} + +pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + C: NodeCodec, + F: ProcessEncodedNode<::Out>, + { + trie_visit_inner::(input, cb_ext, true) + } + +pub fn trie_visit(input: I, cb_ext: &mut F) + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + C: NodeCodec, + F: ProcessEncodedNode<::Out>, + { + trie_visit_inner::(input, cb_ext, false) + } + +// put no_ext as a trait: probably not worth it (fn designed for that)? +fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + C: NodeCodec, + F: ProcessEncodedNode<::Out>, + { + let mut depth_queue = CacheAccum::::new(); + // compare iter ordering + let mut iter_input = input.into_iter(); + if let Some(mut prev_val) = iter_input.next() { + //println!("!st{:?},{:?}",&prev_val.0.as_ref(),&prev_val.1.as_ref()); + // depth of last item TODO rename to last_depth + let mut prev_depth = 0; + + for (k, v) in iter_input { + //println!("!{:?},{:?}",&k.as_ref(),&v.as_ref()); + let common_depth = biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); + // 0 is a reserved value : could use option + let depth_item = common_depth; + if common_depth == prev_val.0.as_ref().len() * 2 { + //println!("stack {} ", common_depth); + // the new key include the previous one : branch value case + // just stored value at branch depth + depth_queue.0[common_depth].2 = Some(prev_val.1); + depth_queue.0[common_depth].1 = true; + } else if depth_item >= prev_depth { + //println!("fv {}", depth_item); + // put prev with next (common branch prev val can be flush) + depth_queue.flush_val(cb_ext, depth_item, &prev_val); + } else if depth_item < prev_depth { + //println!("fbv {}", prev_depth); + // do not put with next, previous is last of a branch + depth_queue.flush_val(cb_ext, prev_depth, &prev_val); + let ref_branches = prev_val.0; + //println!("fb {} {}", depth_item, prev_depth); + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, prev_depth, false); // TODO flush at prev flush depth instead ?? + } + + prev_val = (k, v); + prev_depth = depth_item; + } + // last pendings + if prev_depth == 0 + && !depth_queue.touched(0) { + // one single element corner case + let (k2, v2) = prev_val; + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],prev_depth).encoded(true); + let encoded = C::leaf_node(&nkey.as_ref()[..], &v2.as_ref()[..]); + cb_ext.process(encoded, true); + } else { + //println!("fbvl {}", prev_depth); + depth_queue.flush_val(cb_ext, prev_depth, &prev_val); + let ref_branches = prev_val.0; + //println!("fbl {} {}", 0, prev_depth); + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, prev_depth, true); + } + } else { + // nothing null root corner case + cb_ext.process(C::empty_node(), true); + } +} + +pub trait ProcessEncodedNode { + fn process(&mut self, Vec, bool) -> ChildReference; +} + +/// Get trie root and insert node in hash db on parsing. +/// As for all `ProcessEncodedNode` implementation, it +/// is only for full trie parsing (not existing trie). +pub struct TrieBuilder<'a, H, HO, V, DB> { + pub db: &'a mut DB, + pub root: Option, + _ph: PhantomData<(H,V)>, +} + +impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { + pub fn new(db: &'a mut DB) -> Self { + TrieBuilder { db, root: None, _ph: PhantomData } + } +} + +impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { + fn process(&mut self, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + let len = enc_ext.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + + return ChildReference::Inline(h, len); + } + let hash = self.db.insert(&enc_ext[..]); + if is_root { + //println!("isroot touch"); + self.root = Some(hash.clone()); + }; + ChildReference::Hash(hash) + } +} + +/// Get trie root hash on parsing +pub struct TrieRoot { + pub root: Option, + _ph: PhantomData<(H)>, +} + +impl Default for TrieRoot { + fn default() -> Self { + TrieRoot { root: None, _ph: PhantomData } + } +} + +impl ProcessEncodedNode<::Out> for TrieRoot::Out> { + fn process(&mut self, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + let len = enc_ext.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + + return ChildReference::Inline(h, len); + } + let hash = ::hash(&enc_ext[..]); + if is_root { + self.root = Some(hash.clone()); + }; + ChildReference::Hash(hash) + } +} + +/// Get trie root hash on parsing +/// -> this seems to match current implementation +/// of trie_root but I think it should return the +/// full stream of trie (which would not be doable +/// with current `ProcessEncodedNode` definition +/// but can be doable by switching to something +/// similar to `TrieStream` (initially the trait +/// was a simple FnMut but it make sense to move +/// to something more refined). +pub struct TrieRootUnhashed { + pub root: Option>, + _ph: PhantomData<(H)>, +} + +impl Default for TrieRootUnhashed { + fn default() -> Self { + TrieRootUnhashed { root: None, _ph: PhantomData } + } +} + +impl ProcessEncodedNode<::Out> for TrieRootUnhashed { + fn process(&mut self, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + let len = enc_ext.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + + return ChildReference::Inline(h, len); + } + let hash = ::hash(&enc_ext[..]); + if is_root { + self.root = Some(enc_ext); + }; + ChildReference::Hash(hash) + } +} + + + +#[cfg(test)] +mod test { + use super::*; + use env_logger; + use standardmap::*; + use DBValue; + use memory_db::MemoryDB; + use hash_db::{Hasher, HashDB}; + use keccak_hasher::KeccakHasher; + use reference_trie::{RefTrieDBMut, RefTrieDB, Trie, TrieMut, + ReferenceNodeCodec, ref_trie_root}; + + #[test] + fn trie_root_empty () { + compare_impl(vec![]) + } + + #[test] + fn trie_one_node () { + compare_impl(vec![ + (vec![1u8,2u8,3u8,4u8],vec![7u8]), + ]); + } + + #[test] + fn root_extension_one () { + compare_impl(vec![ + (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + ]); + } + + fn compare_impl(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + let hashdb = MemoryDB::::default(); + reference_trie::compare_impl(data, memdb, hashdb); + } + fn compare_impl_no_ext(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + let hashdb = MemoryDB::::default(); + reference_trie::compare_impl_no_ext(data, memdb, hashdb); + } + fn compare_impl_no_ext_unordered(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + let hashdb = MemoryDB::::default(); + reference_trie::compare_impl_no_ext_unordered(data, memdb, hashdb); + } +/* fn compare_impl_no_ext_unordered_rem(data: Vec<(Vec,Vec)>, rem: &[(usize,usize)]) { + let memdb = MemoryDB::default(); + let hashdb = MemoryDB::::default(); + reference_trie::compare_impl_no_ext_unordered_rem(data, rem, memdb, hashdb); + }*/ + + + + fn compare_root(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + reference_trie::compare_root(data, memdb); + } + fn compare_unhashed(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + reference_trie::compare_unhashed(data, memdb); + } + + + + #[test] + fn trie_middle_node1 () { + compare_impl(vec![ + (vec![1u8,2u8],vec![8u8;32]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + ]); + } + + #[test] + fn trie_middle_node2 () { + compare_impl(vec![ + (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;32]), + (vec![1u8,2u8],vec![8u8;32]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + (vec![1u8,2u8,3u8,5u8],vec![7u8;32]), + (vec![1u8,2u8,3u8,5u8,3u8],vec![7u8;32]), + ]); + } + #[test] + fn root_extension_bis () { + compare_root(vec![ + (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + ]); + } + #[test] + fn root_extension_tierce () { + compare_unhashed(vec![ + (vec![1u8,2u8,3u8,3u8],vec![8u8;2]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), + ]); + } + #[test] + fn root_extension_tierce_big () { + // on more content unhashed would hash + compare_unhashed(vec![ + (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + (vec![1u8,6u8,3u8,3u8],vec![8u8;32]), + (vec![6u8,2u8,3u8,3u8],vec![8u8;32]), + (vec![6u8,2u8,3u8,13u8],vec![8u8;32]), + ]); + } + + + #[test] + fn trie_middle_node2x () { + compare_impl(vec![ + (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;2]), + (vec![1u8,2u8],vec![8u8;2]), + (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), + (vec![1u8,2u8,3u8,5u8],vec![7u8;2]), + (vec![1u8,2u8,3u8,5u8,3u8],vec![7u8;2]), + ]); + } + #[test] + fn fuzz1 () { + compare_impl(vec![ + (vec![01u8],vec![42u8,9]), + (vec![01u8,0u8],vec![0u8,0]), + (vec![255u8,2u8],vec![1u8,0]), + ]); + } + #[test] + fn fuzz2 () { + compare_impl(vec![ + (vec![0,01u8],vec![42u8,9]), + (vec![0,01u8,0u8],vec![0u8,0]), + (vec![0,255u8,2u8],vec![1u8,0]), + ]); + } + #[test] + fn fuzz3 () { + compare_impl(vec![ + (vec![0],vec![196, 255]), + /* (vec![48],vec![138, 255]), + (vec![67],vec![0, 0]), + (vec![128],vec![255, 0]), */ + (vec![247],vec![0, 196]), + (vec![255],vec![0, 0]), + ]); + } + #[test] + fn fuzz_noext1 () { + compare_impl_no_ext(vec![ + (vec![0],vec![128, 0]), + (vec![128],vec![0, 0]), + ]); + } + #[test] + fn fuzz_noext2 () { + compare_impl_no_ext(vec![ + (vec![0],vec![6, 255]), + (vec![6],vec![255, 186]), + (vec![255],vec![186, 255]), + ]); + } + #[test] + fn fuzz_noext2_bis () { + compare_impl_no_ext(vec![ + (vec![0xaa], vec![0xa0]), + (vec![0xaa, 0xaa], vec![0xaa]), + (vec![0xaa, 0xbb], vec![0xab]), + (vec![0xbb], vec![0xb0]), + (vec![0xbb, 0xbb], vec![0xbb]), + (vec![0xbb, 0xcc], vec![0xbc]), + ]); + } + + #[test] + fn fuzz_noext3 () { + compare_impl_no_ext_unordered(vec![ + (vec![11,252],vec![11, 0]), + (vec![11,0],vec![0, 0]), + (vec![0],vec![0, 0]), + ]); + } +/* #[test] + fn fuzz_noextrem1 () { + compare_impl_no_ext_unordered_rem(vec![ + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + (vec![0],vec![0, 0]), + ], &[(0,4)]); + }*/ + +} diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 203f27bc..9b18d3e5 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -51,10 +51,11 @@ mod lookup; mod nibblevec; mod nibbleslice; mod node_codec; +mod iter_build; pub use hash_db::{HashDB, HashDBRef, Hasher}; pub use self::triedb::{TrieDB, TrieDBIterator}; -pub use self::triedbmut::{TrieDBMut, ChildReference}; +pub use self::triedbmut::{TrieDBMutNoExt, TrieDBMut, ChildReference}; pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedb::SecTrieDB; pub use self::fatdb::{FatDB, FatDBIterator}; @@ -63,6 +64,7 @@ pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibbleslice::NibbleSlice; pub use node_codec::NodeCodec; +pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub type DBValue = elastic_array::ElasticArray128; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index cc6657e2..3f999e0a 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -90,7 +90,23 @@ where None => return Ok(None) } }, - _ => return Ok(None), + Node::NibbledBranch(slice, children, value) => { + if !key.starts_with(&slice) { + return Ok(None) + } + + match key.len() == slice.len() { + true => return Ok(value.map(move |val| self.query.decode(val))), + false => match children[key.at(slice.len()) as usize] { + Some(x) => { + node_data = x; + key = key.mid(slice.len() + 1); + } + None => return Ok(None) + } + } + }, + Node::Empty => return Ok(None), } // check if new node data is inline or hash. diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 5db0017a..3b591513 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -31,6 +31,8 @@ pub enum Node<'a> { Extension(NibbleSlice<'a>, &'a [u8]), /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), + /// Branch node with support for a nibble (to avoid extension node) + NibbledBranch(NibbleSlice<'a>, [Option<&'a [u8]>; 16], Option<&'a [u8]>), } /// A Sparse (non mutable) owned vector struct to hold branch keys and value @@ -97,6 +99,8 @@ pub enum OwnedNode { Extension(NibbleVec, DBValue), /// Branch node: 16 children and an optional value. Branch(Branch), + /// Branch node: 16 children and an optional value. + NibbledBranch(NibbleVec, Branch), } impl<'a> From> for OwnedNode { @@ -106,6 +110,7 @@ impl<'a> From> for OwnedNode { Node::Leaf(k, v) => OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), Node::Extension(k, child) => OwnedNode::Extension(k.into(), DBValue::from_slice(child)), Node::Branch(c, val) => OwnedNode::Branch(Branch::new(c, val)), + Node::NibbledBranch(k, c, val) => OwnedNode::NibbledBranch(k.into(), Branch::new(c, val)), } } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 264c0a88..4d94ade1 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -18,10 +18,11 @@ use hash_db::Hasher; use node::Node; use ChildReference; - -use elastic_array::{ElasticArray128}; +use std::borrow::Borrow; /// Trait for trie node encoding/decoding +/// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> +/// avoid Vec by all means. pub trait NodeCodec: Sized { /// Codec error type type Error: ::std::error::Error; @@ -32,7 +33,7 @@ pub trait NodeCodec: Sized { /// Decode bytes to a `Node`. Returns `Self::E` on failure. fn decode(data: &[u8]) -> Result; - /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. + /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. fn try_decode_hash(data: &[u8]) -> Option; /// Check if the provided bytes correspond to the codecs "empty" node. @@ -48,6 +49,8 @@ pub trait NodeCodec: Sized { fn ext_node(partial: &[u8], child_ref: ChildReference) -> Vec; /// Returns an encoded branch node. Takes an iterator yielding `ChildReference` and an optional value - fn branch_node(children: I, value: Option>) -> Vec - where I: IntoIterator>> + Iterator>>; + fn branch_node(children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; + + /// Returns an encoded branch node with a possible partial path. + fn branch_node_nibbled(partial: &[u8], children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 81a14bcd..13e081b8 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -151,15 +151,15 @@ where match C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, } .field("slice", &slice) .field("value", &value) .finish(), - Ok(Node::Extension(ref slice, ref item)) => + Ok(Node::Extension(slice, item)) => match (f.debug_struct("Node::Extension"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, } .field("slice", &slice) @@ -179,11 +179,27 @@ where .field("value", &value) .finish() }, + Ok(Node::NibbledBranch(slice, nodes, value)) => { + let nodes: Vec> = nodes.into_iter() + .enumerate() + .filter_map(|(i, n)| n.map(|n| (i, n))) + .map(|(i, n)| TrieAwareDebugNode { trie: self.trie, index: Some(i as u8), key: n, is_root: false }) + .collect(); + + match (f.debug_struct("Node::NibbledBranch"), self.index) { + (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, _) => d, + } + .field("slice", &slice) + .field("nodes", &nodes) + .field("value", &value) + .finish() + }, Ok(Node::Empty) => f.debug_struct("Node::Empty").finish(), Err(e) => f.debug_struct("BROKEN_NODE") .field("index", &self.index) - .field("key", &self.key) + .field("key", &self.key) // [128, 225, 183, 218, 100, 173, 146, 231, 107, 158, 188, 21], .field("error", &format!("ERROR decoding node branch Rlp: {}", e)) .finish() } @@ -236,8 +252,11 @@ impl Crumb { self.status = match (&self.status, &self.node) { (_, &OwnedNode::Empty) => Status::Exiting, (&Status::Entering, _) => Status::At, - (&Status::At, &OwnedNode::Branch(_)) => Status::AtChild(0), - (&Status::AtChild(x), &OwnedNode::Branch(_)) if x < 15 => Status::AtChild(x + 1), + (&Status::At, &OwnedNode::Branch(_)) + | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), + (&Status::AtChild(x), &OwnedNode::Branch(_)) + | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) + if x < 15 => Status::AtChild(x + 1), _ => Status::Exiting, } } @@ -317,7 +336,36 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { } } }, - _ => return Ok(()), + Node::NibbledBranch(ref slice, ref nodes, _) => { + if !key.starts_with(slice) { + self.descend(&node_data)?; + return Ok(()) + } + if key.len() == slice.len() { + self.trail.push(Crumb { + status: Status::Entering, + node: node.clone().into(), + }); + return Ok(()) + } else { + + let i = key.at(slice.len()); + self.trail.push(Crumb { + status: Status::AtChild(i as usize), + node: node.clone().into(), + }); + self.key_nibbles.extend(slice.iter()); + self.key_nibbles.push(i); + if let Some(ref child) = nodes[i as usize] { + let child = self.db.get_raw_or_lookup(&*child, false)?; + (child, slice.len() + 1) + } else { + return Ok(()) + } + } + + }, + Node::Empty => return Ok(()), } }; @@ -337,7 +385,10 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { fn descend_into_node(&mut self, node: OwnedNode) { self.trail.push(Crumb { status: Status::Entering, node }); match &self.trail.last().expect("just pushed item; qed").node { - &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) => { + &OwnedNode::Leaf(ref n, _) + | &OwnedNode::Extension(ref n, _) + | &OwnedNode::NibbledBranch(ref n, _) + => { self.key_nibbles.extend((0..n.len()).map(|i| n.at(i))); }, _ => {} @@ -391,11 +442,16 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { self.key_nibbles.truncate(l - n.len()); }, OwnedNode::Branch(_) => { self.key_nibbles.pop(); }, - _ => {} + OwnedNode::NibbledBranch(ref n,_) => { + let l = self.key_nibbles.len(); + self.key_nibbles.truncate(l - n.len() - 1); + }, + OwnedNode::Empty => {}, } IterStep::PopTrail }, - (Status::At, &OwnedNode::Branch(ref branch)) if branch.has_value() => { + (Status::At, &OwnedNode::Branch(ref branch)) + | (Status::At, &OwnedNode::NibbledBranch(_, ref branch)) if branch.has_value() => { let value = branch.get_value().expect("already checked `has_value`"); return Some(Ok((self.key(), DBValue::from_slice(value)))); }, @@ -405,8 +461,11 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { (Status::At, &OwnedNode::Extension(_, ref d)) => { IterStep::Descend::(self.db.get_raw_or_lookup(&*d, false)) }, - (Status::At, &OwnedNode::Branch(_)) => IterStep::Continue, - (Status::AtChild(i), &OwnedNode::Branch(ref branch)) if branch.index(i).is_some() => { + (Status::At, &OwnedNode::Branch(_)) + | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, + (Status::AtChild(i), &OwnedNode::Branch(ref branch)) + | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, ref branch)) + if branch.index(i).is_some() => { match i { 0 => self.key_nibbles.push(0), i => *self.key_nibbles.last_mut() @@ -414,7 +473,8 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { } IterStep::Descend::(self.db.get_raw_or_lookup(&branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), false)) }, - (Status::AtChild(i), &OwnedNode::Branch(_)) => { + (Status::AtChild(i), &OwnedNode::Branch(_)) + | (Status::AtChild(i), &OwnedNode::NibbledBranch(_,_)) => { if i == 0 { self.key_nibbles.push(0); } @@ -447,6 +507,7 @@ mod tests { use keccak_hasher::KeccakHasher; use DBValue; use reference_trie::{RefTrieDB, RefTrieDBMut, RefLookup, Trie, TrieMut, NibbleSlice}; + use reference_trie::{RefTrieDBNoExt, RefTrieDBMutNoExt}; #[test] fn iterator_works() { @@ -475,6 +536,34 @@ mod tests { assert_eq!(pairs, iter_pairs); } + #[test] + fn iterator_works_no_ext() { + let pairs = vec![ + (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), + (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), + ]; + + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for (x, y) in &pairs { + t.insert(x, y).unwrap(); + } + } + + let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + + let iter = trie.iter().unwrap(); + let mut iter_pairs = Vec::new(); + for pair in iter { + let (key, value) = pair.unwrap(); + iter_pairs.push((key, value.to_vec())); + } + + assert_eq!(pairs, iter_pairs); + } + #[test] fn iterator_seek_works() { @@ -503,6 +592,34 @@ mod tests { assert_eq!(&pairs[1..], &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..]); } + #[test] + fn iterator_seek_works_no_ext() { + let pairs = vec![ + (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), + (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), + ]; + + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for (x, y) in &pairs { + t.insert(x, y).unwrap(); + } + } + + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + + let mut iter = t.iter().unwrap(); + assert_eq!(iter.next().unwrap().unwrap(), (hex!("0103000000000000000464").to_vec(), DBValue::from_slice(&hex!("fffffffffe")[..]))); + iter.seek(&hex!("00")[..]).unwrap(); + assert_eq!(pairs, iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()); + let mut iter = t.iter().unwrap(); + iter.seek(&hex!("0103000000000000000465")[..]).unwrap(); + assert_eq!(&pairs[1..], &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..]); + } + + #[test] fn iterator() { let d = vec![DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B")]; @@ -521,6 +638,25 @@ mod tests { assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } + #[test] + fn iterator_no_ext() { + 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::::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for x in &d { + t.insert(x, x).unwrap(); + } + } + + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); + assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); + } + + #[test] fn iterator_seek() { let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; @@ -528,13 +664,13 @@ mod tests { let mut memdb = MemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), DBValue::from_slice(b"A"))); iter.seek(b"!").unwrap(); @@ -578,6 +714,23 @@ mod tests { assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); } + #[test] + fn get_len_no_ext() { + let mut memdb = MemoryDB::::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + t.insert(b"A", b"ABC").unwrap(); + t.insert(b"B", b"ABCBA").unwrap(); + } + + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); + assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5)); + assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); + } + + #[test] fn debug_output_supports_pretty_print() { let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index c02b0bb9..a81776cb 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -71,7 +71,9 @@ enum Node { /// The child node is always a branch. Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option) + Branch(Box<[Option>; 16]>, Option), + /// Branch node with support for a nibble (to avoid extension node) + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } impl Node @@ -97,33 +99,42 @@ where } // decode a node from encoded bytes without getting its children. - fn from_encoded(data: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self + fn from_encoded<'a, 'b, C, H>(data: &'a[u8], db: &HashDB, storage: &'b mut NodeStorage) -> Self where C: NodeCodec, H: Hasher, { - match C::decode(data).expect("encoded bytes read from db; qed") { - EncodedNode::Empty => Node::Empty, - EncodedNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)), - EncodedNode::Extension(key, cb) => { - Node::Extension( - key.encoded(false), - Self::inline_or_hash::(cb, db, storage)) - } - EncodedNode::Branch(ref encoded_children, val) => { - let mut child = |i:usize| { + let dec_children = |encoded_children: &[Option<&'a [u8]>; 16], storage: &'b mut NodeStorage| { + let mut child = |i:usize| { encoded_children[i].map(|data| Self::inline_or_hash::(data, db, storage) ) }; - let children = Box::new([ + Box::new([ child(0), child(1), child(2), child(3), child(4), child(5), child(6), child(7), child(8), child(9), child(10), child(11), child(12), child(13), child(14), child(15), - ]); + ]) + }; - Node::Branch(children, val.map(DBValue::from_slice)) - } + match C::decode(data).expect("encoded bytes read from db; qed") { + EncodedNode::Empty => Node::Empty, + EncodedNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)), + EncodedNode::Extension(key, cb) => { + Node::Extension( + key.encoded(false), + Self::inline_or_hash::(cb, db, storage)) + }, + EncodedNode::Branch(encoded_children, val) => { + let children = dec_children(&encoded_children, storage); + Node::Branch(children, val.map(DBValue::from_slice)) + }, + EncodedNode::NibbledBranch(k, encoded_children, val) => { + let children = dec_children(&encoded_children, storage); + // TODO slice currently encoded as extension: makes sense to replace bit id + // TODO this bit info does not seem to be use anywhere -> remove decoding code?? + Node::NibbledBranch(k.encoded(false), children, val.map(DBValue::from_slice)) + }, } } @@ -146,9 +157,21 @@ where .map(|maybe_child| maybe_child.map(|child| child_cb(child)) ), - value + value.as_ref().map(|v|&v[..]) ) - } + }, + Node::NibbledBranch(partial, mut children, value) => { + C::branch_node_nibbled( + &partial, + // map the `NodeHandle`s from the Branch to `ChildReferences` + children.iter_mut() + .map(Option::take) + .map(|maybe_child| + maybe_child.map(|child| child_cb(child)) + ), + value.as_ref().map(|v|&v[..]) + ) + }, } } } @@ -296,6 +319,46 @@ where marker: PhantomData, // TODO: rpheimer: "we could have the NodeCodec trait take &self to its methods and then we don't need PhantomData. we can just store an instance of C: NodeCodec in the trie struct. If it's a ZST it won't have any additional overhead anyway" } +pub struct TrieDBMutNoExt<'a, H, C>(TrieDBMut<'a, H, C>) +where + H: Hasher + 'a, + C: NodeCodec; + +impl<'a, H, C> TrieDBMutNoExt<'a, H, C> +where + H: Hasher, + C: NodeCodec +{ + /// Create a new trie with backing database `db` and empty `root`. + pub fn new(db: &'a mut HashDB, root: &'a mut H::Out) -> Self { + TrieDBMutNoExt(TrieDBMut::new(db, root)) + } + + /// Create a new trie with the backing database `db` and `root. + /// Returns an error if `root` does not exist. + pub fn from_existing(db: &'a mut HashDB, root: &'a mut H::Out) -> Result { + Ok(TrieDBMutNoExt(TrieDBMut::from_existing(db, root)?)) + } + + /// Get the backing database. + pub fn db(&self) -> &HashDB { + self.0.db() + } + + /// Get the backing database mutably. + pub fn db_mut(&mut self) -> &mut HashDB { + self.0.db_mut() + } + + /// Commit the in-memory changes to disk, freeing their storage and + /// updating the state root. + pub fn commit(&mut self) { + self.0.commit() + } + +} + + impl<'a, H, C> TrieDBMut<'a, H, C> where H: Hasher, @@ -401,7 +464,7 @@ where } else { return Ok(None); } - } + }, Node::Extension(ref slice, ref child) => { let slice = NibbleSlice::from_encoded(slice).0; if partial.starts_with(&slice) { @@ -409,7 +472,7 @@ where } else { return Ok(None); } - } + }, Node::Branch(ref children, ref value) => { if partial.is_empty() { return Ok(value.as_ref().map(|v| DBValue::from_slice(v))); @@ -420,7 +483,21 @@ where None => return Ok(None), } } - } + }, + Node::NibbledBranch(ref slice, ref children, ref value) => { + let slice = NibbleSlice::from_encoded(slice).0; + if partial.is_empty() { + return Ok(value.as_ref().map(|v| DBValue::from_slice(v))); + } else if partial.starts_with(&slice) { + let idx = partial.at(0); + match children[idx as usize].as_ref() { + Some(child) => (1 + slice.len(), child), + None => return Ok(None), + } + } else { + return Ok(None) + } + }, } }; @@ -435,7 +512,7 @@ where NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h)?, }; - let stored = self.storage.destroy(h); + let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? let (new_stored, changed) = self.inspect(stored, move |trie, stored| { trie.insert_inspector(stored, partial, value, old_val).map(|a| a.into_action()) })?.expect("Insertion never deletes."); @@ -443,6 +520,20 @@ where Ok((self.storage.alloc(new_stored), changed)) } + fn insert_at_no_ext(&mut self, handle: NodeHandle, partial: NibbleSlice, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), H::Out, C::Error> { + let h = match handle { + NodeHandle::InMemory(h) => h, + NodeHandle::Hash(h) => self.cache(h)?, + }; + let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? + let (new_stored, changed) = self.inspect(stored, move |trie, stored| { + trie.insert_inspector_no_ext(stored, partial, value, old_val).map(|a| a.into_action()) + })?.expect("Insertion never deletes."); + + Ok((self.storage.alloc(new_stored), changed)) + } + + /// the insertion inspector. fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -601,9 +692,144 @@ where )) } } + Node::NibbledBranch(..) => { + unimplemented!() + } + }) + } + + fn insert_inspector_no_ext(&mut self, node: Node, partial: NibbleSlice, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { + trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); + + Ok(match node { + Node::Empty => { + InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) + } + Node::NibbledBranch(encoded, mut children, stored_value) => { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + + let cp = partial.common_prefix(&existing_key); + if cp == existing_key.len() && cp == partial.len() { + let unchanged = stored_value.as_ref() == Some(&value); + let branch = Node::NibbledBranch(existing_key.encoded(false), children, Some(value)); + *old_val = stored_value; + + match unchanged { + true => InsertAction::Restore(branch), + false => InsertAction::Replace(branch), + } + } else if cp < existing_key.len() { + // insert a branch value in between + trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + let low = Node::NibbledBranch(existing_key.mid(cp + 1).encoded(false), children, stored_value); + let ix = existing_key.at(cp); + let mut children = empty_children(); + let alloc_storage = self.storage.alloc(Stored::New(low)); + + + children[ix as usize] = Some(alloc_storage.into()); + + if partial.len() - cp == 0 { + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded_leftmost(cp,false), + children, + Some(value), + ) + ) + } else { + let ix = partial.at(cp); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).encoded(true), value))); + + children[ix as usize] = Some(leaf.into()); + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded_leftmost(cp,false), + children, + None, + ) + ) + + } + + } else { + // append after cp == existing_key and partial > cp + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + let idx = partial.at(cp) as usize; + let partial = partial.mid(cp + 1); + if let Some(child) = children[idx].take() { + // original had something there. recurse down into it. + let (new_child, changed) = self.insert_at_no_ext(child, partial, value, old_val)?; + children[idx] = Some(new_child.into()); + if !changed { + // the new node we composed didn't change. that means our branch is untouched too. + return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.encoded(false), children, stored_value))); + } + } else { + // original had nothing there. compose a leaf. + let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.encoded(true), value))); + children[idx] = Some(leaf.into()); + } + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded(false), + children, + stored_value, + )) + + } + + }, + Node::Leaf(encoded, stored_value) => { + + let existing_key = NibbleSlice::from_encoded(&encoded).0; + let cp = partial.common_prefix(&existing_key); + if cp == existing_key.len() && cp == partial.len() { + trace!(target: "trie", "equivalent-leaf: REPLACE"); + // equivalent leaf. + let unchanged = stored_value == value; + *old_val = Some(stored_value); + + match unchanged { + // unchanged. restore + true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), + false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), + } + } else if cp < existing_key.len() { + trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + + // one of us isn't empty: transmute to branch here + let mut children = empty_children(); + let branch = if existing_key.is_empty() { + // always replace since branch isn't leaf. + Node::NibbledBranch(existing_key.encoded(false), children, Some(stored_value)) + } else { + let idx = existing_key.at(cp) as usize; + let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); + children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); + + Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) + }; + + // always replace because whatever we get out here is not the branch we started with. + let branch_action = self.insert_inspector_no_ext(branch, partial, value, old_val)?.unwrap_node(); + InsertAction::Replace(branch_action) + } else { + trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + // fully-shared prefix for an extension. + // make a stub branch and an extension. + let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); + // augment the new branch. + let branch = self.insert_inspector_no_ext(branch, partial, value, old_val)?.unwrap_node(); + + InsertAction::Replace(branch) + } + }, + Node::Extension(..) => { + unreachable!() + }, + Node::Branch(..) => unreachable!(), }) } + /// Remove a node from the trie based on key. fn remove_at(&mut self, handle: NodeHandle, partial: NibbleSlice, old_val: &mut Option) -> Result, H::Out, C::Error> { let stored = match handle { @@ -619,6 +845,22 @@ where Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } + /// Remove a node from the trie based on key. + fn remove_at_no_ext(&mut self, handle: NodeHandle, partial: NibbleSlice, old_val: &mut Option) -> Result, H::Out, C::Error> { + let stored = match handle { + NodeHandle::InMemory(h) => self.storage.destroy(h), + NodeHandle::Hash(h) => { + let handle = self.cache(h)?; + self.storage.destroy(handle) + } + }; + + let opt = self.inspect(stored, move |trie, node| trie.remove_inspector_no_ext(node, partial, old_val))?; + + Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) + } + + /// the removal inspector fn remove_inspector(&mut self, node: Node, partial: NibbleSlice, old_val: &mut Option) -> Result, H::Out, C::Error> { Ok(match (node, partial.is_empty()) { @@ -697,9 +939,91 @@ where Action::Restore(Node::Extension(encoded, child_branch)) } } + (Node::NibbledBranch(..), _) => { + unreachable!() + } }) } + /// the removal inspector for no extension variant + fn remove_inspector_no_ext(&mut self, node: Node, partial: NibbleSlice, old_val: &mut Option) -> Result, H::Out, C::Error> { + Ok(match (node, partial.is_empty()) { + (Node::Empty, _) => Action::Delete, + (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), + (Node::NibbledBranch(n, children, Some(val)), true) => { + *old_val = Some(val); + // always replace since we took the value out. + Action::Replace(self.fix(Node::NibbledBranch(n, children, None))?) + }, + (Node::NibbledBranch(encoded, mut children, value), false) => { + let (cp, existing_len) = { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + (existing_key.common_prefix(&partial), existing_key.len()) + }; + if cp == existing_len && cp == partial.len() { + + // replace val + if let Some(val) = value { + *old_val = Some(val); + Action::Replace(self.fix(Node::NibbledBranch(encoded, children, None))?) + } else { + Action::Restore(Node::NibbledBranch(encoded, children, None)) + } + } else if cp < existing_len { + // partway through an extension -- nothing to do here. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } else { + // cp == existing_len && cp < partial.len() : check children + // TODO can merge with branch remove code + let idx = partial.at(cp) as usize; + + if let Some(child) = children[idx].take() { + trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); + match self.remove_at_no_ext(child, partial.mid(cp + 1), old_val)? { + Some((new, changed)) => { + children[idx] = Some(new.into()); + let branch = Node::NibbledBranch(encoded, children, value); + match changed { + // child was changed, so we were too. + true => Action::Replace(branch), + // unchanged, so we are too. + false => Action::Restore(branch), + } + }, + None => { + // the child we took was deleted. + // the node may need fixing. + trace!(target: "trie", "branch child deleted, partial={:?}", partial); + Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value))?) + }, + } + } else { + // no change needed. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } + } + }, + (Node::Leaf(encoded, value), _) => { + if NibbleSlice::from_encoded(&encoded).0 == partial { + // this is the node we were looking for. Let's delete it. + *old_val = Some(value); + Action::Delete + } else { + // leaf the node alone. + trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); + Action::Restore(Node::Leaf(encoded, value)) + } + }, + (Node::Extension(..), _) => { + unreachable!() + } + (Node::Branch(..), _) => { + unreachable!() + } + }) + } + + /// Given a node which may be in an _invalid state_, fix it such that it is then in a valid /// state. /// @@ -748,7 +1072,89 @@ where Ok(Node::Branch(children, value)) } } - } + }, + Node::NibbledBranch(enc_nibble, mut children, value) => { + // if only a single value, transmute to leaf/extension and feed through fixed. + #[derive(Debug)] + enum UsedIndex { + None, + One(u8), + Many, + }; + let mut used_index = UsedIndex::None; + for i in 0..16 { + match (children[i].is_none(), &used_index) { + (false, &UsedIndex::None) => used_index = UsedIndex::One(i as u8), + (false, &UsedIndex::One(_)) => { + used_index = UsedIndex::Many; + break; + } + _ => continue, + } + } + + match (used_index, value) { + (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), + (UsedIndex::One(a), None) => { + // only one onward node. use child instead + let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + //let new_node = Node::Extension(new_partial, child); + let stored = match child { + NodeHandle::InMemory(h) => self.storage.destroy(h), + NodeHandle::Hash(h) => { + let handle = self.cache(h)?; + self.storage.destroy(handle) + } + }; + let child_node = match stored { + Stored::New(node) => node, + Stored::Cached(node, hash) => { + self.death_row.insert(hash); + node + }, + }; + match child_node { + Node::Leaf(sub_partial, value) => { + let ix = [a]; + // warning cannot compose more than one time + let new_partial_tmp = NibbleSlice::new_composed( + &NibbleSlice::from_encoded(&enc_nibble).0, + &NibbleSlice::new_offset(&ix, 1) + ).encoded(false); + let new_partial = NibbleSlice::new_composed( + &NibbleSlice::from_encoded(&new_partial_tmp).0, + &NibbleSlice::from_encoded(&sub_partial).0); + Ok(Node::Leaf(new_partial.encoded(false), value)) + }, + Node::NibbledBranch(sub_partial, ch_children, ch_value) => { + let ix = [a]; + // warning cannot compose more than one time + let new_partial_tmp = NibbleSlice::new_composed( + &NibbleSlice::from_encoded(&enc_nibble).0, + &NibbleSlice::new_offset(&ix, 1) + ).encoded(false); + let new_partial = NibbleSlice::new_composed( + &NibbleSlice::from_encoded(&new_partial_tmp).0, + &NibbleSlice::from_encoded(&sub_partial).0); + Ok(Node::NibbledBranch(new_partial.encoded(false), ch_children, ch_value)) + }, + _ => unreachable!(), + } + }, + (UsedIndex::None, Some(value)) => { + // make a leaf. + trace!(target: "trie", "fixing: branch -> leaf"); + let partial = NibbleSlice::from_encoded(&enc_nibble).0; + // TODO encoded switch can be almost costless instead of this enc/dec + Ok(Node::Leaf(partial.encoded(true), value)) + }, + (_, value) => { + // all is well. + trace!(target: "trie", "fixing: restoring branch"); + Ok(Node::NibbledBranch(enc_nibble, children, value)) + }, + } + }, Node::Extension(partial, child) => { let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), @@ -803,7 +1209,7 @@ where Ok(Node::Extension(partial, self.storage.alloc(stored).into())) } } - } + }, other => Ok(other), // only ext and branch need fixing. } } @@ -951,6 +1357,69 @@ where } } +impl<'a, H, C> TrieMut for TrieDBMutNoExt<'a, H, C> +where + H: Hasher, + C: NodeCodec +{ + fn root(&mut self) -> &H::Out { + self.0.root() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + + fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, H::Out, C::Error> + where 'x: 'key + { + self.0.get(key) + } + + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { + if value.is_empty() { return self.remove(key) } + + let mut old_val = None; + + trace!(target: "trie", "insert: key={:#x?}, value={:#x?}", key, value); + + let root_handle = self.0.root_handle(); + let (new_handle, changed) = self.0.insert_at_no_ext( + root_handle, + NibbleSlice::new(key), + DBValue::from_slice(value), + &mut old_val, + )?; + + trace!(target: "trie", "insert: altered trie={}", changed); + self.0.root_handle = NodeHandle::InMemory(new_handle); + + Ok(old_val) + } + + fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { + trace!(target: "trie", "remove: key={:#x?}", key); + + let root_handle = self.0.root_handle(); + let key = NibbleSlice::new(key); + let mut old_val = None; + + match self.0.remove_at_no_ext(root_handle, key, &mut old_val)? { + Some((handle, changed)) => { + trace!(target: "trie", "remove: altered trie={}", changed); + self.0.root_handle = NodeHandle::InMemory(handle); + } + None => { + trace!(target: "trie", "remove: obliterated trie"); + self.0.root_handle = NodeHandle::Hash(C::hashed_null_node()); + *self.0.root = C::hashed_null_node(); + } + } + + Ok(old_val) + } +} + impl<'a, H, C> Drop for TrieDBMut<'a, H, C> where H: Hasher, @@ -969,8 +1438,8 @@ mod tests { use memory_db::MemoryDB; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; - use reference_trie::{RefTrieDBMut, TrieMut, NodeCodec, - ReferenceNodeCodec, ref_trie_root}; + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, NodeCodec, + ReferenceNodeCodec, ref_trie_root, RefTrieDB, RefTrieDBNoExt}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -993,6 +1462,28 @@ mod tests { } } + fn populate_trie_no_ext<'db>( + db: &'db mut HashDB, + root: &'db mut ::Out, + v: &[(Vec, Vec)] + ) -> RefTrieDBMutNoExt<'db> { + let mut t = RefTrieDBMutNoExt::new(db, root); + for i in 0..v.len() { + let key: &[u8]= &v[i].0; + let val: &[u8] = &v[i].1; + t.insert(key, val).unwrap(); + } + t + } + + fn unpopulate_trie_no_ext<'db>(t: &mut RefTrieDBMutNoExt<'db>, v: &[(Vec, Vec)]) { + for i in v { + let key: &[u8]= &i.0; + t.remove(key).unwrap(); + } + } + + #[test] fn playpen() { env_logger::init(); @@ -1074,6 +1565,36 @@ mod tests { t2.remove(&[0x01]).unwrap(); } + #[test] + fn remove_to_empty_no_ext() { + let big_value = b"00000000000000000000000000000000"; + let big_value2 = b"00000000000000000000000000000002"; + let big_value3 = b"00000000000000000000000000000004"; + + let mut memdb2 = MemoryDB::default(); + let mut root2 = Default::default(); + { + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + //t1.insert(&[0x01, 0x23], big_value).unwrap(); + //t1.insert(&[0x01, 0x34], big_value).unwrap(); + let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2); + + t2.insert(&[0x01, 0x23], big_value3).unwrap(); + t2.insert(&[0x01], big_value2).unwrap(); + t2.insert(&[0x01, 0x34], big_value).unwrap(); + t2.remove(&[0x01]).unwrap(); + // commit on drop + } + let t2 = RefTrieDBNoExt::new(& memdb2, &root2); + assert_eq!(&root2[..], &reference_trie::calc_root_no_ext(vec![ + (vec![0x01u8, 0x23], big_value3.to_vec()), + (vec![0x01u8, 0x34], big_value.to_vec()), + ])[..]); + } + + #[test] fn insert_replace_root() { let mut memdb = MemoryDB::default(); diff --git a/trie-db/testset0 b/trie-db/testset0 new file mode 100644 index 0000000000000000000000000000000000000000..e07b5593b038b5f226301500f2d7fa4b10a09c4d GIT binary patch literal 5120 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ü~xDeq;tf*G6?2Ha%S*2x}@q{4e%wqND%$s&ff!_Qt3KmpZiKs+1o^=jwg+rw^QC}IJzdY%e6eT}CbuWrSrO7>d z6THlA014ssqsNzLe2G$Es*Z#f&6}JJhO(?yNGZc4h-LH3+~>EOoKLzjE3x_ugqgc@ zei2MUonc+Kc^|5ErovDM4nAJSQW@?89o+QruXi8|qD^tGrH0aKzm%UB;ZdwHrMOcA z%Xt3U7k)DI6J`Va&EgjK7uxI_rq>NqCdGJOsxN9#lj}Oayi|?!$)H&>&AHLHj2u2D z!|EU=CNZ7ppR>NwtTGMhJFHgcoU1B7Krx>qn3qJT7(=N`d5oT^36Rl}Fp#fu4 zBx&)_s6mE{ezW+iX@BigJ=(w~U#%J$e^}~hH|wnB1BU(#Ds!yG9Xz#w?Ele{bA9t~ zmgHeqc#zzlME zxHbpkkP6#sk-2DDn0NTnoD|z0aG0DX#_VTM+T$BXqsva=lovoR&0c9PnE+iQP6`%E zJWz{{@Ol(kiDb?QOay(Ajyl>+kIf{Hh~4=kL_uhk>!3@e4ENNAaQbzrL5hL8Biuor@+?W3|vj4AZr}4R)iu0v6Hu)$_A`tt$t*st#MJ(?{ GCN^AE%RDpy literal 0 HcmV?d00001 diff --git a/trie-db/testset2 b/trie-db/testset2 new file mode 100644 index 0000000000000000000000000000000000000000..babe3d8be3f82419817441cb504ca843d300a94d GIT binary patch literal 102400 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ü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 z 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�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 A^8f$< literal 0 HcmV?d00001 diff --git a/trie-db/testset3 b/trie-db/testset3 new file mode 100644 index 0000000000000000000000000000000000000000..d4bd3537b5412fc8f8b78714fe9de0f0b9d02309 GIT binary patch literal 204800 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ü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 z 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�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 q8|R9`9X!@)g)er10=a2z8@BD#cglL Date: Tue, 19 Feb 2019 09:37:57 +0100 Subject: [PATCH 002/120] Tabify. --- test-support/reference-trie/src/lib.rs | 116 +++++++------------------ trie-db/src/lookup.rs | 24 ++--- 2 files changed, 44 insertions(+), 96 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 050847db..a81e1b59 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -318,8 +318,8 @@ impl NodeCodec for ReferenceNodeCodec { } fn branch_node( - children: impl Iterator::Out>>>>, - maybe_value: Option<&[u8]>) -> Vec { + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>) -> Vec { let mut output = vec![0, 0, 0]; let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); @@ -343,10 +343,10 @@ impl NodeCodec for ReferenceNodeCodec { } fn branch_node_nibbled( - _partial: &[u8], - _children: impl Iterator::Out>>>>, - _maybe_value: Option<&[u8]>) -> Vec { - unreachable!() + _partial: &[u8], + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!() } } @@ -355,7 +355,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; fn hashed_null_node() -> ::Out { - ReferenceNodeCodec::hashed_null_node() + ReferenceNodeCodec::hashed_null_node() } fn decode(data: &[u8]) -> ::std::result::Result { @@ -364,7 +364,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; - let nibble_count = take(input, 1).ok_or(ReferenceError::BadFormat)?[0] as usize; + let nibble_count = take(input, 1).ok_or(ReferenceError::BadFormat)?[0] as usize; let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); let value = if has_value { @@ -391,50 +391,50 @@ impl NodeCodec for ReferenceNodeCodecNoExt { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } - } - } + } + } fn try_decode_hash(data: &[u8]) -> Option<::Out> { - ReferenceNodeCodec::try_decode_hash(data) + ReferenceNodeCodec::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { - ReferenceNodeCodec::is_empty_node(data) + ReferenceNodeCodec::is_empty_node(data) } fn empty_node() -> Vec { - ReferenceNodeCodec::empty_node() + ReferenceNodeCodec::empty_node() } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { - ReferenceNodeCodec::leaf_node(partial, value) + ReferenceNodeCodec::leaf_node(partial, value) } fn ext_node(_partial: &[u8], _child: ChildReference<::Out>) -> Vec { - unreachable!() + unreachable!() } fn branch_node( - _children: impl Iterator::Out>>>>, - _maybe_value: Option<&[u8]>) -> Vec { - unreachable!() + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!() } fn branch_node_nibbled( - partial: &[u8], - children: impl Iterator::Out>>>>, - maybe_value: Option<&[u8]>) -> Vec { + partial: &[u8], + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>) -> Vec { let mut output = Vec::with_capacity(partial.len() + 4); // TODO choose a good capacity estimation value (here it is only partial) - for _ in 0..3 { - output.push(0); - } + for _ in 0..3 { + output.push(0); + } - let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; - output.push(nibble_count as u8); - if nibble_count % 2 == 1 { - output.push(partial[0] & 0x0f); - } - output.extend_from_slice(&partial[1..]); + let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; + output.push(nibble_count as u8); + if nibble_count % 2 == 1 { + output.push(partial[0] & 0x0f); + } + output.extend_from_slice(&partial[1..]); let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); @@ -606,13 +606,13 @@ pub fn compare_impl_no_ext_unordered( mut memdb: impl hash_db::HashDB, mut hashdb: impl hash_db::HashDB, ) { - let mut b_map = std::collections::btree_map::BTreeMap::new(); + let mut b_map = std::collections::btree_map::BTreeMap::new(); let root = { let mut root = Default::default(); let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); - b_map.insert(data[i].0.clone(),data[i].1.clone()); + b_map.insert(data[i].0.clone(),data[i].1.clone()); } t.root().clone() }; @@ -643,55 +643,3 @@ pub fn compare_impl_no_ext_unordered( assert_eq!(root, root_new); } -/* -pub fn compare_impl_no_ext_unordered_rem( - data: Vec<(Vec,Vec)>, - rem: &[(usize, usize)], - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, -) { - let mut b_map = std::collections::btree_map::BTreeMap::new(); - let root = { - let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - let mut rem_ix = 0; - let mut has_rem = true; - for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); - b_map.insert(data[i].0.clone(),data[i].1.clone()); - if has_rem && i == rem[rem_ix].1 { - t.remove(&data[rem[rem_ix].0].0[..]).unwrap(); - b_map.remove(&data[rem[rem_ix].0].0[..]); - rem_ix += 1; - if rem_ix == rem.len() { has_rem = false } - } - } - t.root().clone() - }; - let root_new = { - let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) - }; - - if root != root_new { - { - let db : &dyn hash_db::HashDB<_,_> = &memdb; - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; - let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - } - - assert_eq!(root, root_new); -}*/ diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 3f999e0a..28433c75 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -91,21 +91,21 @@ where } }, Node::NibbledBranch(slice, children, value) => { - if !key.starts_with(&slice) { + if !key.starts_with(&slice) { return Ok(None) } - match key.len() == slice.len() { - true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children[key.at(slice.len()) as usize] { - Some(x) => { - node_data = x; - key = key.mid(slice.len() + 1); - } - None => return Ok(None) - } - } - }, + match key.len() == slice.len() { + true => return Ok(value.map(move |val| self.query.decode(val))), + false => match children[key.at(slice.len()) as usize] { + Some(x) => { + node_data = x; + key = key.mid(slice.len() + 1); + } + None => return Ok(None) + } + } + }, Node::Empty => return Ok(None), } From 5d79a125b3d763371c0284f25183356b5d0d224c Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Feb 2019 18:52:00 +0100 Subject: [PATCH 003/120] Implementation for trie_root. --- test-support/reference-trie/src/lib.rs | 45 ++++++++++- trie-db/fuzz/Cargo.toml | 5 ++ .../fuzz/fuzz_targets/trie_unhashed_no_ext.rs | 10 +++ trie-db/fuzz/src/lib.rs | 6 ++ trie-db/src/iter_build.rs | 12 ++- trie-root/src/lib.rs | 74 ++++++++++++++++--- 6 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index a81e1b59..b8a51a8c 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -31,7 +31,6 @@ use trie_db::{ DBValue, trie_visit, trie_visit_no_ext, - ProcessEncodedNode, TrieBuilder, TrieRoot, }; @@ -67,7 +66,21 @@ fn ref_trie_root_unhashed(input: I) -> Vec where trie_root::unhashed_trie::(input) } +pub fn ref_trie_root_no_ext(input: I) -> ::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::trie_root_no_ext::(input) +} +fn ref_trie_root_unhashed_no_ext(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::unhashed_trie_no_ext::(input) +} const EMPTY_TRIE: u8 = 0; const LEAF_NODE_OFFSET: u8 = 1; @@ -95,6 +108,17 @@ fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator(nibbles: &'a [u8]) -> impl Iterator + 'a { + debug_assert!(nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); + let first_byte = nibbles.len() as u8; + + once(first_byte) + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) + .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) +} + + pub fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { let first = if has_value { BRANCH_NODE_WITH_VALUE @@ -132,8 +156,11 @@ impl TrieStream for ReferenceTrieStream { value.encode_to(&mut self.buffer); } - fn begin_branch(&mut self, maybe_value: Option<&[u8]>, has_children: impl Iterator) { + fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator) { self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); + if let Some(partial) = maybe_key { + self.buffer.extend(fuse_nibbles_node_no_ext(partial)); + } if let Some(value) = maybe_value { value.encode_to(&mut self.buffer); } @@ -534,6 +561,20 @@ pub fn compare_unhashed( assert_eq!(root, root_new); } +pub fn compare_unhashed_no_ext( + data: Vec<(Vec,Vec)>, +) { + let root_new = { + let mut cb = trie_db::TrieRootUnhashed::::default(); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = ref_trie_root_unhashed_no_ext(data); + + assert_eq!(root, root_new); +} + + pub fn calc_root( data: I, diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index e95d917b..0acc4037 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -29,6 +29,11 @@ members = ["."] name = "trie_root_new" path = "fuzz_targets/trie_root_new.rs" +[[bin]] +name = "trie_unhashed_no_ext" +path = "fuzz_targets/trie_unhashed_no_ext.rs" + + [[bin]] name = "trie_root" path = "fuzz_targets/trie_root.rs" diff --git a/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs new file mode 100644 index 00000000..06d06916 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs @@ -0,0 +1,10 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_unhashed_no_ext; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_unhashed_no_ext(data); +}); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 4b352451..7b430697 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -110,6 +110,12 @@ pub fn fuzz_that_compare_impl(input: &[u8]) { reference_trie::compare_impl(data, memdb, hashdb); } +pub fn fuzz_that_unhashed_no_ext(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data(input)); + reference_trie::compare_unhashed_no_ext(data); +} + + pub fn fuzz_that_no_ext_insert(input: &[u8]) { let data = fuzz_to_data(input); //println!("data{:?}", data); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 5930c497..0030b40c 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -510,6 +510,11 @@ mod test { let memdb = MemoryDB::default(); reference_trie::compare_unhashed(data, memdb); } + fn compare_unhashed_no_ext(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::default(); + reference_trie::compare_unhashed_no_ext(data, memdb); + } + @@ -540,11 +545,14 @@ mod test { } #[test] fn root_extension_tierce () { - compare_unhashed(vec![ + let d = vec![ (vec![1u8,2u8,3u8,3u8],vec![8u8;2]), (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), - ]); + ]; + compare_unhashed(d.clone()); + compare_unhashed_no_ext(d); } + #[test] fn root_extension_tierce_big () { // on more content unhashed would hash diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 637d247f..64c61fa6 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -43,7 +43,7 @@ pub use hash_db::Hasher; pub trait TrieStream { fn new() -> Self; fn append_empty_data(&mut self); - fn begin_branch(&mut self, maybe_value: Option<&[u8]>, has_children: impl Iterator); + fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator); fn append_empty_child(&mut self) {} fn end_branch(&mut self, _value: Option<&[u8]>) {} fn append_leaf(&mut self, key: &[u8], value: &[u8]); @@ -87,6 +87,16 @@ pub fn trie_root(input: I) -> H::Out where B: AsRef<[u8]>, H: Hasher, S: TrieStream, +{ + trie_root_inner::(input, false) +} + +fn trie_root_inner(input: I, no_ext: bool) -> H::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, { // first put elements into btree to sort them and to remove duplicates let input = input @@ -110,10 +120,23 @@ pub fn trie_root(input: I) -> H::Out where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream); + build_trie::(&input, 0, &mut stream, no_ext); H::hash(&stream.out()) } +/// variant of `trie_root` without extension +pub fn trie_root_no_ext(input: I) -> H::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, +{ + trie_root_inner::(input, true) +} + + + //#[cfg(test)] // consider feature="std" pub fn unhashed_trie(input: I) -> Vec where I: IntoIterator, @@ -121,6 +144,16 @@ pub fn unhashed_trie(input: I) -> Vec where B: AsRef<[u8]>, H: Hasher, S: TrieStream, +{ + unhashed_trie_inner::(input, false) +} + +fn unhashed_trie_inner(input: I, no_ext: bool) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, { // first put elements into btree to sort them and to remove duplicates let input = input @@ -145,10 +178,23 @@ pub fn unhashed_trie(input: I) -> Vec where // println!("as nibbles: {:#x?}", input); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream); + build_trie::(&input, 0, &mut stream, no_ext); stream.out() } +/// TODO avoid code redundancy with unhashed_trie +//#[cfg(test)] // consider feature="std" +pub fn unhashed_trie_no_ext(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, +{ + unhashed_trie_inner::(input, true) +} + + /// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. /// /// ```rust @@ -185,7 +231,7 @@ pub fn sec_trie_root(input: I) -> H::Out where /// Takes a slice of key/value tuples where the key is a slice of nibbles /// and encodes it into the provided `Stream`. // pub fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) -fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where +fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ext: bool) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, @@ -217,13 +263,19 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where // than what we saw on the last call (`cursor`): append the new part // of the path then recursively append the remainder of all items // who had this partial key. - if shared_nibble_count > cursor { + let (cursor, o_branch_slice) = if no_ext { + if shared_nibble_count > cursor { + (shared_nibble_count, Some(&key[cursor..shared_nibble_count])) + } else { + (cursor, Some(&key[0..0])) + } + } else if shared_nibble_count > cursor { // println!("[build_trie] appending ext and recursing, cursor={}, stream={:?}, partial key={:?}", cursor, stream.as_raw(), &key[cursor..shared_nibble_count]); stream.append_extension(&key[cursor..shared_nibble_count]); - build_trie_trampoline::(input, shared_nibble_count, stream); + build_trie_trampoline::(input, shared_nibble_count, stream, no_ext); // println!("[build_trie] returning after recursing, cursor={}, stream={:?}, partial key={:?}", cursor, stream.as_raw(), &key[cursor..shared_nibble_count]); return; - } + } else { (cursor, None) }; // We'll be adding a branch node because the path is as long as it gets. // First we need to figure out what entries this branch node will have... @@ -246,7 +298,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where } // Put out the node header: - stream.begin_branch(value, shared_nibble_counts.iter().map(|&n| n > 0)); + stream.begin_branch(o_branch_slice, value, shared_nibble_counts.iter().map(|&n| n > 0)); // Fill in each slot in the branch node. We don't need to bother with empty slots since they // were registered in the header. @@ -254,7 +306,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where for &count in &shared_nibble_counts { if count > 0 { // println!("[build_trie] branch slot {}; recursing with cursor={}, begin={}, shared nibbles={}, input={:?}", i, cursor, begin, shared_nibble_count, &input[begin..(begin + shared_nibble_count)]); - build_trie_trampoline::(&input[begin..(begin + count)], cursor + 1, stream); + build_trie_trampoline::(&input[begin..(begin + count)], cursor + 1, stream, no_ext); begin += count; } else { stream.append_empty_child(); @@ -269,13 +321,13 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where } } -fn build_trie_trampoline(input: &[(A, B)], cursor: usize, stream: &mut S) where +fn build_trie_trampoline(input: &[(A, B)], cursor: usize, stream: &mut S, no_ext: bool) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { let mut substream = S::new(); - build_trie::(input, cursor, &mut substream); + build_trie::(input, cursor, &mut substream, no_ext); stream.append_substream::(substream); } From e7b299c5877e26648f9e290517c3a8d05e3a2231 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Mar 2019 09:56:33 +0100 Subject: [PATCH 004/120] Fix test --- trie-db/src/triedb.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 43164504..c0ae726d 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -353,11 +353,11 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { } }, Node::NibbledBranch(ref slice, ref nodes, _) => { - if !key.starts_with(slice) { + if !partial.starts_with(slice) { self.descend(&node_data)?; return Ok(()) } - if key.len() == slice.len() { + if partial.len() == slice.len() { self.trail.push(Crumb { status: Status::Entering, node: node.clone().into(), @@ -365,7 +365,7 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { return Ok(()) } else { - let i = key.at(slice.len()); + let i = partial.at(slice.len()); self.trail.push(Crumb { status: Status::AtChild(i as usize), node: node.clone().into(), @@ -691,6 +691,7 @@ mod tests { let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); + let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), DBValue::from_slice(b"A"))); iter.seek(b"!").unwrap(); assert_eq!(d, iter.map(|x| x.unwrap().1).collect::>()); From 2ca4542beef007a2e2cd2a32d3f0aabdb0a60b59 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Mar 2019 11:46:54 +0100 Subject: [PATCH 005/120] Fuzz of prefix seems ok for pr 12, no_ext things are totally broken. --- memory-db/src/lib.rs | 39 ++++++++++++++++++++++++-- test-support/reference-trie/src/lib.rs | 9 ++++-- trie-db/fuzz/Cargo.toml | 6 ++-- trie-db/fuzz/src/lib.rs | 38 ++++++++++++++++++------- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 0516c8b2..c1c68b24 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -71,7 +71,7 @@ use std::mem; /// assert!(!m.contains(&k, &[])); /// } /// ``` -#[derive(Clone, PartialEq)] +#[derive(Clone)] pub struct MemoryDB where H: KeyHasher, @@ -82,7 +82,42 @@ pub struct MemoryDB null_node_data: T, _kf: ::std::marker::PhantomData, } - +// TODO rem just for quick check of calc_impl +impl PartialEq> for MemoryDB + where + H: KeyHasher, + KF: KeyFunction, + >::Key: Eq + std::fmt::Debug, + T: Eq + std::fmt::Debug, +{ + fn eq(&self, other: &MemoryDB) -> bool { + for a in self.data.iter() { + match other.data.get(&a.0) { + Some(v) => { + if v != a.1 { + println!("{} {}", self.data.len(), other.data.len()); + println!("val {:?} \n{:?}", v, a.1); + return false; + } + }, + None => { + println!("{} {}", self.data.len(), other.data.len()); + println!("key {:?}", a.0); + return false; + } + } + } + true + } +} +impl Eq for MemoryDB + where + H: KeyHasher, + KF: KeyFunction, + >::Key: Eq + std::fmt::Debug, + T: Eq + std::fmt::Debug, +{} + pub trait KeyFunction { type Key: Send + Sync + Clone + std::hash::Hash + std::cmp::Eq ; diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index cb434608..75f7f845 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -485,10 +485,10 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } -pub fn compare_impl( +pub fn compare_impl + Eq> ( data: Vec<(Vec,Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, + mut memdb: X, + mut hashdb: X, ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); @@ -501,6 +501,7 @@ pub fn compare_impl( for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } + t.commit(); t.root().clone() }; if root != root_new { @@ -523,6 +524,8 @@ pub fn compare_impl( } assert_eq!(root, root_new); + // compare db content for key fuzzing + assert!(memdb == hashdb); } pub fn compare_root( diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 0acc4037..8440a733 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -10,9 +10,9 @@ edition = "2018" cargo-fuzz = true [dependencies] -memory-db = { path = "../../memory-db", version = "0.11.0" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.11.0" } -keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.11.0" } +memory-db = { path = "../../memory-db", version = "0.12.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.12.0" } +keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.12.0" } afl = "0.4.3" honggfuzz = "0.5" diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 7b430697..38964266 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -1,11 +1,12 @@ -use memory_db::MemoryDB; +use memory_db::{MemoryDB, HashKey, PrefixedKey}; use reference_trie::{ RefTrieDBMutNoExt, RefTrieDBMut, ref_trie_root, calc_root_no_ext, + calc_root, }; use trie_db::{TrieMut, DBValue}; use keccak_hasher::KeccakHasher; @@ -56,7 +57,7 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { pub fn fuzz_that_ref_trie_root(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); - let mut memdb = MemoryDB::default(); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for a in 0..data.len() { @@ -67,7 +68,7 @@ pub fn fuzz_that_ref_trie_root(input: &[u8]) { pub fn fuzz_that_ref_trie_root_fix_len(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data_fix_len(input)); - let mut memdb = MemoryDB::default(); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for a in 0..data.len() { @@ -105,8 +106,8 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { pub fn fuzz_that_compare_impl(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); - let memdb = MemoryDB::default(); - let hashdb = MemoryDB::::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl(data, memdb, hashdb); } @@ -119,7 +120,7 @@ pub fn fuzz_that_unhashed_no_ext(input: &[u8]) { pub fn fuzz_that_no_ext_insert(input: &[u8]) { let data = fuzz_to_data(input); //println!("data{:?}", data); - let mut memdb = MemoryDB::default(); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for a in 0..data.len() { @@ -135,12 +136,20 @@ pub fn fuzz_that_no_ext_insert(input: &[u8]) { pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { let data = fuzz_to_data(input); let mut data2 = std::collections::BTreeMap::new(); - let mut memdb = MemoryDB::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); let mut torem = None; + let mut a = 0; + { + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + t.commit(); + } // println!("data{:?}", data); - for a in 0..data.len() { + while a < data.len() { + + root = { + let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); + for _ in 0..3 { if a % 7 == 6 { // println!("remrand{:?}", a); // a random removal some time @@ -161,9 +170,18 @@ pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { } } } + a += 1; + if a == data.len() { + break; + } + } + t.commit(); + *t.root() + }; } + let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. //println!("data{:?}", data); - assert_eq!(*t.root(), calc_root_no_ext(data2)); + assert_eq!(*t.root(), calc_root(data2)); } From cfeb96af54c67264ebe2abaf0036f4c9768c9d50 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Mar 2019 16:20:38 +0100 Subject: [PATCH 006/120] Fix iter_build key indexes. --- memory-db/src/lib.rs | 3 +++ trie-db/fuzz/src/lib.rs | 1 + trie-db/src/iter_build.rs | 57 ++++++++++++++++++++------------------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index c1c68b24..00996572 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -91,6 +91,9 @@ impl PartialEq> for MemoryDB T: Eq + std::fmt::Debug, { fn eq(&self, other: &MemoryDB) -> bool { + /*for a in other.data.iter() { + println!("ok{:?}", a); + }*/ for a in self.data.iter() { match other.data.get(&a.0) { Some(v) => { diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 38964266..02b94b14 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -106,6 +106,7 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { pub fn fuzz_that_compare_impl(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); + //println!("data:{:?}", &data); let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl(data, memdb, hashdb); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 8a13b14d..92b5beed 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -1,6 +1,6 @@ // Copyright 2017, 2019 Parity Technologies // -// Licensed under the Apache License, Version 2.0 (the "License"); +// Licensed under the Apache License, Version .0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // @@ -28,23 +28,23 @@ fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { if v1[a] == v2[a] { } else { if (v1[a] >> 4) == (v2[a] >> 4) { - return a * 2 + 1; + return a * NIBBLE_PER_BYTES + 1; } else { - return a * 2; + return a * NIBBLE_PER_BYTES; } } } - return v1.len() * 2; + return v1.len() * NIBBLE_PER_BYTES; } // warn! start at 0 // TODO change biggest_depth?? // warn! slow don't loop on that when possible #[inline(always)] fn nibble_at(v1: &[u8], ix: usize) -> u8 { - if ix % 2 == 0 { - v1[ix/2] >> 4 + if ix % NIBBLE_PER_BYTES == 0 { + v1[ix / NIBBLE_PER_BYTES] >> 4 } else { - v1[ix/2] & 15 + v1[ix / NIBBLE_PER_BYTES] & 15 } } @@ -84,6 +84,7 @@ fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { /// initially allocated cache const INITIAL_DEPTH: usize = 10; const NIBBLE_SIZE: usize = 16; +const NIBBLE_PER_BYTES: usize = 2; // 2 ^ 8 / 2 ^ NIBBLE_SIZE impl CacheAccum where H: Hasher, @@ -129,14 +130,14 @@ where ) { let nibble_value = nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],target_depth+1).encoded(true); + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],target_depth+1); // Note: fwiu, having fixed key size, all values are in leaf (no value in // branch). TODO run metrics on a node to count branch with values - let encoded = C::leaf_node(&nkey.as_ref()[..], &v2.as_ref()[..]); + let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes // in fact or TODO put Vec in the trait? - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(nkey.len(), false); + let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); let hash = cb_ext.process(&encoded_key[..], encoded, false); // insert hash in branch (first level branch only at this point) @@ -176,8 +177,7 @@ where }; let nkey = if slice_size > 0 { - Some(NibbleSlice::new_offset(&ref_branch.as_ref()[..],offset) - .encoded_leftmost(slice_size, false)) + Some((offset, slice_size)) } else { None }; @@ -219,19 +219,20 @@ where cb_ext: &mut impl ProcessEncodedNode<::Out>, branch_d: usize, is_root: bool, - nkey: Option>, + nkey: Option<(usize, usize)>, ) -> ChildReference<::Out> { + // enc branch let v = self.0[branch_d].2.take(); let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); let branch_hash = cb_ext.process(&encoded_key[..], encoded, is_root && nkey.is_none()); - if let Some(nkey) = nkey { - let encoded = C::ext_node(&nkey[..], branch_hash); - let ext_len = nkey.len(); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if let Some(nkeyix) = nkey { + let nib = NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); + let encoded = C::ext_node(&nib[..], branch_hash); + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); let h = cb_ext.process(&encoded_key[..], encoded, is_root); h } else { @@ -246,17 +247,18 @@ where cb_ext: &mut impl ProcessEncodedNode<::Out>, branch_d: usize, is_root: bool, - nkey: Option>, + nkey: Option<(usize, usize)>, ) -> ChildReference<::Out> { // enc branch let v = self.0[branch_d].2.take(); + let enc_nkey = nkey.as_ref() + .map(|nkeyix|NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); - nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), // TODO warning this default value is unclean (should refer to an encoded impl) + enc_nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); - self.reset_depth(branch_d); - let ext_len = nkey.as_ref().map(|v|v.len()).unwrap_or(0); + let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! cb_ext.process(&encoded_key[..], encoded, is_root) } @@ -310,7 +312,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) let common_depth = biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == prev_val.0.as_ref().len() * 2 { + if common_depth == prev_val.0.as_ref().len() * NIBBLE_PER_BYTES { //println!("stack {} ", common_depth); // the new key include the previous one : branch value case // just stored value at branch depth @@ -336,11 +338,10 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) if prev_depth == 0 && !depth_queue.touched(0) { // one single element corner case - let (k2, v2) = prev_val; - let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],prev_depth).encoded(true); - let encoded = C::leaf_node(&nkey.as_ref()[..], &v2.as_ref()[..]); - let ext_len = nkey.len(); - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(ext_len, false); + let (k2, v2) = prev_val; + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],prev_depth); + let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); + let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); cb_ext.process(&encoded_key[..], encoded, true); } else { //println!("fbvl {}", prev_depth); From 1d9be119766ee700f1c7bc3fb2eb3a13a32ea483 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 Apr 2019 12:52:20 +0200 Subject: [PATCH 007/120] Fix merge regression --- trie-db/src/triedb.rs | 17 +++++++++-------- trie-db/src/triedbmut.rs | 22 +++++++++++----------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index cf0fd573..0eae8bfb 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -152,7 +152,7 @@ where { trie: &'db TrieDB<'db, H, C>, node_key: &'a[u8], - partial_key: ElasticArray36, + partial_key: &'a ElasticArray36, index: Option, } @@ -182,8 +182,8 @@ where .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: combine_encoded(&self.partial_key, item), - index: None, + partial_key: &self.partial_key, + index: None, }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { @@ -194,7 +194,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: combine_encoded(&self.partial_key, n), + partial_key: self.partial_key, }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -213,7 +213,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: combine_encoded(&self.partial_key, n), + partial_key: self.partial_key, }).collect(); match (f.debug_struct("Node::NibbledBranch"), self.index) { (ref mut d, Some(ref i)) => d.field("index", i), @@ -255,7 +255,7 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: Default::default(), + partial_key: &Default::default(), index: None, }) .finish() @@ -363,9 +363,9 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { node: node.clone().into(), }); self.key_nibbles.push(i); - full_key_nibbles += 1; - partial = partial.mid(1); if let Some(ref child) = nodes[i as usize] { + full_key_nibbles += 1; + partial = partial.mid(1); let child = self.db.get_raw_or_lookup(&*child, &key.encoded_leftmost(full_key_nibbles, false))?; child } else { @@ -778,6 +778,7 @@ mod tests { } let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + println!("{:?}", &t); assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5)); assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 498ddbda..28db7fe2 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -208,9 +208,12 @@ where children.iter_mut() .map(Option::take) .enumerate() - .map(|(i, maybe_child)| - maybe_child.map(|child| child_cb(child, &NibbleSlice::new_offset(&[i as u8], 1).encoded(false))) - ), + .map(|(i, maybe_child)|{ + let branch_ix = [i as u8]; + // costy + let nibble = NibbleSlice::new_composed(&NibbleSlice::from_encoded(&partial).0, &NibbleSlice::new_offset(&branch_ix, 1)); + maybe_child.map(|child| child_cb(child, &nibble.encoded(false))) + }), value.as_ref().map(|v|&v[..]) ) }, @@ -570,7 +573,6 @@ where }; let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { - // TODO EMCH should key and partial behave similarily : ne key in function?? : seems odd trie.insert_inspector_no_ext(stored, key, value, old_val).map(|a| a.into_action()) })?.expect("Insertion never deletes."); @@ -747,7 +749,6 @@ where }) } - //fn insert_inspector_no_ext(&mut self, node: Node, partial: NibbleSlice, key: NodeKey, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { fn insert_inspector_no_ext(&mut self, node: Node, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -807,9 +808,9 @@ where // append after cp == existing_key and partial > cp trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; + key.advance(cp + 1); if let Some(child) = children[idx].take() { // original had something there. recurse down into it. - key.advance(cp + 1); let (new_child, changed) = self.insert_at_no_ext(child, key, value, old_val)?; children[idx] = Some(new_child.into()); if !changed { @@ -818,7 +819,7 @@ where } } else { // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).encoded(true), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::NibbledBranch( @@ -850,6 +851,8 @@ where // one of us isn't empty: transmute to branch here let mut children = empty_children(); let branch = if existing_key.is_empty() { + // TODO EMCH this condition seems unreachable (see previous if cp < + // existing_key.len()) // always replace since branch isn't leaf. Node::NibbledBranch(existing_key.encoded(false), children, Some(stored_value)) } else { @@ -861,7 +864,6 @@ where }; // always replace because whatever we get out here is not the branch we started with. - key.advance(cp); let branch_action = self.insert_inspector_no_ext(branch, key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch_action) } else { @@ -871,7 +873,6 @@ where // make a stub branch let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); // augment the new branch. - key.advance(cp); let branch = self.insert_inspector_no_ext(branch, key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch) @@ -1297,6 +1298,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let encoded_root = node.into_encoded::<_, C, H>(|child, k| { + // TODO EMCH useless combine encoded call?? let combined = combine_encoded(nibbleslice::EMPTY_ENCODED, k); self.commit_child(child, &combined) }); @@ -1477,8 +1479,6 @@ where let mut key = Partial::new(NibbleSlice::new(key)); let mut old_val = None; - // TODO EMCH notice that here the matching key / partial is not aboslute of invertible (one - // grow the other decrease match self.0.remove_at_no_ext(root_handle, &mut key, &mut old_val)? { Some((handle, changed)) => { trace!(target: "trie", "remove: altered trie={}", changed); From f1fdcbda8777315af3ad48337fa3035298e47c60 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 Apr 2019 15:02:35 +0200 Subject: [PATCH 008/120] No error found : need more tests (after evos). --- trie-db/fuzz/Cargo.toml | 13 +-- trie-db/fuzz/afl_targets/trie_root_new.rs | 14 --- trie-db/fuzz/src/lib.rs | 110 +++++++++++----------- trie-db/src/iter_build.rs | 59 +++++++----- trie-db/src/triedb.rs | 9 +- trie-db/src/triedbmut.rs | 4 +- 6 files changed, 103 insertions(+), 106 deletions(-) delete mode 100644 trie-db/fuzz/afl_targets/trie_root_new.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 8440a733..5812c39d 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -1,4 +1,3 @@ - [package] name = "trie-db-fuzz" version = "0.0.1" @@ -10,14 +9,14 @@ edition = "2018" cargo-fuzz = true [dependencies] -memory-db = { path = "../../memory-db", version = "0.12.0" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.12.0" } -keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.12.0" } -afl = "0.4.3" +memory-db = { path = "../../memory-db", version = "0.12.2" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.12.2" } +keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.12.2" } honggfuzz = "0.5" [dependencies.trie-db] path = ".." + [dependencies.libfuzzer-sys] git = "https://github.com/rust-fuzz/libfuzzer-sys.git" @@ -50,10 +49,6 @@ path = "fuzz_targets/no_ext_insert.rs" name = "no_ext_insert_rem" path = "fuzz_targets/no_ext_insert_rem.rs" -[[bin]] -name = "afl_trie_root_new" -path = "afl_targets/trie_root_new.rs" - [[bin]] name = "hfuzz_trie_root_new" path = "hongfuzz_targets/trie_root_new.rs" diff --git a/trie-db/fuzz/afl_targets/trie_root_new.rs b/trie-db/fuzz/afl_targets/trie_root_new.rs deleted file mode 100644 index e6cea8a4..00000000 --- a/trie-db/fuzz/afl_targets/trie_root_new.rs +++ /dev/null @@ -1,14 +0,0 @@ - -// TODO this is currently borked behind linker sanitizer issues (unordered without gold or multiple -// definition with it). - -use afl::fuzz; - -use trie_db_fuzz::fuzz_that_compare_impl; - -fn main() { - fuzz_target!(|data: &[u8]| { - // fuzzed code goes here - fuzz_that_compare_impl(data); - }); -} diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 02b94b14..f0b5579f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -2,11 +2,11 @@ use memory_db::{MemoryDB, HashKey, PrefixedKey}; use reference_trie::{ - RefTrieDBMutNoExt, - RefTrieDBMut, - ref_trie_root, - calc_root_no_ext, - calc_root, + RefTrieDBMutNoExt, + RefTrieDBMut, + ref_trie_root, + calc_root_no_ext, + calc_root, }; use trie_db::{TrieMut, DBValue}; use keccak_hasher::KeccakHasher; @@ -106,7 +106,7 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { pub fn fuzz_that_compare_impl(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); - //println!("data:{:?}", &data); + //println!("data:{:?}", &data); let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl(data, memdb, hashdb); @@ -119,70 +119,70 @@ pub fn fuzz_that_unhashed_no_ext(input: &[u8]) { pub fn fuzz_that_no_ext_insert(input: &[u8]) { - let data = fuzz_to_data(input); - //println!("data{:?}", data); + let data = fuzz_to_data(input); + //println!("data{:?}", data); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } - // we are testing the RefTrie code here so we do not sort or check uniqueness - // before. + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. let data = data_sorted_unique(fuzz_to_data(input)); - //println!("data{:?}", data); + //println!("data{:?}", data); assert_eq!(*t.root(), calc_root_no_ext(data)); } pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { - let data = fuzz_to_data(input); + let data = fuzz_to_data(input); let mut data2 = std::collections::BTreeMap::new(); let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); - let mut torem = None; - let mut a = 0; - { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - t.commit(); - } -// println!("data{:?}", data); - while a < data.len() { - - root = { - let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); - for _ in 0..3 { - if a % 7 == 6 { -// println!("remrand{:?}", a); - // a random removal some time - t.remove(&data[a].0[..]).unwrap(); - data2.remove(&data[a].0[..]); - } else { - if a % 5 == 0 { -// println!("rem{:?}", a); - torem = Some(data[a].0.to_vec()); - } - t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); - data2.insert(&data[a].0[..], &data[a].1[..]); - if a % 5 == 4 { - if let Some(v) = torem.take() { -// println!("remdoneaft {:?}", a); - t.remove(&v[..]); - data2.remove(&v[..]); - } - } - } - a += 1; - if a == data.len() { - break; - } - } - t.commit(); - *t.root() - }; + let mut torem = None; + let mut a = 0; + { + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + t.commit(); + } +// println!("data{:?}", data); + while a < data.len() { + + root = { + let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); + for _ in 0..3 { + if a % 7 == 6 { +// println!("remrand{:?}", a); + // a random removal some time + t.remove(&data[a].0[..]).unwrap(); + data2.remove(&data[a].0[..]); + } else { + if a % 5 == 0 { +// println!("rem{:?}", a); + torem = Some(data[a].0.to_vec()); + } + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + data2.insert(&data[a].0[..], &data[a].1[..]); + if a % 5 == 4 { + if let Some(v) = torem.take() { +// println!("remdoneaft {:?}", a); + t.remove(&v[..]).unwrap(); + data2.remove(&v[..]); + } + } + } + a += 1; + if a == data.len() { + break; + } + } + t.commit(); + *t.root() + }; } let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); - // we are testing the RefTrie code here so we do not sort or check uniqueness - // before. - //println!("data{:?}", data); + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. + //println!("data{:?}", data); assert_eq!(*t.root(), calc_root(data2)); } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 955d89e1..dbeb9369 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -133,10 +133,10 @@ where // Note: fwiu, having fixed key size, all values are in leaf (no value in // branch). TODO run metrics on a node to count branch with values let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); - // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the - // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes - // in fact or TODO put Vec in the trait? - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); + // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the + // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes + // in fact or TODO put Vec in the trait? + let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); let hash = cb_ext.process(&encoded_key[..], encoded, false); // insert hash in branch (first level branch only at this point) @@ -214,7 +214,7 @@ where #[inline(always)] fn standard_ext( &mut self, - key_branch: &[u8], + key_branch: &[u8], cb_ext: &mut impl ProcessEncodedNode<::Out>, branch_d: usize, is_root: bool, @@ -225,13 +225,13 @@ where let v = self.0[branch_d].2.take(); let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); let branch_hash = cb_ext.process(&encoded_key[..], encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let nib = NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); + let nib = NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); let encoded = C::ext_node(&nib[..], branch_hash); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); let h = cb_ext.process(&encoded_key[..], encoded, is_root); h } else { @@ -242,7 +242,7 @@ where #[inline(always)] fn alt_no_ext( &mut self, - key_branch: &[u8], + key_branch: &[u8], cb_ext: &mut impl ProcessEncodedNode<::Out>, branch_d: usize, is_root: bool, @@ -250,15 +250,15 @@ where ) -> ChildReference<::Out> { // enc branch let v = self.0[branch_d].2.take(); - let enc_nkey = nkey.as_ref() - .map(|nkeyix|NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); + let enc_nkey = nkey.as_ref() + .map(|nkeyix|NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); enc_nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! cb_ext.process(&encoded_key[..], encoded, is_root) } @@ -340,7 +340,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) let (k2, v2) = prev_val; let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],prev_depth); let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); + let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); cb_ext.process(&encoded_key[..], encoded, true); } else { //println!("fbvl {}", prev_depth); @@ -466,7 +466,7 @@ mod test { use env_logger; use standardmap::*; use DBValue; - use memory_db::{MemoryDB, HashKey}; + use memory_db::{MemoryDB, HashKey, PrefixedKey}; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; use reference_trie::{RefTrieDBMut, RefTrieDB, Trie, TrieMut, @@ -493,6 +493,18 @@ mod test { } fn compare_impl(data: Vec<(Vec,Vec)>) { + compare_impl_h(data.clone()); + compare_impl_pk(data.clone()); + compare_impl_no_ext(data.clone()); + compare_impl_no_ext_pk(data.clone()); + } + + fn compare_impl_pk(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let hashdb = MemoryDB::, DBValue>::default(); + reference_trie::compare_impl(data, memdb, hashdb); + } + fn compare_impl_h(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl(data, memdb, hashdb); @@ -502,6 +514,11 @@ mod test { let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl_no_ext(data, memdb, hashdb); } + fn compare_impl_no_ext_pk(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let hashdb = MemoryDB::, DBValue>::default(); + reference_trie::compare_impl_no_ext(data, memdb, hashdb); + } fn compare_impl_no_ext_unordered(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); @@ -586,7 +603,7 @@ mod test { (vec![1u8,2u8,3u8,5u8],vec![7u8;2]), (vec![1u8,2u8,3u8,5u8,3u8],vec![7u8;2]), ]); - } + } #[test] fn fuzz1 () { compare_impl(vec![ @@ -607,23 +624,23 @@ mod test { fn fuzz3 () { compare_impl(vec![ (vec![0],vec![196, 255]), - /* (vec![48],vec![138, 255]), + (vec![48],vec![138, 255]), (vec![67],vec![0, 0]), - (vec![128],vec![255, 0]), */ + (vec![128],vec![255, 0]), (vec![247],vec![0, 196]), (vec![255],vec![0, 0]), ]); } #[test] fn fuzz_noext1 () { - compare_impl_no_ext(vec![ + compare_impl(vec![ (vec![0],vec![128, 0]), (vec![128],vec![0, 0]), ]); } #[test] fn fuzz_noext2 () { - compare_impl_no_ext(vec![ + compare_impl(vec![ (vec![0],vec![6, 255]), (vec![6],vec![255, 186]), (vec![255],vec![186, 255]), @@ -631,7 +648,7 @@ mod test { } #[test] fn fuzz_noext2_bis () { - compare_impl_no_ext(vec![ + compare_impl(vec![ (vec![0xaa], vec![0xa0]), (vec![0xaa, 0xaa], vec![0xaa]), (vec![0xaa, 0xbb], vec![0xab]), diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 0eae8bfb..b40235c4 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -595,7 +595,7 @@ mod tests { (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::<_, HashKey<_>,_>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -651,7 +651,7 @@ mod tests { (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::<_, HashKey<_>,_>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -694,7 +694,7 @@ mod tests { fn iterator_no_ext() { 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::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -769,7 +769,7 @@ mod tests { #[test] fn get_len_no_ext() { - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -778,7 +778,6 @@ mod tests { } let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - println!("{:?}", &t); assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5)); assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 28db7fe2..bdd8492b 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1646,10 +1646,10 @@ mod tests { let big_value2 = b"00000000000000000000000000000002"; let big_value3 = b"00000000000000000000000000000004"; - let mut memdb2 = MemoryDB::<_,HashKey<_>,_>::default(); + let mut memdb2 = MemoryDB::<_,PrefixedKey<_>,_>::default(); let mut root2 = Default::default(); { - let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root); //t1.insert(&[0x01, 0x23], big_value).unwrap(); From bd5b7359355d94a2fa2c66246858e72915ca97d7 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 Apr 2019 20:15:49 +0200 Subject: [PATCH 009/120] Put a no_ext header specific scheme (with second bit as in substrate). And an additional malleability safe guard. Tests involving `Lookup` are currently broken. --- test-support/reference-trie/src/lib.rs | 254 +++++++++++++++++++++---- trie-db/fuzz/src/lib.rs | 2 +- trie-db/src/nibbleslice.rs | 16 +- 3 files changed, 229 insertions(+), 43 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 75f7f845..3ac18726 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -49,6 +49,7 @@ pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, KeccakHasher, ReferenceNodeCode pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodec, Q>; +pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodecNoExt, Q>; pub fn ref_trie_root(input: I) -> ::Out where I: IntoIterator, @@ -71,7 +72,7 @@ pub fn ref_trie_root_no_ext(input: I) -> ::Out A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::trie_root_no_ext::(input) + trie_root::trie_root_no_ext::(input) } fn ref_trie_root_unhashed_no_ext(input: I) -> Vec where @@ -79,7 +80,7 @@ fn ref_trie_root_unhashed_no_ext(input: I) -> Vec where A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::unhashed_trie_no_ext::(input) + trie_root::unhashed_trie_no_ext::(input) } const EMPTY_TRIE: u8 = 0; @@ -92,6 +93,22 @@ const EXTENSION_NODE_OVER: u8 = BRANCH_NODE_NO_VALUE - EXTENSION_NODE_OFFSET; const LEAF_NODE_LAST: u8 = EXTENSION_NODE_OFFSET - 1; const EXTENSION_NODE_LAST: u8 = BRANCH_NODE_NO_VALUE - 1; +mod noext_cst { + pub const EMPTY_TRIE: u8 = 0; + pub const LEAF_NODE_OFFSET: u8 = 1; + pub const LEAF_NODE_BIG: u8 = 85; + pub const BRANCH_NODE_NO_VALUE: u8 = 86; + pub const BRANCH_NODE_NO_VALUE_BIG: u8 = 170; + pub const BRANCH_NODE_WITH_VALUE: u8 = 171; + pub const BRANCH_NODE_WITH_VALUE_BIG: u8 = 255; + pub const LEAF_NODE_OVER: u8 = LEAF_NODE_BIG - LEAF_NODE_OFFSET; + pub const BRANCH_NODE_WITH_VALUE_OVER: u8 = BRANCH_NODE_WITH_VALUE_BIG - BRANCH_NODE_WITH_VALUE; + pub const BRANCH_NODE_NO_VALUE_OVER: u8 = BRANCH_NODE_NO_VALUE_BIG - BRANCH_NODE_NO_VALUE; + pub const LEAF_NODE_LAST: u8 = LEAF_NODE_BIG - 1; + pub const BRANCH_NODE_WITH_VALUE_LAST: u8 = BRANCH_NODE_WITH_VALUE_BIG - 1; + pub const BRANCH_NODE_NO_VALUE_LAST: u8 = BRANCH_NODE_NO_VALUE_BIG - 1; +} + /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 125 and if /// you attempt to do so *IT WILL PANIC*. @@ -108,30 +125,55 @@ fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator(nibbles: &'a [u8]) -> impl Iterator + 'a { - debug_assert!(nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); - let first_byte = nibbles.len() as u8; +enum NodeKindNoExt { + Leaf, + BranchNoValue, + BranchWithValue, +} +/// Create a leaf/extension node, encoding a number of nibbles. Note that this +/// cannot handle a number of nibbles that is zero or greater than 84 + 255 and if +/// you attempt to do so *IT WILL PANIC*. +fn fuse_nibbles_node_noext<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl Iterator + 'a { + debug_assert!(nibbles.len() < 255 + 84, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); + // We use two ranges of possible values; one for leafs and the other for extensions. + // Each range encodes zero following nibbles up to some maximum. If the maximum is + // reached, then it is considered "big" and a second byte follows it in order to + // encode a further offset to the number of nibbles of up to 255. Beyond that, we + // cannot encode. This shouldn't be a problem though since that allows for keys of + // up to 380 nibbles (190 bytes) and we expect key sizes to be generally 128-bit (16 + // bytes) or, at a push, 384-bit (48 bytes). + + let (first_byte_small, big_threshold) = match kind { + NodeKindNoExt::Leaf => (noext_cst::LEAF_NODE_OFFSET, noext_cst::LEAF_NODE_BIG as usize), + NodeKindNoExt::BranchNoValue => (noext_cst::BRANCH_NODE_NO_VALUE, noext_cst::BRANCH_NODE_NO_VALUE_BIG as usize), + NodeKindNoExt::BranchWithValue => (noext_cst::BRANCH_NODE_WITH_VALUE, noext_cst::BRANCH_NODE_WITH_VALUE_BIG as usize), + }; + let first_byte = first_byte_small + nibbles.len().min(big_threshold) as u8; once(first_byte) + .chain(if nibbles.len() >= big_threshold { Some((nibbles.len() - big_threshold) as u8) } else { None }) .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) } - pub fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE }; + let bm = branch_node_bit_mask(has_children); + [first, bm.0, bm.1] +} + +pub fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { let mut bitmap: u16 = 0; let mut cursor: u16 = 1; for v in has_children { if v { bitmap |= cursor } cursor <<= 1; } - [first, (bitmap % 256 ) as u8, (bitmap / 256 ) as u8] + ((bitmap % 256 ) as u8, (bitmap / 256 ) as u8) } /// Reference implementation of a `TrieStream`. @@ -159,13 +201,14 @@ impl TrieStream for ReferenceTrieStream { fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator) { self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); if let Some(partial) = maybe_key { - self.buffer.extend(fuse_nibbles_node_no_ext(partial)); + // should not happen + self.buffer.extend(fuse_nibbles_node(partial, false)); } if let Some(value) = maybe_value { value.encode_to(&mut self.buffer); } } - + fn append_extension(&mut self, key: &[u8]) { self.buffer.extend(fuse_nibbles_node(key, false)); } @@ -181,6 +224,63 @@ impl TrieStream for ReferenceTrieStream { fn out(self) -> Vec { self.buffer } } +/// Reference implementation of a `TrieStream`. +#[derive(Default, Clone)] +pub struct ReferenceTrieStreamNoExt { + buffer: Vec +} + +impl TrieStream for ReferenceTrieStreamNoExt { + fn new() -> Self { + ReferenceTrieStreamNoExt { + buffer: Vec::new() + } + } + + fn append_empty_data(&mut self) { + self.buffer.push(noext_cst::EMPTY_TRIE); + } + + fn append_leaf(&mut self, key: &[u8], value: &[u8]) { + self.buffer.extend(fuse_nibbles_node_noext(key, NodeKindNoExt::Leaf)); + value.encode_to(&mut self.buffer); + } + + fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator) { + if let Some(partial) = maybe_key { + if maybe_value.is_some() { + self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchWithValue)); + } else { + self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchNoValue)); + } + let bm = branch_node_bit_mask(has_children); + self.buffer.extend([bm.0,bm.1].iter()); + } else { + // should not happen + self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); + } + if let Some(value) = maybe_value { + value.encode_to(&mut self.buffer); + } + } + + fn append_extension(&mut self, _key: &[u8]) { + // should not happen + } + + fn append_substream(&mut self, other: Self) { + let data = other.out(); + match data.len() { + 0...31 => data.encode_to(&mut self.buffer), + _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), + } + } + + fn out(self) -> Vec { self.buffer } +} + + + /// A node header. #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum NodeHeader { @@ -190,6 +290,14 @@ enum NodeHeader { Leaf(usize), } +/// A node header no extension. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum NodeHeaderNoExt { + Null, + Branch(bool, usize), + Leaf(usize), +} + impl Encode for NodeHeader { fn encode_to(&self, output: &mut T) { match self { @@ -202,6 +310,33 @@ impl Encode for NodeHeader { } } +impl Encode for NodeHeaderNoExt { + fn encode_to(&self, output: &mut T) { + match self { + NodeHeaderNoExt::Null => output.push_byte(noext_cst::EMPTY_TRIE), + + NodeHeaderNoExt::Branch(true, nibble_count) if *nibble_count < noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize => + output.push_byte(noext_cst::BRANCH_NODE_WITH_VALUE + *nibble_count as u8), + NodeHeaderNoExt::Branch(true, nibble_count) => { + output.push_byte(noext_cst::BRANCH_NODE_WITH_VALUE_BIG); + output.push_byte((*nibble_count - noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize) as u8); + }, + NodeHeaderNoExt::Branch(false, nibble_count) if *nibble_count < noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize => + output.push_byte(noext_cst::BRANCH_NODE_NO_VALUE + *nibble_count as u8), + NodeHeaderNoExt::Branch(false, nibble_count) => { + output.push_byte(noext_cst::BRANCH_NODE_NO_VALUE_BIG); + output.push_byte((*nibble_count - noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize) as u8); + }, + NodeHeaderNoExt::Leaf(nibble_count) if *nibble_count < noext_cst::LEAF_NODE_OVER as usize => + output.push_byte(noext_cst::LEAF_NODE_OFFSET + *nibble_count as u8), + NodeHeaderNoExt::Leaf(nibble_count) => { + output.push_byte(noext_cst::LEAF_NODE_BIG); + output.push_byte((*nibble_count - noext_cst::LEAF_NODE_OVER as usize) as u8); + } + } + } +} + impl Decode for NodeHeader { fn decode(input: &mut I) -> Option { Some(match input.read_byte()? { @@ -214,6 +349,30 @@ impl Decode for NodeHeader { } } +impl Decode for NodeHeaderNoExt { + fn decode(input: &mut I) -> Option { + Some(match input.read_byte()? { + noext_cst::EMPTY_TRIE => NodeHeaderNoExt::Null, + + i @ noext_cst::LEAF_NODE_OFFSET ... noext_cst::LEAF_NODE_LAST => + NodeHeaderNoExt::Leaf((i - noext_cst::LEAF_NODE_OFFSET) as usize), + noext_cst::LEAF_NODE_BIG => + NodeHeaderNoExt::Leaf(input.read_byte()? as usize + noext_cst::LEAF_NODE_OVER as usize), + + i @ noext_cst::BRANCH_NODE_WITH_VALUE ... noext_cst::BRANCH_NODE_WITH_VALUE_LAST => + NodeHeaderNoExt::Branch(true, (i - noext_cst::BRANCH_NODE_WITH_VALUE) as usize), + noext_cst::BRANCH_NODE_WITH_VALUE_BIG => + NodeHeaderNoExt::Branch(true, input.read_byte()? as usize + noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize), + + i @ noext_cst::BRANCH_NODE_NO_VALUE ... noext_cst::BRANCH_NODE_NO_VALUE_LAST => + NodeHeaderNoExt::Branch(true, (i - noext_cst::BRANCH_NODE_NO_VALUE) as usize), + noext_cst::BRANCH_NODE_NO_VALUE_BIG => + NodeHeaderNoExt::Branch(true, input.read_byte()? as usize + noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize), + + }) + } +} + /// Simple reference implementation of a `NodeCodec`. #[derive(Default, Clone)] pub struct ReferenceNodeCodec; @@ -261,6 +420,21 @@ fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { output } +fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { + let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; + let mut output = Vec::with_capacity(2 + partial.len()); + match node_kind { + NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + }; + if nibble_count % 2 == 1 { + output.push(partial[0] & 0x0f); + } + output.extend_from_slice(&partial[1..]); + output +} + // NOTE: what we'd really like here is: // `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` // but due to the current limitations of Rust const evaluation we can't @@ -386,13 +560,15 @@ impl NodeCodec for ReferenceNodeCodecNoExt { fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; - match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { - NodeHeader::Null => Ok(Node::Empty), - NodeHeader::Branch(has_value) => { - let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; - let nibble_count = take(input, 1).ok_or(ReferenceError::BadFormat)?[0] as usize; + match NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)? { + NodeHeaderNoExt::Null => Ok(Node::Empty), + NodeHeaderNoExt::Branch(has_value, nibble_count) => { + if nibble_count % 2 == 1 && input[0] & 0xf0 != 0x00 { + return Err(ReferenceError::BadFormat); + } let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) @@ -410,8 +586,10 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } Ok(Node::NibbledBranch(nibble_slice, children, value)) } - NodeHeader::Extension(_) => unreachable!(), - NodeHeader::Leaf(nibble_count) => { + NodeHeaderNoExt::Leaf(nibble_count) => { + if nibble_count % 2 == 1 && input[0] & 0xf0 != 0x00 { + return Err(ReferenceError::BadFormat); + } let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -425,15 +603,17 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn is_empty_node(data: &[u8]) -> bool { - ReferenceNodeCodec::is_empty_node(data) + data == &[noext_cst::EMPTY_TRIE][..] } fn empty_node() -> Vec { - ReferenceNodeCodec::empty_node() + vec![EMPTY_TRIE] } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { - ReferenceNodeCodec::leaf_node(partial, value) + let mut output = partial_enc(partial, NodeKindNoExt::Leaf); + value.encode_to(&mut output); + output } fn ext_node(_partial: &[u8], _child: ChildReference<::Out>) -> Vec { @@ -450,25 +630,18 @@ impl NodeCodec for ReferenceNodeCodecNoExt { partial: &[u8], children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { - let mut output = Vec::with_capacity(partial.len() + 4); // TODO choose a good capacity estimation value (here it is only partial) - for _ in 0..3 { - output.push(0); - } - - let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; - output.push(nibble_count as u8); - if nibble_count % 2 == 1 { - output.push(partial[0] & 0x0f); - } - output.extend_from_slice(&partial[1..]); - - let have_value = if let Some(value) = maybe_value { - value.encode_to(&mut output); - true + let mut output = if maybe_value.is_some() { + partial_enc(partial, NodeKindNoExt::BranchWithValue) } else { - false + partial_enc(partial, NodeKindNoExt::BranchNoValue) }; - let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child.borrow() { + let bm_ix = output.len(); + output.push(0); + output.push(0); + if let Some(value) = maybe_value { + value.encode_to(&mut output); + }; + let bm = branch_node_bit_mask(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -479,7 +652,8 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } None => false, })); - output[0..3].copy_from_slice(&prefix[..]); + output[bm_ix] = bm.0; + output[bm_ix + 1] = bm.1; output } @@ -501,7 +675,7 @@ pub fn compare_impl + Eq> ( for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } - t.commit(); + t.commit(); t.root().clone() }; if root != root_new { @@ -524,7 +698,7 @@ pub fn compare_impl + Eq> ( } assert_eq!(root, root_new); - // compare db content for key fuzzing + // compare db content for key fuzzing assert!(memdb == hashdb); } diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index f0b5579f..b61c6352 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -107,7 +107,7 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { pub fn fuzz_that_compare_impl(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); //println!("data:{:?}", &data); - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl(data, memdb, hashdb); } diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 7bf81712..94b14e0d 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -21,6 +21,16 @@ use elastic_array::ElasticArray36; /// Empty slice encoded as non-leaf partial key pub const EMPTY_ENCODED: &[u8] = &[0]; + +/// mask for nibble encoded first byte for extension +pub const NIBBLE_EXT_MASK: u8 = 0x00; + +/// mask for nibble encoded first byte for leaf +pub const NIBBLE_ODD_MASK: u8 = 0x10; + +/// mask for nibble encoded first byte for leaf +pub const NIBBLE_LEAF_MASK: u8 = 0x20; + /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// /// This is an immutable struct. No operations actually change it. @@ -166,7 +176,8 @@ impl<'a> NibbleSlice<'a> { let l = self.len(); let mut r = ElasticArray36::new(); let mut i = l % 2; - r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); + r.push(if i == 1 {NIBBLE_ODD_MASK + self.at(0)} else {0} + + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); while i < l { r.push(self.at(i) * 16 + self.at(i + 1)); i += 2; @@ -180,7 +191,8 @@ impl<'a> NibbleSlice<'a> { let l = min(self.len(), n); let mut r = ElasticArray36::new(); let mut i = l % 2; - r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); + r.push(if i == 1 {NIBBLE_ODD_MASK + self.at(0)} else {0} + + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); while i < l { r.push(self.at(i) * 16 + self.at(i + 1)); i += 2; From e727fbd0d418f253057539985a6359a98cb3778c Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 Apr 2019 20:51:27 +0200 Subject: [PATCH 010/120] Fix decoding issue of iter --- test-support/reference-trie/src/lib.rs | 7 ++++--- trie-db/src/triedb.rs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 3ac18726..e18a2b53 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -365,9 +365,9 @@ impl Decode for NodeHeaderNoExt { NodeHeaderNoExt::Branch(true, input.read_byte()? as usize + noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize), i @ noext_cst::BRANCH_NODE_NO_VALUE ... noext_cst::BRANCH_NODE_NO_VALUE_LAST => - NodeHeaderNoExt::Branch(true, (i - noext_cst::BRANCH_NODE_NO_VALUE) as usize), + NodeHeaderNoExt::Branch(false, (i - noext_cst::BRANCH_NODE_NO_VALUE) as usize), noext_cst::BRANCH_NODE_NO_VALUE_BIG => - NodeHeaderNoExt::Branch(true, input.read_byte()? as usize + noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize), + NodeHeaderNoExt::Branch(false, input.read_byte()? as usize + noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize), }) } @@ -560,7 +560,8 @@ impl NodeCodec for ReferenceNodeCodecNoExt { fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; - match NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)? { + let head = NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)?; + match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { if nibble_count % 2 == 1 && input[0] & 0xf0 != 0x00 { diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index b40235c4..6a76823a 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -417,6 +417,7 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { let node_data = &self.db.get_raw_or_lookup(d, &self.encoded_key())?; let node = C::decode(&node_data) .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; + println!("a node data end"); Ok(self.descend_into_node(node.into())) } From b3c988c66c0a0c8fd8a10c2612587ddc086d8370 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 Apr 2019 21:06:11 +0200 Subject: [PATCH 011/120] Reset fuzzer to right no_ext impl, found a failing test (in comment). --- trie-db/fuzz/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index b61c6352..c71d1a08 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -136,20 +136,24 @@ pub fn fuzz_that_no_ext_insert(input: &[u8]) { pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { let data = fuzz_to_data(input); + println!("data{:?}", data); + // TODO debug this failing: +//data[([0], [0, 251]), ([0], [251, 255]), ([251, 255, 79, 59, 42, 34, 0, 0, 0, 255, 255, 255, 255, 255, 255], [255, 0]), ([255, 0, 0, 0, 91, 0, 0, 0, 0, 82, 0, 59, 136, 136, 0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0])] + let mut data2 = std::collections::BTreeMap::new(); let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); let mut torem = None; let mut a = 0; { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); t.commit(); } // println!("data{:?}", data); while a < data.len() { root = { - let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); for _ in 0..3 { if a % 7 == 6 { // println!("remrand{:?}", a); @@ -180,9 +184,8 @@ pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { *t.root() }; } - let mut t = RefTrieDBMut::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. - //println!("data{:?}", data); - assert_eq!(*t.root(), calc_root(data2)); + assert_eq!(*t.root(), calc_root_no_ext(data2)); } From bdd99c0c5b8bddd6345687aa4b8baa279e5cd739 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 Apr 2019 13:40:09 +0200 Subject: [PATCH 012/120] Find and fix fuzzing error (a missing branch child ix). --- test-support/reference-trie/src/lib.rs | 51 ++++++++++++++++- trie-db/fuzz/src/lib.rs | 77 +++++++++----------------- trie-db/src/iter_build.rs | 47 ++++++++++------ trie-db/src/triedb.rs | 1 - trie-db/src/triedbmut.rs | 16 ++++-- 5 files changed, 115 insertions(+), 77 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index e18a2b53..e9545720 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -560,7 +560,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; - let head = NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)?; + let head = NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)?; match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { @@ -636,7 +636,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } else { partial_enc(partial, NodeKindNoExt::BranchNoValue) }; - let bm_ix = output.len(); + let bm_ix = output.len(); output.push(0); output.push(0); if let Some(value) = maybe_value { @@ -796,6 +796,12 @@ pub fn compare_impl_no_ext( } t.root().clone() }; +/* { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + }*/ + if root != root_new { { let db : &dyn hash_db::HashDB<_,_> = &memdb; @@ -860,3 +866,44 @@ pub fn compare_impl_no_ext_unordered( assert_eq!(root, root_new); } + +pub fn compare_no_ext_insert_remove( + data: Vec<(bool, Vec,Vec)>, + mut memdb: impl hash_db::HashDB, +) { + let mut data2 = std::collections::BTreeMap::new(); + let mut root = Default::default(); + let mut a = 0; + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + t.commit(); + } + while a < data.len() { + // new triemut every 3 element + root = { + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + for _ in 0..3 { + if data[a].0 { + // remove + t.remove(&data[a].1[..]).unwrap(); + data2.remove(&data[a].1[..]); + } else { + // add + t.insert(&data[a].1[..], &data[a].2[..]).unwrap(); + data2.insert(&data[a].1[..], &data[a].2[..]); + } + + a += 1; + if a == data.len() { + break; + } + } + t.commit(); + *t.root() + }; + } + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. + assert_eq!(*t.root(), calc_root_no_ext(data2)); +} diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index c71d1a08..96902b79 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -7,6 +7,7 @@ use reference_trie::{ ref_trie_root, calc_root_no_ext, calc_root, + compare_no_ext_insert_remove, }; use trie_db::{TrieMut, DBValue}; use keccak_hasher::KeccakHasher; @@ -54,6 +55,28 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { result } +fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { + let mut res = Vec::new(); + let mut torem = None; + let mut a = 0; + for (a, d) in data.into_iter().enumerate() { + if a % 7 == 6 { + // a random removal some time + res.push((true, d.0, d.1)); + } else { + if a % 5 == 0 { + torem = Some((true, d.0.clone(), d.1.clone())); + } + res.push((false, d.0, d.1)); + if a % 5 == 4 { + if let Some(v) = torem.take() { + res.push(v); + } + } + } + } + res +} pub fn fuzz_that_ref_trie_root(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); @@ -136,56 +159,8 @@ pub fn fuzz_that_no_ext_insert(input: &[u8]) { pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { let data = fuzz_to_data(input); - println!("data{:?}", data); - // TODO debug this failing: -//data[([0], [0, 251]), ([0], [251, 255]), ([251, 255, 79, 59, 42, 34, 0, 0, 0, 255, 255, 255, 255, 255, 255], [255, 0]), ([255, 0, 0, 0, 91, 0, 0, 0, 0, 82, 0, 59, 136, 136, 0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0]), ([0], [0, 0])] - - let mut data2 = std::collections::BTreeMap::new(); - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let mut root = Default::default(); - let mut torem = None; - let mut a = 0; - { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - t.commit(); - } -// println!("data{:?}", data); - while a < data.len() { + let data = fuzz_removal(data); - root = { - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); - for _ in 0..3 { - if a % 7 == 6 { -// println!("remrand{:?}", a); - // a random removal some time - t.remove(&data[a].0[..]).unwrap(); - data2.remove(&data[a].0[..]); - } else { - if a % 5 == 0 { -// println!("rem{:?}", a); - torem = Some(data[a].0.to_vec()); - } - t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); - data2.insert(&data[a].0[..], &data[a].1[..]); - if a % 5 == 4 { - if let Some(v) = torem.take() { -// println!("remdoneaft {:?}", a); - t.remove(&v[..]).unwrap(); - data2.remove(&v[..]); - } - } - } - a += 1; - if a == data.len() { - break; - } - } - t.commit(); - *t.root() - }; - } - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); - // we are testing the RefTrie code here so we do not sort or check uniqueness - // before. - assert_eq!(*t.root(), calc_root_no_ext(data2)); + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + compare_no_ext_insert_remove(data, memdb); } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index dbeb9369..cb72ea48 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -515,6 +515,8 @@ mod test { reference_trie::compare_impl_no_ext(data, memdb, hashdb); } fn compare_impl_no_ext_pk(data: Vec<(Vec,Vec)>) { +// let memdb = MemoryDB::<_, HashKey<_>, _>::default(); +// let hashdb = MemoryDB::, DBValue>::default(); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl_no_ext(data, memdb, hashdb); @@ -529,9 +531,10 @@ mod test { let hashdb = MemoryDB::::default(); reference_trie::compare_impl_no_ext_unordered_rem(data, rem, memdb, hashdb); }*/ - - - + fn compare_no_ext_insert_remove(data: Vec<(bool, Vec,Vec)>) { + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + reference_trie::compare_no_ext_insert_remove(data, memdb); + } fn compare_root(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); reference_trie::compare_root(data, memdb); @@ -543,9 +546,6 @@ mod test { reference_trie::compare_unhashed_no_ext(data); } - - - #[test] fn trie_middle_node1 () { compare_impl(vec![ @@ -666,17 +666,28 @@ mod test { (vec![0],vec![0, 0]), ]); } -/* #[test] - fn fuzz_noextrem1 () { - compare_impl_no_ext_unordered_rem(vec![ - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - (vec![0],vec![0, 0]), - ], &[(0,4)]); - }*/ + #[test] + fn fuzz_noext_ins_rem_pref () { + let data = vec![ + (false, vec![0], vec![251, 255]), + (false, vec![0,1], vec![251, 255]), + (false, vec![0,1,2], vec![255; 32]), + (true, vec![0,1], vec![0, 251]), + ]; + + compare_no_ext_insert_remove(data); + } + +/* #[test] + fn fdispc () { + let data = vec![ + (vec![0], vec![251;32]), + (vec![0,1], vec![251; 32]), + (vec![0,1,2], vec![251; 32]), + ]; + compare_impl_no_ext_pk(data); + panic!("dd"); + } + */ } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 6a76823a..b40235c4 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -417,7 +417,6 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { let node_data = &self.db.get_raw_or_lookup(d, &self.encoded_key())?; let node = C::decode(&node_data) .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; - println!("a node data end"); Ok(self.descend_into_node(node.into())) } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index bdd8492b..b4fc500f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1027,7 +1027,9 @@ where // replace val if let Some(val) = value { *old_val = Some(val); - Action::Replace(self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix())?) +// key.advance(cp + 1); + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); + Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) } @@ -1162,11 +1164,16 @@ where (UsedIndex::One(a), None) => { // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); - //let new_node = Node::Extension(new_partial, child); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &combine_encoded(&key, &enc_nibble))?; + // TODO EMCH optimize this concat (new_partial_tmp may be calc again afterward) + let new_partial_tmp = NibbleSlice::new_composed( + &NibbleSlice::from_encoded(&enc_nibble).0, + &NibbleSlice::new_offset(&[a], 1) + ).encoded(false); + + let handle = self.cache(h, &combine_encoded(&key, &new_partial_tmp))?; self.storage.destroy(handle) } }; @@ -1298,12 +1305,11 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let encoded_root = node.into_encoded::<_, C, H>(|child, k| { - // TODO EMCH useless combine encoded call?? + // TODO EMCH useless combine encoded call?? (as no encode but partial) let combined = combine_encoded(nibbleslice::EMPTY_ENCODED, k); self.commit_child(child, &combined) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); self.hash_count += 1; From ac36fc957cf25a44640bf1081efab60f17cd90f8 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 Apr 2019 14:26:10 +0200 Subject: [PATCH 013/120] Removing bench blobs. --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/benches/bench.rs | 93 +++++++++++-------------- trie-db/testset0 | Bin 5120 -> 0 bytes trie-db/testset1 | Bin 10240 -> 0 bytes trie-db/testset2 | Bin 102400 -> 0 bytes trie-db/testset3 | Bin 204800 -> 0 bytes 6 files changed, 41 insertions(+), 54 deletions(-) delete mode 100644 trie-db/testset0 delete mode 100644 trie-db/testset1 delete mode 100644 trie-db/testset2 delete mode 100644 trie-db/testset3 diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index e9545720..50f4e4c6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -263,7 +263,7 @@ impl TrieStream for ReferenceTrieStreamNoExt { value.encode_to(&mut self.buffer); } } - + fn append_extension(&mut self, _key: &[u8]) { // should not happen } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 080e7812..7ba1e013 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -24,7 +24,7 @@ criterion_main!(benches); extern crate trie_standardmap; extern crate trie_db; -use std::io::Read; +extern crate rand; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; use trie_db::NibbleSlice; @@ -52,10 +52,10 @@ fn nibble_common_prefix(b: &mut Criterion) { fn root_old(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - input("./testset0"), // 286 vals - input("./testset1"), // 571 vals - input("./testset2"), // 5649 vals - input("./testset3"), // 11376 vals + input(1, 5120), + input(41, 10240), + input(18, 102400), + input(29, 204800), ]; c.bench_function_over_inputs("root_old",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| @@ -73,10 +73,10 @@ fn root_old(c: &mut Criterion) { fn root_new(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - input("./testset0"), - input("./testset1"), - input("./testset2"), - input("./testset3"), + input(1, 5120), + input(41, 10240), + input(18, 102400), + input(29, 204800), ]; c.bench_function_over_inputs("root_new",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| @@ -94,49 +94,32 @@ fn root_new(c: &mut Criterion) { ,data); } -fn fuzz_to_data(fp: &std::path::Path) -> Vec<(Vec,Vec)> { - let mut file = std::fs::File::open(fp).unwrap(); - let mut input = Vec::new(); - file.read_to_end(&mut input).unwrap(); - let mut result = Vec::new(); - // enc = (minkeylen, maxkeylen (min max up to 32), datas) - // fix data len 2 bytes - let mut minkeylen = if let Some(v) = input.get(0) { +fn fuzz_to_data(input: Vec) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let minkeylen = 1; + let maxkeylen = 32; + let mut ix = 0; + loop { + let keylen = if let Some(v) = input.get(ix) { let mut v = *v & 31u8; v = v + 1; - v - } else { return result; }; - let mut maxkeylen = if let Some(v) = input.get(1) { - let mut v = *v & 31u8; - v = v + 1; - v - } else { return result; }; - - if maxkeylen < minkeylen { - let v = minkeylen; - minkeylen = maxkeylen; - maxkeylen = v; - } - let mut ix = 2; - loop { - let keylen = if let Some(v) = input.get(ix) { - let mut v = *v & 31u8; - v = v + 1; - v = std::cmp::max(minkeylen, v); - v = std::cmp::min(maxkeylen, v); - v as usize - } else { break }; - let key = if input.len() > ix + keylen { - input[ix..ix+keylen].to_vec() - } else { break }; - ix += keylen; - let val = if input.len() > ix + 2 { - input[ix..ix + 2].to_vec() - } else { break }; - ix += 2; - result.push((key,val)); - } - result + v = std::cmp::max(minkeylen, v); + v = std::cmp::min(maxkeylen, v); + v as usize + } else { break }; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix + 2].to_vec() + } else { break }; + ix += 2; + result.push((key,val)); + } + result } fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { @@ -147,8 +130,12 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { m.into_iter().collect() } -fn input(file: &str) -> Vec<(Vec,Vec)> { - let pb = std::path::PathBuf::from(file); - let data = data_sorted_unique(fuzz_to_data(&pb)); +fn input(seed: u64, len: usize) -> Vec<(Vec,Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + let data = data_sorted_unique(fuzz_to_data(data)); data } diff --git a/trie-db/testset0 b/trie-db/testset0 deleted file mode 100644 index e07b5593b038b5f226301500f2d7fa4b10a09c4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 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ü~xDeq;tf*G6?2Ha%S*2x}@q{4e%wqND%$s&ff!_Qt3KmpZiKs+1o^=jwg+rw^QC}IJzdY%e6eT}CbuWrSrO7>d z6THlA014ssqsNzLe2G$Es*Z#f&6}JJhO(?yNGZc4h-LH3+~>EOoKLzjE3x_ugqgc@ zei2MUonc+Kc^|5ErovDM4nAJSQW@?89o+QruXi8|qD^tGrH0aKzm%UB;ZdwHrMOcA z%Xt3U7k)DI6J`Va&EgjK7uxI_rq>NqCdGJOsxN9#lj}Oayi|?!$)H&>&AHLHj2u2D z!|EU=CNZ7ppR>NwtTGMhJFHgcoU1B7Krx>qn3qJT7(=N`d5oT^36Rl}Fp#fu4 zBx&)_s6mE{ezW+iX@BigJ=(w~U#%J$e^}~hH|wnB1BU(#Ds!yG9Xz#w?Ele{bA9t~ zmgHeqc#zzlME zxHbpkkP6#sk-2DDn0NTnoD|z0aG0DX#_VTM+T$BXqsva=lovoR&0c9PnE+iQP6`%E zJWz{{@Ol(kiDb?QOay(Ajyl>+kIf{Hh~4=kL_uhk>!3@e4ENNAaQbzrL5hL8Biuor@+?W3|vj4AZr}4R)iu0v6Hu)$_A`tt$t*st#MJ(?{ GCN^AE%RDpy diff --git a/trie-db/testset2 b/trie-db/testset2 deleted file mode 100644 index babe3d8be3f82419817441cb504ca843d300a94d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102400 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ü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 z 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�c@TUJ9*ARfSlO|SE&JdE|oI<8S-a3VOLJ(w;ie$?(NUI zN*bT7$>xeUlLO=Dm44j?NcHeh(ra&c4&zyA zJH1La8yWq5nswVqFKz|53w6o!5t+%(kWAd%yn3Sy2r?@^fDhFSY*?T<(X9}t!v4h{ zk<^-D7>CcZfxLzN=RxF}A6zKY5yd<4V!E7{1q!-5; z4VtZcwS7j1v2-hIO5Mu7yO)PMzrk^+SPIQ$s|%SvkUS9Vd2iRXGClD=zLVqxrF?oK zkF48xGB?<44bmP>mE(w6f>dRdwRDWJ*0O3LI)QKBD3q;8q;u35n})zJeFoI$P%cWZ zrM}Za;kDr-=uNPf&1>(3j>!~6-fJ)$yojOoXwNxB*0hiTBB9%M6&Z@dETvkhFi-CB zBOxHPhQ-}cGc4i?aCyMZWx62{8IEKOS6vG2GzOs2b%y^@k_?<0?fDHs9JOvwCEUCu z-AHmQ%7mR%tdEHW<3GhiXPnC^WIj~2Jf3VJ;JN$QIO+Z0O|0}qx+-f2-nG z@($l5v@g78QCf`;c6J|~m?p_cHM{ypoa)d0WioNu3VBXH)yy59@P!vfd|*qSOqYMH zo))hN``5)=w0zX8Ox#~ZJTFNsq)A*Z)q)A?>(Z@9M*R8OO793ep;>qRH|*5gtght0 z{(%T`4noy1v&LU9)$wQF7p+q5bq;T)v%dix_L$;_GH?oakd5pn^iBIEN0`$UPh7^W z#wKXVM9b8vo5TPf1vLvchP8oqqD`bEM2FDfbHxMO4n&yeQh~M7rIcCSA5TIK^j?d` zgr*WfRwer2!e!MbpW|s>!hInF&s{+b`VEup?EpL2puFX>8;*Hnt$)-n(7LqP)Hu3ZY8vF=M7yY zrM4hT=k}*o*{#eGw78eoY3%Bg@@{7_gq=&mM5;+DL*{@n=Xp^uVCJpq6^X?u_c~jo zWHYW10k>7{mgFp;xD&X;Iif_19Cb|Nqr%EO5~Ams5JxnS3xHq2I^*ef^2D=*n})dP z#dxz@u_!fg4`TuMw$l;XarEyyt45!HqC;WOkQ# z$*FQ_xZiAgD&CSw6WZ(1kYs(?3AL42qHPDvKlm4kuY;2w7y1ac@HrFtuTd;X*>Ttz z&?I;9<$BV*)1cJ?XB~l62nzM5tBj))u^e#_1()x>=?G5W$!hC;yytCeK#fEhn_JCl zD>J2GOkErj!8;FEA1WIhDs{AJ-B z2?DYiaALhtK}c`7l_6>RZF6+4-Y{y*vw1j>(IJC{l7Q=uv`Z^qw-qIb?WXQzH9uMR z<)AzI;(fciAiC_Db3HsH1A%k^aIplsCGh)P*ptLAVL>Q2xQ)orsHiUevsot|+=PvJ z^M*h?-AF7#&B(r2B_zvXi{cpSb$%`1zL7VM;i^*pekkQ5Z+ORoVCe061F()5caLE` zv2))e#n&7fY8l5OI^aW;jI#jZN(HYP&5EgMmM2i^C z$fS(jVo+j=az5|SC>GR=WBBQRKEu`n4F2m9D5h3i%9{5U8wVa&=|u#uBiA~Kkw?s< z^N2dYM|0t_!EdWLd~%NCdv0&10r$##r88VGBHqet!i)uRj>JOoNaSR(aawZv!XSQA z=lh{DN~p+&+0R@|ssCC#wmqyjKp`$ScTH(7WQMro5)Y(tD9N!)>_1=7kg_^(lv2jQ zECPrwO()BKGUcSf32WE%l(d38CxB-8f4D$oH443Qe>&#M zec(b_3bw4~l*3v^&QpEEb#yESySA*B&wx5qt?x|I;`oOBrp0BfChD%^Dyl?91Dmq+ zO;c(7=6f^jS9LE}&M6qrcF1GV1&6NeskA$u$n_24YRD9@GdfT;RS_$csZ~tvMMIw8 zAqB$^(V8AY`y9&0`?bqND26kr==@<9t7T|u4y>4wAd0F~>pwJKK?LHg=6k=uV&_aT zTm?DXXn1gEJdfm|vn251!`#KZ`)HNn?m{DIAGouiKW0nB`XB{eMoj{%V#>YVgbOXx ziLPBYwYkNJu>L+7L+M0)LLLfpMd^#T?i3Y;kQs$GnIJ6U%W21lmxBW{c6fG>7u8H4J#5lj6-Az9k9JLA@6&)@Ok_%1wP2N5VGPYs; zMzlS=b*?cC_V)&6@hq+?a3TJnqXG?=v;}(~h4L~}W6q2wkSmQFL9ZPkh*zH9m*8@M z3*JOKXDF8@AR3osYVVc=YtkChtGeb`^4Hwko z;Cg>i#zuK!&v=R|ePa(xm$P99(qhMO%D(Q6)HH36#m{8m9WEb>Jc@lPIES|0;UrZ zv%XAjy8rD4RSHN{YkTbLzWyknvFzrx07!W~=>d-cJaP*hcvTXYvJ@bC?X5R{+dQz| zkH;Djeu7(Z1OTotQK$npSe{ISHe?QFtX5Y+mEDMw&V-Ja4b2L})eV`Zy(7|9BtqI1*ig}nTh!fere91J%;J+dvA98S*AWC{9fio%Ag4guLsvz0NTeD%GmnCsRUlb%X z4b2981D*L-&jfq{d4haZk6Hpn{|<5d@!~m>hdrRC5JS0Vb4P8TANj0_#YvcN?){Z? zQoncfs|A?W70X#I2{zc*U8#LD1E*YF*G1a;%Tdm*SZ-bFRr9oaq3+BBVz%>rz|DfK zRcwR>Jh%6yJ}6g`fN=fjDZYJwC50V7Q>PIt%b9qc6eXE(ge1x5bPST9e5O<)-qP}n z>CiL>Mc72{mRg;X~q^?mBP z1RGzY;Cb!kyk>#!n1)3aAH~!irN`xa8`yv|+_@DatQF;z>_0L2a4iBfInH06;nR>v zM|WjP@GEX)w4Q4=mFag6U94mya8|CjjKHZ!lW`@D4GeSso#^+rKxqBKgky_}LGQ$SQXKBKY*Oo2ZkClx^Al230DwGoFNnBeiR(kfc8?#>$UDmo<| z*h>AeBH40*ywT%IoR>6pMl;Tp#RjGE;1OStn01lY2?wBAv3V`Rt%&>%H*_M5dnH^wKt zIHH+V(LtlpX^qrmvDJeOYg9L2H#7F9AvSp2LZuUKxpa%scUy;F*6xy&6%b2Vx?R=V z(dvh>&|T)MQm1ebSNVRY;g-GJKc@g;+XtCDU2~$w%jj}{MJkZPM_P}DhT#=~5)f7h zKY67*(bQj?)A~;XmJWoqyy@9)79?8`IPt0#u}4Ph=bALqJ)(h?2ZxR5;Xke;QmtJZ zkYer$PY>UTtZ#d*;bCp?;u5vV%K#v$@%+CQ`!ez8PrS8BfB2iSAQu`6(<+yxT zUcmKP8Ft3)NXR{kR6S^S7WAT@pQE$Wvapk1Z}eKkojSym+U*rZ=ktfZp25<$qR#AS zgIhXm1l5}I^cgO5J>s8j3^gEI&qLa-pK8lku}Em};$!Q2PbZZ11ipb4WZyj0oB5JVz!D(Z6Py(%sijn z$43HZ{u9)q`(dG=8Oax{r%a#cpOTir4(@fwOtcBN6ESRhoy2Y$o0T*LVqdIauxO6N zRpr}i$$dZu>1X2_7M;4gC2>;+xZ55ef=RK}zFBx}!>qGT>rm0`6g}c8Te5M$Lvo6ptY3+rodCE3#?EPa!)y>p&h(gKPca@l*?Q@6R#GD|L z$*K5?H1M_-#(Z^05rk7PdswWNU6CT%E;w3WnY$Uh!8T+Cf0&PlTtHVANap#6(xiLl zf!Nj|Qh8TL28`6uJte?I#&ol_#Y2l!pR^=?8Z&7`Z-Ov?hRi8Tb9-R>n;cURvIWx9 zpk^t!2EFs4O{T6~SAcyWK&+dKBK*NQA9VD9b;e6tNmg_LD(_*Yk=X7i_C< zJi)_leO?H>1e2qCN2n>3)imXZk(=*08)Zy^se5Aqyc+wtDcntk7Lg3E^7|Dm{h#yL zKs3+0<>4s4W059&ex}Yb-gCrH#;j4-tIvX_%VY%jjY$$RDNOVkH%eIhN7lJFjS*+2 zfzHA{vL$6>8Jqrhu$OEFcpIF}f<9Q99_9vk4`c8%+xXzkNnt%czodzSHIJ==?TVe* z@((10#fP1A@EfgEIDP~!Elfp+fu>S>K;VF|duM%DjvLeD{{g&~1 zh74+_kAetqwaByy+32{ z>v0j6Pk%Dtt6h4=1{hY|;av+{bPq6+SNS+w85mJb8LD8UgMXYtGTSUyvcu>PO2|TY zb#bPXjw2!+Li5rcsW$L8tRRv08sr%WW=6Xvrv#=TAP=%6O9UpR?BHTgXBFu4T{2vc zDtB~E^Mqa8Te@Y!g;q+w!GtwWHj)Vp=9TrMZ^_T^ZTE9Sr1Q?9jpl6QZ3K^@NABdV zCJH8wYJH-_@@QqJWzVZvvef53UUnJo3?Gqbi1m&&sS8x*Me|o_ZZ?b2}Owv3rSWr?8A=O`i zPo~`Md-|viL|-UG+zBIqn}&%A6QZm%LRYD@fEQ$dwh`oMN|VtE_CnIluK(B{Cpi$P zAt5}MQ`nIaZf)ez%S(O!uCvuiEY-oo-O5ih4xPP^tl zlt+!TeQ>mYv~#jUP{o1gZ9Rlod`SL&#o5|m1@yZdo<>sO&?Rt|QLLYMct8Nb+yfOO z;U~{;P0tVnum9Rd_kZan8Mpn|6LDQRlz`O#KYGQyLKH0^9fGg^$|T%0g^)XSZpkIe zGvYGYbpISTpm5n@?1ahR+&97gNw8~g_nti8J=bRp{a zifJHBZFqYFxh5`VMtmgP3QZd9=RHb-6Fw#(NYFUAWnWuY!Ht+ET<0mxjt- zsjcqPz5~4Hek|$#AXY*a*{5y?vW})jJWCDnd9zlKxKu&V+5j$D3DkxR|HPbB(3iCg zlZli0t)06jnizL!{QKhH9f~8A^`MW(_w<>4>3EtPN89Lo?$AClN|Pc}gM&yOOC>nNk% z=hMN!HkgBhb2EBJe|IVjd!G5i*5-=q#$kuI+srM=Fp1rU8KXdf9G%bQne_fq&H&cG zdI`#yeIOBhR-L*H8cqA_P1I|XYPV(@c(7^2&I zxC!mG!Ywir20quu>d3NFMWHrpF{72>DK!n%UfpOX~${ri) zw_(B~{b?>6IfV-DzVu1B^1K88p1* zXatjMUvduqA?mnQr+ocY8BLebgyiC3+AKeooP)`Q=|t|?c(a_`EV?A>Fj8eL2~xGZ z_+f}H)O=>W};v5YMwBg!RtU47}jc3V9t{JzvHl%h@ z&h;T~Vk;-u{sR%j*AaoYgQSMZZCBL@x5JoLl(}1e+y-o`v1Z|T12eLPqo|fj; z<>tM@JTB@#8!CZcAOU>*7&dt_hFcW z_Ly?!i|@!6I{WA-@(CD;nwID%&z76(0a_VeWTW_K>OLIOa0!^y<~CKqAp>syncGKc z*XF`yDBHpN+6?{x{A~PLkAbnw95L^u|MFTW2u>Ku>x7^fEuJC>ud#)Xb##aO7W@GT zs&9B7WP*?2D(-NzRiHMWH8frBMe1-H0M%pw;m4{Rs0l1yHn!f1a6<{TwhECo`go+Y zLR13nY4ZM}EhWJ1?-^OV<_cI(qPqwA+aWri9rE7xd;#W5fW)#36ponwZkh;d!A@@j zuU}XsxLOVYaQvMdF%LmviL|K$j$xe4SKM^BWwd7aIe7PHkx8MjM9w&Oe7o(``NC z_oFAy+3_w<(`mJG?x%N~6E0QUd0)oDd@>v+&`gi<O3UA(qggVd*R;+KVx_L0>FOuD1J61x z!cR{{yLK06WIkN?FJS)ajVCOiI?XLbv}c@gB{6xTJj$Q)Ia+8uAff}=pMY8eKAwO& zisxDDK_AQiOJhwwZ}Jw%hDNYyrjEFhMb|ytf^E8vht*&Zit_K@KM6It@N1*3RYM3gr zGXercI_5qu(t1j-X*h%J0*ihhZFc^GmQv3Eml}Qj6pZl=Dt?`q%m4n%obduuEPfx5 z{{a@uuTtvGJOPG=6nZF*F-V5mU$&3yv<^Vl5(Sw$1pPZX++>dO$A~;LO!{Yee_Y09 z+lifeLad(9k?q?i>?ZE(JRSGl$1xZ>#SDwnM&FIIRJ%8kbb;6`BP0=DO;0Ij%;GI@ zH<72@-;I0HT}v?YpLVp(?3qQ}l#wc@soMkbAHUk~%WIh71(r<{_k51{0l9>e$#^mS z`TZGRwIZl-3Hhkj1#vfe zFG5&TC#J+;f=lv&2)&TUpU{7-xuXPCxwY^xePG8QifB;N->tC6wQ(b+mi}uIL?9G{ zL^BI`+yj5MtJcq8 z*9cH1YjEO2r>0#zt>nVAeLHU(Ba}{YtRxikT_P?^*u+Q;645Kl+uMIUulQMK(VHn( z+Q9)S`szRF`GPTmbBqo6irt?qd}kc5EFG!`JjrVNMEUSMNh`2w{IO8FcN2O~L<`RT zc(+;iqq7^VSOIX=pJ>-QeOeI_ti5xu*P~N8tCb*e^#`=Z$U{nEH=&YHDg0CTLg)O! zbAR1z=~&j`rTghU5b~cTKF%pub9c@N)7%xVBw_5O$Cmb`rGfy`m$xJ{alJW6K>^76 zI!s28UzFWUh5U`w5hHfPKA1!?Z8|e~?pn9L%Gr5Lu;g?$8Xa?n)NY?>zeqTrjqXIj zR@Sr`2=<>ci6lTR#1dAVAMEYU)l^wQVNYDBJlWwYd!^rcHL`i+7w|x=LQ#x`R4$7V z_h2hP!>Nvi4MHZW)roR_8wY{MdQ(>U63pw)2P^}wBDmhs(d{MhbuGphCBnY7J6Z@; zdqY=^RaG+<^)(?B1m*NYfbnJySztRC+4!W?_ir2E!Wh=#g4$jR`Kvk-5jvsAHfbS; z6m#q+ofRlmbld#Ykuxi$Z>lwW?770qUnR$$_{+otBJ^Gz6Z9FeM`AS-%0LT9Ez3L3 z9-w_1JNU9~$^^}!P_T!>q5wuJ4Z3h$@cb1wN6!&ZxzGIh^oD``EJnZ<2dU4(o`FLd zrWOXrFog!ew#*IGoPw$GfhR3>Cg~cpxDL=R<6>awy6FjZy_GEBxY0&=6Z$S5{qXsT zRCp}vL5gQH%``Eu+*G<*U_VDek{$^Z`?KAw-JK;`PyeJsG;1R6zUKVa^r4t%31g89 z7sZZ`duh)~1Mm!f!q)#&I-hCZd)R0A4;sQz*?a{xsoE#qb+_3BB$bP3l~HF`#Pndj zzB2n&WWzwyL8MT!7}N1kS&>2YpU|jsJWyTU3D4_fpBm<#P)>5G_~Z5}SC-Ddz*Jnj z^+02*;y1*`W`u0a=V6YNn-J+e7|^$ZiJ0SRveHj|%onq<4$EdhNh(A?dJ6cn6e_3` z%ZT|zDAv1wVmrA$0_`ms=`)m8flu`Vx)8O7KQ2i1W!9+WJ{F!ate}VAcWi!a30k=q zOGNHf6HV)m>Hj6Q(H>{r&Bs2(Mr6Si+gA+r`~+#Ur+Jz%B-o;0ev%>h-1<%=NvlO+t)=cj8X6(6-$ARZIG1S)f!q(ebP0@FKE zc2*C}epgtPgyy~?6@*#-*z%%}vp@Gsh&|F&DvLiZfMj}&&Qu}vSPiqtt7JDuIKK+r zQ-dgu$mBGqd-qbioxy*+*FwItu;OnlA|`bArD!B9EgAnvNsc`H!A0RF3HE>U1CBAE zH`+(MgXg)>RC4TAb2^hlNj^9Eg{(IGwIix zrFbcL!2Ae9@oqmYo15_r8hK$mxZQawk-oLE1d7@%i|%1si{IYWH+yzc&a{wOqIg)o zMWULU5Y?O&uw}uUJVQn9$)S)cLdzTekvCBVsdAyTW z`4u21ZT$yy9Ejfs6F)pEyO+qwvB2Jv6jpBCg{4`DUwn8ZnZ>oq6!|ukJZ_MM$6ze7 zCKUmxFPx**X3F_@~Z3W6b@p%ay6>zO_5ZkQdN`SVIK}2QqStK(lTk!=3%G%+L}d<^>j>$KA7vVr z5_ced-DZZ6cGc1f!n3<>aI&|}P)qo01n<=|al-9Ml;xVEywbc@hK5*?ybF*hKyS;b ztoR@`m2K7<*>VJ0008%a?>!*#gkR#)Y>WZ*@E3m`vgs=C#>f2#%OTPi5>ot!I~S?t z&jOPcWmJuvLB_jht(DIvjm4#=^t`G}#23`CI}~G}e1R~wxXFnnY}ydc`(lYPpBHab z`@@{X_@iaNJeWsYIaZho7wlC=@h;m#;#y3jJa)sv;WA~tJ&r+=3QU{f5{NvpLC_b} zM8nv_CMA`a3Tnlq@5!k=2n@r;~4j=F&_lKW2i{F_r^e2%y zOiBk1!saQQ1F-Kvaw({u8DuXnaA%l!q{|m0Tyh z==7WMJ9k9~g>3#rlfj0ecqw`dVC`CBhu{OEsd2VTuU{d&>xwS}ABIKY|6E$D|2F#{ zUrIp4`{HtWnOQ;t*S_;xfokH=LfL2;#)qr_;H*`cbu1@Io5R6D3=9)RLO!ksb40rU9 zcR8u^p8<9C&hu4=AC*weMPba`&-xnVPZ%H!O*L#6nu-Ew;fSmTdkUiH$Q9@0PB%g{ zm04jGp6)H#Ac$dKD=m7mpUj^A>13%j*ciYXqm==rsGJ3(=>MTd{Xsk9hjJ^Fa0Dgj zSC7tM{?p~8_cKsRxSCg#E4gNjs7P2T!9m-BCO$SX%V{$l08W2}P~ibn$_qqXobxCT zs5p!Kp>7+SxVnMW!rYUvlN^MzR3)|67-+qA&F-p#z7w-PO$ySTm8bK7EH`!fz}dq& z*PQVDu1KPJFqFK!@<#a2C`7~=me$&oq`$B|s_kW^9a;>NbC>-!d?tn)dCj86m1)Vn z$k;Q`=!>=dDO@3Lt!@>~R_>kl(~SlNXbl74ydmT9%3hyWnef?{%rlvT20_!9r1@gFpTj(osM11cHr6h*3w8{HzcU;KKJn3@Jr<1+X zNj221B1-%z@Kz%0hG+LrT(9PZWDP~R_PMbY)8P1aZAf=+#?-UC!-@oIC21rL2f+xX=If|(#Vag2w0vs8 ze~q7o-R=6ZgrSe>%)|?BJGsd{nY7f$>yLrqbYhqS=#Gxwt)B5FR=v0FB9B=>(>th9#eMnAg9 zSlmV5mNLug!;3DE_zAAZBB!bCC7`oHfbG=tc|A+<90_(mfyr|)4!tbH;`;^4%I<8#G3$Q_-X-Q@9Wf# zK?8jN+!b3!@PTgdQq{fIaOrV-@$=7aas`eQhb0KTwH4CZYkh04QDM1w6rs4ph|xyM zM50P63UsIpIbtVq@$?hgU-s2gcJYIt!IE)%I2@J9@b01`{qzU0lU*CPgh8otg{=+s zWT>JG6N8bF`86Rkdmd%=Fc9$R?O``Pi; zqTu1&APiEj@AHAd$gOc>v!ihg0K!_@*E`v`^3LOGo0TV|o}V1HpI(SeHAP$q}zuor6H@cm?oH72`MBxQYYgO7pXlvph0Vv^B|K^_w+l01?*dzIZXU`X-;l zX=GMjk@zW!ohhwPa`tcOy4$kIQ|UIZ$lpMzba@7qnXe|k*sSOtOfK2j_2Ix?eR4Jq zL$-jY@$hgt77f_ufLkLGf|uh1qJ#pw+;#3q)!+5D+MnAIfZQb%4FHxrPqLXG<8~Sc zXpxeo6DaX%XY7FVqE!>-l0?Cd0WcRUweH_&%kVF+Tk24wi87Hx$-{F~$z2)1j`OTB zrTrIl>g?_%I>r7#pH)VV5&@!57LStB+Cz@*D=&$aqLHDM?*9XCn}3c69$cf4#G>s# zDn4=P&8YlLoey1-rXG0qhAM>D{_rf4;yFZ5Z~mi6Q+4g*_BRab9Rx4u2}nhwXUP?8 zlmKrcjoKFE>R37Drq^{Aq{-y=xu8*}vfxI*ZZ?wz8Rn_Ita#8zgqF;oYldx3c_erfGZ&Mq0R>sTb3A#Gz-MU8?$o~R zukIf@M{|{D5riV7$w9pdmtZS)IOy zGU)H}-tsSUT}yt%@u0)+)dX_0C<&&}KAp<^2PjE<#6c-a!*sd1)-g_6bQ21U0AeI2 zX(sIuq%HFdpHXwKahy^8YOe#bJ5y{wmjxJE2(c}omkt`ff~tErh#5cgI`aag=I7-3 zkVk))RfPkT!03T1>(kapX*6CEZya}mEBQ!*CCZ*EPvKs*<>g$3bL7Blx<`y2T&wCx zq{df6dPc^H>1CU{m-x!RD8Qxqj(?L-d06YcCAzRyU&fH}d-Z#~c?MF-cyek^$i(aM z{4JU#StxK(v1A^9lY#N#l$Wl<=+ar;8v-#cOdvQNBW;H4bEfV(fmYXhY|4{%1=%yO?KI#lb32`mriyr1)It&4RFar`)If>_MFe0t%Zt&Z}72_3-ff+t36 zr$jv*ht)<3=ccIZUmEk&lQUT6pI!sE!th&ZIXM(tGj8cD@WiRz#Df)EoZMv1La#1! z(6`jzPcF6ibCTy)V^cGsq1oFx>6x{{(zHqrjink%fa5@T*-u;dQ96W3IVAqHg01!H zf#NDQ@&du14WXZ0zb@I!oh2(Zh@WtziKzi=@BX#q(=c*b-OC z?~sK5B>_l66)ugHP%jeK0pv9tsc@4&f090P9~HzG0!Q~d>r1Xy60=jwvjiMHdRu9}8ku-eA^O)OqH ztiD0e9^ZPQ@KG3~#6}0K7H^Af#SsWJ3{&lRTYE7j^zkY#Znoi{I;l~Er9<%-{O&W0 zJL>*RSO+!-uB}rncw++%Rn{Og6r@npOuzS&^P%pA-kgwRY19KCWpOi_u@BXB^-CSU zeb}2bB?WSJG+Z!_)ket+F*&Qx;KaVv9b$h+3;>*wkHtI^8|Qu!qx=?Wofm6PeUB&d zLNJPBmujvUIkqnwCzsh1@*$jP(tG>tnP2N&HPT|s1^tgmr|2SKWUj&B zVEGzeZRdPfcnMaW#dIj&3jqo6%ss&gpye66cHcFj>OSV0w!&(TZt-yu#q*h6tGX?p z26-5ebx0d}4cT4MILL#b5B1)7GBuMrDjR}j{++mpgGkG@c6_;=IEmtpq`AU4wbR=p zPc&bWnLZt=N7Sdz#td19uH&;}m#z2Mr62-`v_UxTFMalLfcx$v^1?=GnzC5g>7!mE z@PACGJ&V?hlLgFDK}DSzpc8UQXaF^n!?Ot{SXXU7TRW??Mbr1^5g9f>4Ti}+W_Ucc z%t3Ao=y*NuivgztIo~-{SD64uGcUR;E0`jvrD0^nvXTgBF%MktUAulcA(@WJ(`YZF z@qjO?=hwecHDkU8n7`l-Dk2N7PuCg0FR%|-a?e~Y_TPL;2lvjG0A3uQBD?t*a)HlG zJV=Woh^JO^6_HK$NUkRQ>hCCC#*9_^hjD~|9Qi=yk$r;*o>0!$gUh#BjvxPfEP^5p z;dh<=K#ZRfF|l;6eLb28@6Eoy zT)TFe=o*?n7cm89FRKV{oaOczJY$wFTzeveVZBaFYArmzg0}lJ6{TI7q0Io*R{3dK z^!oABzTvt{dteiM>XCTjynBrJIcK5#RfnB3#Y=O!HT7?5qPuBvES=qQQ z=7zC6k?bS~G-`VJt;Pl^A9IGUmfb!b$nRmf*0&!>R ztmV;CAm~x2dx<*~rfhs_S|tFM_0|61As*sA=6m-g9EEhi_dnZBgh}Cc*Z+xH0;g(R zbekY*1U8WU<@mWdL~$cYHnx$MI)AqrdcvCGxYCPN%03UJ#Uc4aTb?dYc>cdJ_-MX} zT=GLQQ>P-kwybUD0={w^OG|5)3}Qzd8L^VPJ4{UBvlkGlSWh^#reN})&PqQP&Arij zzrU9`o{^^U>oG7=7yl1Wg5}>Al$z%ZkqBFo6@*mNqccr6gIhuZuspd!v+ej zWx#js-fj`y0%t4Yg|>l@R@CN2MWmKD=B<8b=w%HD04DQBBBuTl8%zpA1?OUGfIv@w zXq$}kA(&R@a477+f$=Y%0YO{kMLTW%AzjVCR##zFBd@vx^M5n5(N05+6zhaDG5g;$ z)vv@=4+plwLI*l1pTEM?r_z!ZhU?wc4xfBCXA{^xd)S%qRH}!@KHVI%=3Dmo)XB+<@uJ?KR_C$wR&k47d=SIeyV}iZ{6^Bhs$Du zox|2zUN%%!MK_m?oTGDS17ynninYfg8+;E~K2A8lz|w_X$YMTTLIo7qW9iI;@>+&LELY z0QVPkpGDH~M=a3u$vlXM2tlC#j}>XQV$<8RY6qW#$s5S9izLV{D2Q;;=Mq_NmlAsT z%vvO?l5P2Qs+#x2{DovH#NT~#bQK5d-7bdL#=6d_Lh;?J5dmd>vi@kn-d2J(Y|DdX zntI|B4F}~iMSp)!NX*v%>bJtF0)l6iq7tlDR^Fv>%O|)C3zW{ybet5tI~l%n?gPHZsqb}6`~xm_ z`4Vql-*YfaBK5))9xy@I@k^TB%iwqWZLL=S*pRe%Bh?VJ&}0|6@?jzfleQp!FcZ0b zpzQtXM`sc*`MU>d1XCqYy0QQtqM&w z1JwH9?qt_p44{-1dO`7bo2WYS&Y<>rTv|8eTfdB5OpDDOTfM6W`B)!EHA2#x{To*6 zUk1BKa^N+Ma77h`_CbNO;Ln=lM_<{>ZPQNFVgj3sJ@rG~PPf7lXI0P6TqKwwhidI{ zdK13CYfOic$s5bxj8VCs>T#l*;>gOi!YBO1hJ($_zd6vqu_^wuQ&wlD-m*M~vXO*6 z?;noFt*kxO{L$=G;Fzy}BP z_-ZRaQ&>Md(|eS6#i#8S@3ONTOidZq?N4W(Vt)e!1O z2HzmKJud;3lKp!ziT~Uoi*Y4*g@e->vSGfxKo^jsaT%CVA;%dtshinno%ZY$187ou z^+BBpn4cT&pYtPc+xov9UF=q8?BcnGo_pDG=dw~>f&>{V!{!=%xJ7FakNPg}^sll5 zknXca*vT)ijO%n;NZ$w2ajEOyVzsRgLDA;G5y_a4_O;AaR&$q%Sm> zzikz*JnjBd3LVGRdm>dz0(8p0&Iby@GbxvqQU^;jH$Z2OeKD9BC=pSYH5~DYF2q8r zayrF60t-BmlVH*184ppi_FrpMcNs$vMI%EAlhT{LhfDfrrXGZ{Oz_Q&b#gWk>DJetE>*g*WoIUPg9)(OU_g$y)v$Rd&g4_K<&P zGZWWbl{CG&3NXppR7ZEfP1Qiz{v1GNnXX?|#|3mfb713j4rn0R4LuW&*zqfQ-i-m6 zhdSWTChm0^zlL$fEDV7XDP}$r6D}iuxMkiZk4CNURrNmA=G#d{-&wBuX!`?G!c>zT zO7^Op^9o2UoPf{=;X}bdm2@Y3Te`qypgZVmr?$J3n@-K9Z)+mwx%S;O0avCSJ!!J^ z4t1t>WGS!hbWogQG2w7n>jru{VYk`vl9PvLat@Xe{~3b|H%G}Za6#N;vohug8T?Q^ z#k#&tu&PyGEP3I~ILU(H(>$~O!k$kU)n76y36VXVbsiC9=`8?~vgi*q`;7lmi;`8~hcd60#$ua+esw>x zuia2_K!yTS;vW7xcT&^}RvGFFFUYltn8N4!E0Vv&IgQ|2Q*WF&S9DDJkc)vTV(7}d z-GQT`_j|B0ch_;~5@I&;ctvJvy_DwlmEB=(hL3N)qc?OcjW=2wp^E`O}LdY{4NJ=kOCZ?=!Oi;&ktAb@}?Y|9_| z@~-cVd}ZUiV6G@#cf{od!B5rZ$%o46t9pKLp6lwLWYUoP~_6Mm@zY{;X7 zwtBnb=+41?pDK=2{ooIuMSq?&Bv&3JM=QxL6@OExF=K6-|Ln7dMZwdoVqskn5X$LH1&vJ3HnZSH)rzB}kOA9~L`1sWp z8;u^qP^)zcmMI^^y&#ZVBOHTzJu|KYeR#vxEeTj`1i+h*I$xlM=!U5)L;_1`>_%A? ziRo{F0=ny)(vY*92J_XrBrx=N8SnwrdCa4ciq6Vj6l=8%y(`y=uvq(db&h`APUQQs znww-lY~~>v}NnW!4b6F zDKQ+&aFZmdm!?5kj{;9*37=RbX1*oiY|czEeOW-8{27M?6E%arvey>vGZWLDwD>Y? z=brn31xf&?LDc4b$y@mJCi(db4}W@j(#-uEm2ZrGf-PoCtI?P#R=MV&j7R>%fBUvM zTSm@+rZr8+pV`UNq0Jx`MP?ziSJfQ~Fr`uzUH!uU#MbUK!JkrdOAyd(eCoIyr2Ko= z1XQe?Wzk`mG$>sE;SxFI7G!`vI;;)c{?9tFtqdPZiQH$Yt4#P|%*7p7tOT z93|DxR{+^p5vcfJK#*d!qBG2ue5UQkewE=WS(_9cVQjfneCWlk z{k+&S0o(}dK`^f_ExeMaNWYW00HwXl2l_%BKB8KaroZ4SwM$~ccVoUc2#9vbgp$No zbYPzo0=TzqWZY3s!fQXJmUcu4mvT+{L}D<>b!5&YaonAeS})7?A~h-R-U%Xm6LyZZ z9}zutz#qHWsOs=HEtX;wgH34v%p8201D95bFITuVfe+0zz8SPxh(jHZ*l4M{szDBWY65D;#gvwYEj0R=YT*l^3L%`0El|<56ZJE zF?4IIW?o5tMnXT*8zxQ4Rb8FTb5iA=!^JxateZu*=q&SA<@7ES3O~;cGgur5TNU~LtiFVRAl_qQwc@|AnLel<^p9gA(}APNQ&(FP#yUo-I&#<%iuA4C(^ zZ>>rp8mh`K<(Dcoz0~#m4dd8+ssF$si#N>TS z3uDVnpgsd-MiY@Dm2C9JHh8H+VT6rRKyNK!f8G2-W_P#z(Dh`+&IZ1e9K#MazXYHeMX$5NFh{f#jFozS#TzfD?A- z_&iq@7Q&%O{PJ7bu_(CM`chp}4ar& z-;$iLuJGZ$G=?gvd}NyJV1g`1<^m*CqF#-|`{9=G(}tn)MelemL<5mA%S%o=K_F|; A^8f$< diff --git a/trie-db/testset3 b/trie-db/testset3 deleted file mode 100644 index d4bd3537b5412fc8f8b78714fe9de0f0b9d02309..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204800 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ü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 z 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�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 q8|R9`9X!@)g)er10=a2z8@BD#cglL Date: Mon, 8 Apr 2019 11:22:37 +0200 Subject: [PATCH 014/120] Minor change (remove dead branch, and useless encoding, also panic on key to long). --- memory-db/src/lib.rs | 47 ++++++++++---------------- test-support/reference-trie/src/lib.rs | 23 +++++++++++++ trie-db/src/iter_build.rs | 25 ++++++++++++++ trie-db/src/triedbmut.rs | 21 +++--------- trie-root/src/lib.rs | 2 -- 5 files changed, 71 insertions(+), 47 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 07f87765..03c44f61 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -116,43 +116,32 @@ pub struct MemoryDB null_node_data: T, _kf: PhantomData, } -// TODO rem just for quick check of calc_impl + impl PartialEq> for MemoryDB - where + where H: KeyHasher, KF: KeyFunction, - >::Key: Eq + std::fmt::Debug, - T: Eq + std::fmt::Debug, + >::Key: Eq + std::fmt::Debug, + T: Eq + std::fmt::Debug, { - fn eq(&self, other: &MemoryDB) -> bool { - /*for a in other.data.iter() { - println!("ok{:?}", a); - }*/ - for a in self.data.iter() { - match other.data.get(&a.0) { - Some(v) => { - if v != a.1 { - println!("{} {}", self.data.len(), other.data.len()); - println!("val {:?} \n{:?}", v, a.1); - return false; - } - }, - None => { - println!("{} {}", self.data.len(), other.data.len()); - println!("key {:?}", a.0); - return false; - } - } - } - true - } + fn eq(&self, other: &MemoryDB) -> bool { + for a in self.data.iter() { + match other.data.get(&a.0) { + Some(v) if v != a.1 => return false, + None => return false, + _ => (), + } + } + true + } } + impl Eq for MemoryDB - where + where H: KeyHasher, KF: KeyFunction, - >::Key: Eq + std::fmt::Debug, - T: Eq + std::fmt::Debug, + >::Key: Eq + std::fmt::Debug, + T: Eq + std::fmt::Debug, {} pub trait KeyFunction { diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 50f4e4c6..0ddbc434 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -422,6 +422,9 @@ fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; + + assert!(nibble_count < noext_cst::LEAF_NODE_OVER as usize + 256); + let mut output = Vec::with_capacity(2 + partial.len()); match node_kind { NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), @@ -660,6 +663,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } + pub fn compare_impl + Eq> ( data: Vec<(Vec,Vec)>, mut memdb: X, @@ -907,3 +911,22 @@ pub fn compare_no_ext_insert_remove( // before. assert_eq!(*t.root(), calc_root_no_ext(data2)); } + +// TODO define how this should be handle: +// panic does not look really good: +// Either redesign encoding trait for for errors +// or bound it (currently the code overflow 255 to +// 0 and truncate). +#[should_panic] +#[test] +fn too_big_nibble_len () { + // + 1 for 0 added byte of nibble encode + let input = vec![0u8; (noext_cst::LEAF_NODE_OVER as usize + 256) / 2 + 1]; + let enc = ReferenceNodeCodecNoExt::leaf_node(&input, &[1]); + let dec = ReferenceNodeCodecNoExt::decode(&enc).unwrap(); + let o_sl = if let Node::Leaf(sl,_) = dec { + //assert_eq!(&input[..], &sl.encoded(false)[..]); + Some(sl) + } else { None }; + //assert!(o_sl.is_some()); +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index cb72ea48..c4c93192 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -678,6 +678,31 @@ mod test { compare_no_ext_insert_remove(data); } + #[test] + fn two_bytes_nibble_len () { + let data = vec![ + (vec![00u8],vec![0]), + (vec![01u8;64],vec![0;32]), + ]; + compare_impl_no_ext(data.clone()); + compare_impl_no_ext_pk(data.clone()); + } + #[test] + #[should_panic] + fn too_big_nibble_len_old () { + compare_impl_h(vec![ + (vec![01u8;64],vec![0;32]), + ]); + } + #[test] + #[should_panic] + fn too_big_nibble_len_new () { + compare_impl_no_ext(vec![ + (vec![01u8;64 + 255],vec![0;32]), + ]); + } + + /* #[test] fn fdispc () { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index b4fc500f..0058100d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -171,8 +171,6 @@ where }, EncodedNode::NibbledBranch(k, encoded_children, val) => { let children = dec_children(&encoded_children, storage); - // TODO slice currently encoded as extension: makes sense to replace bit id - // TODO this bit info does not seem to be use anywhere -> remove decoding code?? Node::NibbledBranch(k.encoded(false), children, val.map(DBValue::from_slice)) }, } @@ -850,18 +848,11 @@ where // one of us isn't empty: transmute to branch here let mut children = empty_children(); - let branch = if existing_key.is_empty() { - // TODO EMCH this condition seems unreachable (see previous if cp < - // existing_key.len()) - // always replace since branch isn't leaf. - Node::NibbledBranch(existing_key.encoded(false), children, Some(stored_value)) - } else { - let idx = existing_key.at(cp) as usize; - let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); - children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); + let idx = existing_key.at(cp) as usize; + let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); + children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) - }; + let branch = Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None); // always replace because whatever we get out here is not the branch we started with. let branch_action = self.insert_inspector_no_ext(branch, key, value, old_val)?.unwrap_node(); @@ -1305,9 +1296,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let encoded_root = node.into_encoded::<_, C, H>(|child, k| { - // TODO EMCH useless combine encoded call?? (as no encode but partial) - let combined = combine_encoded(nibbleslice::EMPTY_ENCODED, k); - self.commit_child(child, &combined) + self.commit_child(child, k) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 64c61fa6..8227cad2 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -182,8 +182,6 @@ fn unhashed_trie_inner(input: I, no_ext: bool) -> Vec where stream.out() } -/// TODO avoid code redundancy with unhashed_trie -//#[cfg(test)] // consider feature="std" pub fn unhashed_trie_no_ext(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, From 84ff90d603e4db7ab6ff9e28d55a9afcd97a4f23 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 9 Apr 2019 14:31:12 +0200 Subject: [PATCH 015/120] Use a unique trait for spec (layout) : hasher, nodecodec and alogorithm specific constant that will be use for merging code of NoExt with previous one. Furthermore could be use in multitrie implementation (no constant use but fn ). Currently only done for Mut (non mut will wait to see if merging goes well). --- test-support/reference-trie/src/lib.rs | 50 ++++-- test-support/trie-bench/src/lib.rs | 39 ++--- trie-db/src/fatdb.rs | 2 +- trie-db/src/fatdbmut.rs | 57 +++---- trie-db/src/lib.rs | 59 ++++--- trie-db/src/nibbleslice.rs | 4 + trie-db/src/node_codec.rs | 2 + trie-db/src/recorder.rs | 2 +- trie-db/src/sectriedb.rs | 2 +- trie-db/src/sectriedbmut.rs | 52 +++--- trie-db/src/triedb.rs | 24 +-- trie-db/src/triedbmut.rs | 217 ++++++++++++------------- 12 files changed, 272 insertions(+), 238 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 0ddbc434..feecde8b 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -37,17 +37,41 @@ use trie_db::{ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record, TrieLayOut}; pub use trie_root::TrieStream; +#[derive(Clone,Default)] +/// trie layout similar to parity-ethereum +pub struct LayoutOri; + +impl TrieLayOut for LayoutOri { + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodec; + + fn uses_extension(&self) -> bool { true } + fn new_codec(&self) -> Self::C { ReferenceNodeCodec } +} + +#[derive(Clone,Default)] +/// trie layout similar to substrate one +pub struct LayoutNew; + +impl TrieLayOut for LayoutNew { + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodecNoExt; + + fn uses_extension(&self) -> bool { false } + fn new_codec(&self) -> Self::C { ReferenceNodeCodecNoExt } +} + pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodecNoExt>; -pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMutNoExt<'a, KeccakHasher, ReferenceNodeCodecNoExt>; +pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, LayoutOri>; +pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMutNoExt<'a, LayoutNew>; pub type RefFatDB<'a> = trie_db::FatDB<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, KeccakHasher, ReferenceNodeCodec>; +pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; +pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, LayoutOri>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodec, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodecNoExt, Q>; @@ -663,7 +687,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } - +// TODO fuse with other layout pub fn compare_impl + Eq> ( data: Vec<(Vec,Vec)>, mut memdb: X, @@ -676,7 +700,7 @@ pub fn compare_impl + Eq> ( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, LayoutOri); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } @@ -718,7 +742,7 @@ pub fn compare_root( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, LayoutOri); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } @@ -794,7 +818,7 @@ pub fn compare_impl_no_ext( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } @@ -836,7 +860,7 @@ pub fn compare_impl_no_ext_unordered( let mut b_map = std::collections::btree_map::BTreeMap::new(); let root = { let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); b_map.insert(data[i].0.clone(),data[i].1.clone()); @@ -879,13 +903,13 @@ pub fn compare_no_ext_insert_remove( let mut root = Default::default(); let mut a = 0; { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); t.commit(); } while a < data.len() { // new triemut every 3 element root = { - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root, LayoutNew).unwrap(); for _ in 0..3 { if data[a].0 { // remove @@ -906,7 +930,7 @@ pub fn compare_no_ext_insert_remove( *t.root() }; } - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root, LayoutNew).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. assert_eq!(*t.root(), calc_root_no_ext(data2)); diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index f21d4379..dee40d92 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -28,7 +28,8 @@ use criterion::{Criterion, black_box, Fun}; use keccak_hasher::KeccakHasher; use hash_db::Hasher; use memory_db::{MemoryDB, HashKey}; -use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut}; +use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut, TrieLayOut, TrieHash}; +use std::default::Default; use trie_root::{TrieStream, trie_root}; use trie_standardmap::*; @@ -45,33 +46,33 @@ impl ::std::fmt::Debug for TrieInsertionList { } } -fn benchmark, S: TrieStream>(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) +fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) where - ::Out: 'static + ::Out: 'static { let funs = vec![ Fun::new("Closed", |b, d: &TrieInsertionList| b.iter(&mut ||{ - trie_root::(d.0.clone()) + trie_root::(d.0.clone()) })), Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut ||{ - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&N::empty_node()[..]); - let mut root = H::Out::default(); - let mut t = TrieDBMut::::new(&mut memdb, &mut root); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::C::empty_node()[..]); + let mut root = >::default(); + let mut t = TrieDBMut::new(&mut memdb, &mut root, L::default()); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } })), Fun::new("Iter", |b, d: &TrieInsertionList| { - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&N::empty_node()[..]); - let mut root = H::Out::default(); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::C::empty_node()[..]); + let mut root = >::default(); { - let mut t = TrieDBMut::::new(&mut memdb, &mut root); + let mut t = TrieDBMut::new(&mut memdb, &mut root, L::default()); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } } b.iter(&mut ||{ - let t = TrieDB::::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); for n in t.iter().unwrap() { black_box(n).unwrap(); } @@ -108,7 +109,7 @@ fn random_value(seed: &mut ::Out) -> Vec { } } -pub fn standard_benchmark, S: TrieStream>(b: &mut Criterion, name: &str) { +pub fn standard_benchmark(b: &mut Criterion, name: &str) { // Typical ethereum transaction payload passing through `verify_block_integrity()` close to block #6317032; // 140 iteams, avg length 157bytes, total 22033bytes payload (expected root: 0xc1382bbef81d10a41d325e2873894b61162fb1e6167cafc663589283194acfda) @@ -259,7 +260,7 @@ pub fn standard_benchmark, S: TrieStream>( .enumerate() .map(|(i, v)| (Compact(i as u32).encode(), v) ) .collect::>(); - benchmark::(b, &format!("{}.typical_txs", name), d); + benchmark::(b, &format!("{}.typical_txs", name), d); let st = StandardMap { alphabet: Alphabet::All, @@ -268,7 +269,7 @@ pub fn standard_benchmark, S: TrieStream>( value_mode: ValueMode::Mirror, count: 1000, }; - benchmark::(b, &format!("{}.32_mir_1k", name), st.make()); + benchmark::(b, &format!("{}.32_mir_1k", name), st.make()); let st = StandardMap { alphabet: Alphabet::All, @@ -277,7 +278,7 @@ pub fn standard_benchmark, S: TrieStream>( value_mode: ValueMode::Random, count: 1000, }; - benchmark::(b, &format!("{}.32_ran_1k", name), st.make()); + benchmark::(b, &format!("{}.32_ran_1k", name), st.make()); let mut d: Vec<(Vec, Vec)> = Vec::new(); let mut seed = ::Out::default(); @@ -287,7 +288,7 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.six_high_1k", name), d); + benchmark::(b, &format!("{}.six_high_1k", name), d); let alphabet = b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -297,7 +298,7 @@ pub fn standard_benchmark, S: TrieStream>( let v = random_value(&mut seed); d.push((k, v)) } - benchmark::(b, &format!("{}.six_mid_1k", name), d); + benchmark::(b, &format!("{}.six_mid_1k", name), d); let alphabet = b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -308,7 +309,7 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.random_mid_1k", name), d); + benchmark::(b, &format!("{}.random_mid_1k", name), d); let alphabet = b"abcdef"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -319,5 +320,5 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.six_low_1k", name), d); + benchmark::(b, &format!("{}.six_low_1k", name), d); } diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 64aad601..5c72936e 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -134,7 +134,7 @@ mod test { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefFatDBMut::new(&mut memdb, &mut root); + let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefFatDB::new(&memdb, &root).unwrap(); diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 0dffd063..61fc2b84 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -13,90 +13,87 @@ // limitations under the License. use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieDBMut, TrieMut}; +use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayOut, TrieHash, CError}; use node_codec::NodeCodec; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. /// /// Use it as a `Trie` or `TrieMut` trait object. -pub struct FatDBMut<'db, H, C> +pub struct FatDBMut<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayOut, { - raw: TrieDBMut<'db, H, C>, + raw: TrieDBMut<'db, L>, } -impl<'db, H, C> FatDBMut<'db, H, C> +impl<'db, L> FatDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut HashDB, root: &'db mut H::Out) -> Self { - FatDBMut { raw: TrieDBMut::new(db, root) } + pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Self { + FatDBMut { raw: TrieDBMut::new(db, root, layout) } } /// Create a new trie with the backing database `db` and `root`. /// /// Returns an error if root does not exist. - pub fn from_existing(db: &'db mut HashDB, root: &'db mut H::Out) -> Result { - Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? }) + pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Result, CError> { + Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root, layout)? }) } /// Get the backing database. - pub fn db(&self) -> &HashDB { + pub fn db(&self) -> &HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut HashDB { + pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } } -impl<'db, H, C> TrieMut for FatDBMut<'db, H, C> +impl<'db, L> TrieMut for FatDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&mut self) -> &H::Out { self.raw.root() } + fn root(&mut self) -> &TrieHash { self.raw.root() } fn is_empty(&self) -> bool { self.raw.is_empty() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::H::hash(key).as_ref()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(H::hash(key).as_ref()) + self.raw.get(L::H::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { - let hash = H::hash(key); + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { + let hash = L::H::hash(key); let out = self.raw.insert(hash.as_ref(), value)?; let db = self.raw.db_mut(); // insert if it doesn't exist. if out.is_none() { - let aux_hash = H::hash(hash.as_ref()); + let aux_hash = L::H::hash(hash.as_ref()); db.emplace(aux_hash, &[], DBValue::from_slice(key)); } Ok(out) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { - let hash = H::hash(key); + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + let hash = L::H::hash(key); let out = self.raw.remove(hash.as_ref())?; // remove if it already exists. if out.is_some() { - let aux_hash = H::hash(hash.as_ref()); + let aux_hash = L::H::hash(hash.as_ref()); self.raw.db_mut().remove(&aux_hash, &[]); } @@ -117,7 +114,7 @@ mod test { let mut memdb = MemoryDB::, _>::default(); let mut root = Default::default(); { - let mut t = RefFatDBMut::new(&mut memdb, &mut root); + let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); @@ -132,7 +129,7 @@ mod test { let val = [0x01u8, 0x24]; let key_hash = KeccakHasher::hash(&key); let aux_hash = KeccakHasher::hash(&key_hash); - let mut t = RefFatDBMut::new(&mut memdb, &mut root); + let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&key, &val).unwrap(); assert_eq!(t.get(&key), Ok(Some(DBValue::from_slice(&val)))); assert_eq!(t.db().get(&aux_hash, &[]), Some(DBValue::from_slice(&key))); diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index ba5eec62..7680c0d7 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -218,6 +218,7 @@ pub trait Trie> { } /// A key-value datastore implemented as a database-backed modified Merkle tree. +/// TODO EMCH switch to use only H::Out and C::Error as trait params?? pub trait TrieMut> { /// Return the root of the trie. fn root(&mut self) -> &H::Out; @@ -267,10 +268,9 @@ impl Default for TrieSpec { /// Trie factory. #[derive(Default, Clone)] -pub struct TrieFactory> { +pub struct TrieFactory { spec: TrieSpec, - mark_hash: PhantomData, - mark_codec: PhantomData, + layout: L, } /// All different kinds of tries. @@ -319,22 +319,21 @@ impl<'db, H: Hasher, C: NodeCodec> Trie for TrieKinds<'db, H, C> { } } -impl<'db, H, C> TrieFactory +impl<'db, L> TrieFactory where - H: Hasher, - C: NodeCodec + 'db + L: TrieLayOut + 'db, { /// Creates new factory. - pub fn new(spec: TrieSpec) -> Self { - TrieFactory { spec, mark_hash: PhantomData, mark_codec: PhantomData } + pub fn new(spec: TrieSpec, layout: L) -> Self { + TrieFactory { spec, layout } } /// Create new immutable instance of Trie. pub fn readonly( &self, - db: &'db HashDBRef, - root: &'db H::Out - ) -> Result, H::Out, >::Error> { + db: &'db HashDBRef, + root: &'db TrieHash + ) -> Result, TrieHash, CError> { match self.spec { TrieSpec::Generic => Ok(TrieKinds::Generic(TrieDB::new(db, root)?)), TrieSpec::Secure => Ok(TrieKinds::Secure(SecTrieDB::new(db, root)?)), @@ -343,27 +342,45 @@ where } /// Create new mutable instance of Trie. - pub fn create(&self, db: &'db mut HashDB, root: &'db mut H::Out) -> Box + 'db> { + pub fn create(&self, db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Box + 'db> { match self.spec { - TrieSpec::Generic => Box::new(TrieDBMut::<_, C>::new(db, root)), - TrieSpec::Secure => Box::new(SecTrieDBMut::<_, C>::new(db, root)), - TrieSpec::Fat => Box::new(FatDBMut::<_, C>::new(db, root)), + TrieSpec::Generic => Box::new(TrieDBMut::new(db, root, layout)), + TrieSpec::Secure => Box::new(SecTrieDBMut::new(db, root, layout)), + TrieSpec::Fat => Box::new(FatDBMut::new(db, root, layout)), } } /// Create new mutable instance of trie and check for errors. pub fn from_existing( &self, - db: &'db mut HashDB, - root: &'db mut H::Out - ) -> Result + 'db>, H::Out, >::Error> { + db: &'db mut HashDB, + root: &'db mut TrieHash, + layout: L, + ) -> Result + 'db>, TrieHash, CError> { match self.spec { - TrieSpec::Generic => Ok(Box::new(TrieDBMut::<_, C>::from_existing(db, root)?)), - TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::<_, C>::from_existing(db, root)?)), - TrieSpec::Fat => Ok(Box::new(FatDBMut::<_, C>::from_existing(db, root)?)), + TrieSpec::Generic => Ok(Box::new(TrieDBMut::from_existing(db, root, layout)?)), + TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::from_existing(db, root, layout)?)), + TrieSpec::Fat => Ok(Box::new(FatDBMut::from_existing(db, root, layout)?)), } } /// Returns true iff the trie DB is a fat DB (allows enumeration of keys). pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } + + +/// trait with definition of trie layout +pub trait TrieLayOut: Clone + Send + Sync + Default { + type H: Hasher; + type C: NodeCodec; + + /// does the trie use extension before its branch + fn uses_extension(&self) -> bool; + fn new_codec(&self) -> Self::C; +} + +/// alias to acces hasher hash output type from a `TrieLayout` +pub type TrieHash = ::Out; +/// alias to acces `NodeCodec` `Error` type from a `TrieLayout` +pub type CError = >::Error; + diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 94b14e0d..51f4b48c 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -18,6 +18,10 @@ use ::core_::cmp::*; use ::core_::fmt; use elastic_array::ElasticArray36; + +// TODO EMCH Nibbleslice encoder trait that notably contains output type. +// EMPTY_ENCODED must be part of the trait + /// Empty slice encoded as non-leaf partial key pub const EMPTY_ENCODED: &[u8] = &[0]; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 6863caf4..8dfb3fc5 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -37,6 +37,8 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} +// TODO EMCH change node codec trait to use &mut self as input in order to run on internal buffer. +// (not for decode actually!!; code seems fine to do that and new layout trait is ok too /// Trait for trie node encoding/decoding /// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> /// avoid Vec by all means. diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index 0d6bd3ea..f6653428 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -137,7 +137,7 @@ mod tests { let mut db = MemoryDB::, _>::default(); let mut root = Default::default(); { - let mut x = RefTrieDBMut::new(&mut db, &mut root); + let mut x = RefTrieDBMut::new(&mut db, &mut root, Default::default()); x.insert(b"dog", b"cat").unwrap(); x.insert(b"lunch", b"time").unwrap(); diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 1c5a50d1..d130ad6b 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -91,7 +91,7 @@ mod test { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); t.insert(&KeccakHasher::hash(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap(); } let t = RefSecTrieDB::new(&db, &root).unwrap(); diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index ef4dc87b..c7b675a0 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -13,52 +13,48 @@ // limitations under the License. use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieMut, TrieDBMut}; -use node_codec::NodeCodec; +use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayOut, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// /// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object. -pub struct SecTrieDBMut<'db, H, C> +pub struct SecTrieDBMut<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayOut { - raw: TrieDBMut<'db, H, C> + raw: TrieDBMut<'db, L> } -impl<'db, H, C> SecTrieDBMut<'db, H, C> +impl<'db, L> SecTrieDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut HashDB, root: &'db mut H::Out) -> Self { - SecTrieDBMut { raw: TrieDBMut::new(db, root) } + pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Self { + SecTrieDBMut { raw: TrieDBMut::new(db, root, layout) } } /// Create a new trie with the backing database `db` and `root`. /// /// Returns an error if root does not exist. - pub fn from_existing(db: &'db mut HashDB, root: &'db mut H::Out) -> Result { - Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? }) + pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Result, CError> { + Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root, layout)? }) } /// Get the backing database. - pub fn db(&self) -> &HashDB { self.raw.db() } + pub fn db(&self) -> &HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } + pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } } -impl<'db, H, C> TrieMut for SecTrieDBMut<'db, H, C> +impl<'db, L> TrieMut for SecTrieDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&mut self) -> &H::Out { + fn root(&mut self) -> &TrieHash { self.raw.root() } @@ -66,22 +62,22 @@ where self.raw.is_empty() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(&H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(&L::H::hash(key).as_ref()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(&H::hash(key).as_ref()) + self.raw.get(&L::H::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { - self.raw.insert(&H::hash(key).as_ref(), value) + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { + self.raw.insert(&L::H::hash(key).as_ref(), value) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { - self.raw.remove(&H::hash(key).as_ref()) + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + self.raw.remove(&L::H::hash(key).as_ref()) } } @@ -98,7 +94,7 @@ mod test { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefSecTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefSecTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index b40235c4..27062109 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -58,7 +58,7 @@ use elastic_array::ElasticArray36; /// fn main() { /// let mut memdb = MemoryDB::, _>::default(); /// let mut root = Default::default(); -/// RefTrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); +/// RefTrieDBMut::new(&mut memdb, &mut root, Default::default()).insert(b"foo", b"bar").unwrap(); /// let t = RefTrieDB::new(&memdb, &root).unwrap(); /// assert!(t.contains(b"foo").unwrap()); /// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); @@ -571,7 +571,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -598,7 +598,7 @@ mod tests { let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -627,7 +627,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -654,7 +654,7 @@ mod tests { let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -679,7 +679,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); for x in &d { t.insert(x, x).unwrap(); } @@ -697,7 +697,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); for x in &d { t.insert(x, x).unwrap(); } @@ -716,7 +716,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); for x in &d { t.insert(x, x).unwrap(); } @@ -756,7 +756,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } @@ -772,7 +772,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } @@ -791,7 +791,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let root = { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); for x in &d { t.insert(x, x).unwrap(); } @@ -859,7 +859,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 0058100d..a0d91c33 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -14,7 +14,7 @@ //! In-memory trie representation. -use super::{Result, TrieError, TrieMut}; +use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; use super::lookup::Lookup; use super::node::Node as EncodedNode; use node_codec::NodeCodec; @@ -336,7 +336,7 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// fn main() { /// let mut memdb = MemoryDB::, DBValue>::default(); /// let mut root = Default::default(); -/// let mut t = RefTrieDBMut::new(&mut memdb, &mut root); +/// let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); /// assert!(t.is_empty()); /// assert_eq!(*t.root(), KeccakHasher::hash(&[0u8][..])); /// t.insert(b"foo", b"bar").unwrap(); @@ -346,50 +346,47 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// assert!(!t.contains(b"foo").unwrap()); /// } /// ``` -pub struct TrieDBMut<'a, H, C> +pub struct TrieDBMut<'a, L> where - H: Hasher + 'a, - C: NodeCodec + L: TrieLayOut, { - storage: NodeStorage, - db: &'a mut HashDB, - root: &'a mut H::Out, - root_handle: NodeHandle, - death_row: HashSet<(H::Out, NodeKey)>, + storage: NodeStorage>, + db: &'a mut HashDB, + root: &'a mut TrieHash, + root_handle: NodeHandle>, + death_row: HashSet<(TrieHash, NodeKey)>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, - marker: PhantomData, // TODO: rpheimer: "we could have the NodeCodec trait take &self to its methods and then we don't need PhantomData. we can just store an instance of C: NodeCodec in the trie struct. If it's a ZST it won't have any additional overhead anyway" + layout: L, } -pub struct TrieDBMutNoExt<'a, H, C>(TrieDBMut<'a, H, C>) +pub struct TrieDBMutNoExt<'a, L>(TrieDBMut<'a, L>) where - H: Hasher + 'a, - C: NodeCodec; + L: TrieLayOut; -impl<'a, H, C> TrieDBMutNoExt<'a, H, C> +impl<'a, L> TrieDBMutNoExt<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut HashDB, root: &'a mut H::Out) -> Self { - TrieDBMutNoExt(TrieDBMut::new(db, root)) + pub fn new(db: &'a mut HashDB, root: &'a mut ::Out, layout: L) -> Self { + TrieDBMutNoExt(TrieDBMut::new(db, root, layout)) } /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'a mut HashDB, root: &'a mut H::Out) -> Result { - Ok(TrieDBMutNoExt(TrieDBMut::from_existing(db, root)?)) + pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Result, CError> { + Ok(TrieDBMutNoExt(TrieDBMut::from_existing(db, root, layout)?)) } /// Get the backing database. - pub fn db(&self) -> &HashDB { + pub fn db(&self) -> &HashDB { self.0.db() } /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut HashDB { + pub fn db_mut(&mut self) -> &mut HashDB { self.0.db_mut() } @@ -402,30 +399,29 @@ where } -impl<'a, H, C> TrieDBMut<'a, H, C> +impl<'a, L> TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut HashDB, root: &'a mut H::Out) -> Self { - *root = C::hashed_null_node(); - let root_handle = NodeHandle::Hash(C::hashed_null_node()); + pub fn new(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Self { + *root = L::C::hashed_null_node(); + let root_handle = NodeHandle::Hash(L::C::hashed_null_node()); TrieDBMut { storage: NodeStorage::empty(), - db: db, - root: root, - root_handle: root_handle, + db, + root, + root_handle, death_row: HashSet::new(), hash_count: 0, - marker: PhantomData, + layout } } /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'a mut HashDB, root: &'a mut H::Out) -> Result { + pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Result, CError> { if !db.contains(root, nibbleslice::EMPTY_ENCODED) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -433,28 +429,28 @@ where let root_handle = NodeHandle::Hash(*root); Ok(TrieDBMut { storage: NodeStorage::empty(), - db: db, - root: root, - root_handle: root_handle, + db, + root, + root_handle, death_row: HashSet::new(), hash_count: 0, - marker: PhantomData, + layout, }) } /// Get the backing database. - pub fn db(&self) -> &HashDB { + pub fn db(&self) -> &HashDB { self.db } /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut HashDB { + pub fn db_mut(&mut self) -> &mut HashDB { self.db } // cache a node by hash - fn cache(&mut self, hash: H::Out, key: &[u8]) -> Result { + fn cache(&mut self, hash: TrieHash, key: &[u8]) -> Result, CError> { let node_encoded = self.db.get(&hash, key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + let node = Node::from_encoded::( &node_encoded, &*self.db, &mut self.storage @@ -464,8 +460,8 @@ where // inspect a node, choosing either to replace, restore, or delete it. // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored, key: &mut Partial, inspector: F) -> Result, bool)>, H::Out, C::Error> - where F: FnOnce(&mut Self, Node, &mut Partial) -> Result, H::Out, C::Error> { + fn inspect(&mut self, stored: Stored>, key: &mut Partial, inspector: F) -> Result>, bool)>, TrieHash, CError> + where F: FnOnce(&mut Self, Node>, &mut Partial) -> Result>, TrieHash, CError> { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -487,7 +483,7 @@ where } // walk the trie, attempting to find the key's node. - fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle) -> Result, H::Out, C::Error> + fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle>) -> Result, TrieHash, CError> where 'x: 'key { let mut handle = handle; @@ -497,7 +493,7 @@ where db: &self.db, query: DBValue::from_slice, hash: hash.clone(), - marker: PhantomData::, + marker: PhantomData::, }.look_up(partial), NodeHandle::InMemory(ref handle) => match self.storage[handle] { Node::Empty => return Ok(None), @@ -550,7 +546,7 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), H::Out, C::Error> { + fn insert_at(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, @@ -564,7 +560,7 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at_no_ext(&mut self, handle: NodeHandle, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), H::Out, C::Error> { + fn insert_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, @@ -579,7 +575,7 @@ where /// the insertion inspector. - fn insert_inspector(&mut self, node: Node, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn insert_inspector(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -747,7 +743,7 @@ where }) } - fn insert_inspector_no_ext(&mut self, node: Node, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn insert_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -806,7 +802,7 @@ where // append after cp == existing_key and partial > cp trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; - key.advance(cp + 1); + key.advance(cp + 1); if let Some(child) = children[idx].take() { // original had something there. recurse down into it. let (new_child, changed) = self.insert_at_no_ext(child, key, value, old_val)?; @@ -878,7 +874,7 @@ where /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn remove_at(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { @@ -893,7 +889,7 @@ where } /// Remove a node from the trie based on key. - fn remove_at_no_ext(&mut self, handle: NodeHandle, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn remove_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { @@ -909,7 +905,7 @@ where /// the removal inspector - fn remove_inspector(&mut self, node: Node, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn remove_inspector(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, @@ -998,7 +994,7 @@ where } /// the removal inspector for no extension variant - fn remove_inspector_no_ext(&mut self, node: Node, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { + fn remove_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, @@ -1018,8 +1014,8 @@ where // replace val if let Some(val) = value { *old_val = Some(val); -// key.advance(cp + 1); - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); +// key.advance(cp + 1); + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) @@ -1035,7 +1031,7 @@ where if let Some(child) = children[idx].take() { trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); let prefix = key.encoded_prefix(); - key.advance(cp + 1); + key.advance(cp + 1); match self.remove_at_no_ext(child, key, old_val)? { Some((new, changed)) => { children[idx] = Some(new.into()); @@ -1087,7 +1083,7 @@ where /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node, key: NodeKey) -> Result, H::Out, C::Error> { + fn fix(&mut self, node: Node>, key: NodeKey) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1158,7 +1154,7 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO EMCH optimize this concat (new_partial_tmp may be calc again afterward) + // TODO EMCH optimize this concat (new_partial_tmp may be calc again afterward) let new_partial_tmp = NibbleSlice::new_composed( &NibbleSlice::from_encoded(&enc_nibble).0, &NibbleSlice::new_offset(&[a], 1) @@ -1295,7 +1291,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let encoded_root = node.into_encoded::<_, C, H>(|child, k| { + let encoded_root = node.into_encoded::<_, L::C, L::H>(|child, k| { self.commit_child(child, k) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); @@ -1317,7 +1313,7 @@ where /// case where we can fit the actual data in the `Hasher`s output type, we /// store the data inline. This function is used as the callback to the /// `into_encoded` method of `Node`. - fn commit_child(&mut self, handle: NodeHandle, prefix: &NodeKey) -> ChildReference { + fn commit_child(&mut self, handle: NodeHandle>, prefix: &NodeKey) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), NodeHandle::InMemory(storage_handle) => { @@ -1329,15 +1325,15 @@ where let combined = combine_encoded(&prefix, partial); self.commit_child(node_handle, &combined) }; - node.into_encoded::<_, C, H>(commit_child) + node.into_encoded::<_, L::C, L::H>(commit_child) }; - if encoded.len() >= H::LENGTH { + if encoded.len() >= L::H::LENGTH { let hash = self.db.insert(&prefix, &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { - // it's a small value, so we cram it into a `H::Out` and tag with length - let mut h = H::Out::default(); + // it's a small value, so we cram it into a `TrieHash` and tag with length + let mut h = >::default(); let len = encoded.len(); h.as_mut()[..len].copy_from_slice(&encoded[..len]); ChildReference::Inline(h, len) @@ -1349,7 +1345,7 @@ where } // a hack to get the root node's handle - fn root_handle(&self) -> NodeHandle { + fn root_handle(&self) -> NodeHandle> { match self.root_handle { NodeHandle::Hash(h) => NodeHandle::Hash(h), NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), @@ -1357,19 +1353,18 @@ where } } -impl<'a, H, C> TrieMut for TrieDBMut<'a, H, C> +impl<'a, L> TrieMut for TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&mut self) -> &H::Out { + fn root(&mut self) -> &TrieHash { self.commit(); self.root } fn is_empty(&self) -> bool { match self.root_handle { - NodeHandle::Hash(h) => h == C::hashed_null_node(), + NodeHandle::Hash(h) => h == L::C::hashed_null_node(), NodeHandle::InMemory(ref h) => match self.storage[h] { Node::Empty => true, _ => false, @@ -1377,13 +1372,13 @@ where } } - fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, TrieHash, CError> where 'x: 'key { self.lookup(NibbleSlice::new(key), &self.root_handle) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { if value.is_empty() { return self.remove(key) } let mut old_val = None; @@ -1404,7 +1399,7 @@ where Ok(old_val) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); @@ -1418,8 +1413,8 @@ where } None => { trace!(target: "trie", "remove: obliterated trie"); - self.root_handle = NodeHandle::Hash(C::hashed_null_node()); - *self.root = C::hashed_null_node(); + self.root_handle = NodeHandle::Hash(L::C::hashed_null_node()); + *self.root = L::C::hashed_null_node(); } } @@ -1427,12 +1422,11 @@ where } } -impl<'a, H, C> TrieMut for TrieDBMutNoExt<'a, H, C> +impl<'a, L> TrieMut for TrieDBMutNoExt<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&mut self) -> &H::Out { + fn root(&mut self) -> &TrieHash { self.0.root() } @@ -1440,13 +1434,13 @@ where self.0.is_empty() } - fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, TrieHash, CError> where 'x: 'key { self.0.get(key) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { if value.is_empty() { return self.remove(key) } let mut old_val = None; @@ -1467,7 +1461,7 @@ where Ok(old_val) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.0.root_handle(); @@ -1481,8 +1475,8 @@ where } None => { trace!(target: "trie", "remove: obliterated trie"); - self.0.root_handle = NodeHandle::Hash(C::hashed_null_node()); - *self.0.root = C::hashed_null_node(); + self.0.root_handle = NodeHandle::Hash(L::C::hashed_null_node()); + *self.0.root = L::C::hashed_null_node(); } } @@ -1490,10 +1484,9 @@ where } } -impl<'a, H, C> Drop for TrieDBMut<'a, H, C> +impl<'a, L> Drop for TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { fn drop(&mut self) { self.commit(); @@ -1516,7 +1509,7 @@ mod tests { root: &'db mut ::Out, v: &[(Vec, Vec)] ) -> RefTrieDBMut<'db> { - let mut t = RefTrieDBMut::new(db, root); + let mut t = RefTrieDBMut::new(db, root, Default::default()); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -1537,7 +1530,7 @@ mod tests { root: &'db mut ::Out, v: &[(Vec, Vec)] ) -> RefTrieDBMutNoExt<'db> { - let mut t = RefTrieDBMutNoExt::new(db, root); + let mut t = RefTrieDBMutNoExt::new(db, root, Default::default()); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -1603,7 +1596,7 @@ mod tests { fn init() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); } @@ -1611,7 +1604,7 @@ mod tests { fn insert_on_empty() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); } @@ -1622,12 +1615,12 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t1.insert(&[0x01, 0x23], big_value).unwrap(); t1.insert(&[0x01, 0x34], big_value).unwrap(); let mut memdb2 = MemoryDB::, DBValue>::default(); let mut root2 = Default::default(); - let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2); + let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2, Default::default()); t2.insert(&[0x01], big_value).unwrap(); t2.insert(&[0x01, 0x23], big_value).unwrap(); @@ -1646,10 +1639,10 @@ mod tests { { let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); //t1.insert(&[0x01, 0x23], big_value).unwrap(); //t1.insert(&[0x01, 0x34], big_value).unwrap(); - let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2); + let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2, Default::default()); t2.insert(&[0x01, 0x23], big_value3).unwrap(); t2.insert(&[0x01], big_value2).unwrap(); @@ -1669,7 +1662,7 @@ mod tests { fn insert_replace_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); @@ -1679,7 +1672,7 @@ mod tests { fn insert_make_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1692,7 +1685,7 @@ mod tests { fn insert_into_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -1707,7 +1700,7 @@ mod tests { fn insert_value_into_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[], &[0x0]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1720,7 +1713,7 @@ mod tests { fn insert_split_leaf() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1733,7 +1726,7 @@ mod tests { fn insert_split_extenstion() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap(); t.insert(&[0x01, 0xf3, 0x45], &[0x02]).unwrap(); t.insert(&[0x01, 0xf3, 0xf5], &[0x03]).unwrap(); @@ -1751,7 +1744,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x11u8, 0x23], big_value1).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1766,7 +1759,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x11u8, 0x23], big_value).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1779,7 +1772,7 @@ mod tests { fn test_at_empty() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let t = RefTrieDBMut::new(&mut memdb, &mut root); + let t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); assert_eq!(t.get(&[0x5]).unwrap(), None); } @@ -1787,7 +1780,7 @@ mod tests { fn test_at_one() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x1u8, 0x23])); t.commit(); @@ -1798,7 +1791,7 @@ mod tests { fn test_at_three() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -1856,12 +1849,12 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } { - let _ = RefTrieDBMut::from_existing(&mut db, &mut root); + let _ = RefTrieDBMut::from_existing(&mut db, &mut root, Default::default()); } } @@ -1878,7 +1871,7 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); for &(ref key, ref value) in &x { t.insert(key, value).unwrap(); } @@ -1906,7 +1899,7 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); for &(ref key, ref value) in &x { assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); From e092a0c1054f607e62c4482ca6ef8ae9f351d6bd Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 9 Apr 2019 15:31:22 +0200 Subject: [PATCH 016/120] Fuse triedbmut code, before remove NoExt variant --- trie-db/src/triedbmut.rs | 189 ++++++++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 23 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index a0d91c33..eed14a0d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -559,7 +559,8 @@ where Ok((self.storage.alloc(new_stored), changed)) } - /// insert a key-value pair into the trie, creating new nodes if necessary. + /// insert a key-value pair into the trie, creating new nodes if necessary. TODO rem : no + /// additional meaning fn insert_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, @@ -583,8 +584,9 @@ where Node::Empty => { trace!(target: "trie", "empty: COMPOSE"); InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) - } + }, Node::Branch(mut children, stored_value) => { + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { @@ -615,7 +617,80 @@ where InsertAction::Replace(Node::Branch(children, stored_value)) } - } + }, + Node::NibbledBranch(encoded, mut children, stored_value) => { + debug_assert!(!self.layout.uses_extension()); + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + let existing_key = NibbleSlice::from_encoded(&encoded).0; + + let cp = partial.common_prefix(&existing_key); + if cp == existing_key.len() && cp == partial.len() { + let unchanged = stored_value.as_ref() == Some(&value); + let branch = Node::NibbledBranch(existing_key.encoded(false), children, Some(value)); + *old_val = stored_value; + + match unchanged { + true => InsertAction::Restore(branch), + false => InsertAction::Replace(branch), + } + } else if cp < existing_key.len() { + // insert a branch value in between + trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + let low = Node::NibbledBranch(existing_key.mid(cp + 1).encoded(false), children, stored_value); + let ix = existing_key.at(cp); + let mut children = empty_children(); + let alloc_storage = self.storage.alloc(Stored::New(low)); + + + children[ix as usize] = Some(alloc_storage.into()); + + if partial.len() - cp == 0 { + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded_leftmost(cp,false), + children, + Some(value), + ) + ) + } else { + let ix = partial.at(cp); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).encoded(true), value))); + + children[ix as usize] = Some(leaf.into()); + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded_leftmost(cp,false), + children, + None, + ) + ) + + } + + } else { + // append after cp == existing_key and partial > cp + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + let idx = partial.at(cp) as usize; + key.advance(cp + 1); + if let Some(child) = children[idx].take() { + // original had something there. recurse down into it. + let (new_child, changed) = self.insert_at_no_ext(child, key, value, old_val)?; + children[idx] = Some(new_child.into()); + if !changed { + // the new node we composed didn't change. that means our branch is untouched too. + return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.encoded(false), children, stored_value))); + } + } else { + // original had nothing there. compose a leaf. + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); + children[idx] = Some(leaf.into()); + } + InsertAction::Replace(Node::NibbledBranch( + existing_key.encoded(false), + children, + stored_value, + )) + + } + }, Node::Leaf(encoded, stored_value) => { let existing_key = NibbleSlice::from_encoded(&encoded).0; let cp = partial.common_prefix(&existing_key); @@ -630,26 +705,39 @@ where true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), } - } else if cp == 0 { - trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + } else if (self.layout.uses_extension() && cp == 0) + || (!self.layout.uses_extension() && cp < existing_key.len()) { + trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // one of us isn't empty: transmute to branch here let mut children = empty_children(); - let branch = if existing_key.is_empty() { + let branch = if self.layout.uses_extension() && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) } else { - let idx = existing_key.at(0) as usize; - let new_leaf = Node::Leaf(existing_key.mid(1).encoded(true), stored_value); + let idx = existing_key.at(cp) as usize; + let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - Node::Branch(children, None) + if self.layout.uses_extension() { + Node::Branch(children, None) + } else { + Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) + } }; // always replace because whatever we get out here is not the branch we started with. let branch_action = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch_action) + } else if !self.layout.uses_extension() { + trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + // fully-shared prefix for a branch using no_extension. + // make a stub branch + let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); + InsertAction::Replace(branch) + } else if cp == existing_key.len() { + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -663,6 +751,7 @@ where let branch_handle = self.storage.alloc(Stored::New(branch)).into(); InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) } else { + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared prefix for an extension. @@ -680,8 +769,9 @@ where self.storage.alloc(Stored::New(augmented_low)).into() )) } - } + }, Node::Extension(encoded, child_branch) => { + debug_assert!(self.layout.uses_extension()); let existing_key = NibbleSlice::from_encoded(&encoded).0; let cp = partial.common_prefix(&existing_key); if cp == 0 { @@ -736,13 +826,11 @@ where self.storage.alloc(Stored::New(augmented_low)).into() )) } - } - Node::NibbledBranch(..) => { - unimplemented!() - } + }, }) } + // TODO EMCH delete (fused in insert_inspector) fn insert_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -888,7 +976,7 @@ where Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } - /// Remove a node from the trie based on key. + /// Remove a node from the trie based on key. TODO EMCH delete fn remove_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), @@ -910,11 +998,17 @@ where Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), + (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), (Node::Branch(children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. Action::Replace(self.fix(Node::Branch(children, None), key.encoded_prefix())?) - } + }, + (Node::NibbledBranch(n, children, Some(val)), true) => { + *old_val = Some(val); + // always replace since we took the value out. + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.encoded_prefix())?) + }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; if let Some(child) = children[idx].take() { @@ -943,7 +1037,58 @@ where // no change needed. Action::Restore(Node::Branch(children, value)) } - } + }, + (Node::NibbledBranch(encoded, mut children, value), false) => { + let (cp, existing_len) = { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + (existing_key.common_prefix(&partial), existing_key.len()) + }; + if cp == existing_len && cp == partial.len() { + + // replace val + if let Some(val) = value { + *old_val = Some(val); + + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); + Action::Replace(f?) + } else { + Action::Restore(Node::NibbledBranch(encoded, children, None)) + } + } else if cp < existing_len { + // partway through an extension -- nothing to do here. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } else { + // cp == existing_len && cp < partial.len() : check children + let idx = partial.at(cp) as usize; + + if let Some(child) = children[idx].take() { + trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); + let prefix = key.encoded_prefix(); + key.advance(cp + 1); + match self.remove_at_no_ext(child, key, old_val)? { + Some((new, changed)) => { + children[idx] = Some(new.into()); + let branch = Node::NibbledBranch(encoded, children, value); + match changed { + // child was changed, so we were too. + true => Action::Replace(branch), + // unchanged, so we are too. + false => Action::Restore(branch), + } + }, + None => { + // the child we took was deleted. + // the node may need fixing. + trace!(target: "trie", "branch child deleted, partial={:?}", partial); + Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) + }, + } + } else { + // no change needed. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } + } + }, (Node::Leaf(encoded, value), _) => { if NibbleSlice::from_encoded(&encoded).0 == partial { // this is the node we were looking for. Let's delete it. @@ -954,7 +1099,7 @@ where trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); Action::Restore(Node::Leaf(encoded, value)) } - } + }, (Node::Extension(encoded, child_branch), _) => { let (cp, existing_len) = { let existing_key = NibbleSlice::from_encoded(&encoded).0; @@ -986,14 +1131,11 @@ where // partway through an extension -- nothing to do here. Action::Restore(Node::Extension(encoded, child_branch)) } - } - (Node::NibbledBranch(..), _) => { - unreachable!() - } + }, }) } - /// the removal inspector for no extension variant + /// the removal inspector for no extension variant TODO EMCH delete fn remove_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); Ok(match (node, partial.is_empty()) { @@ -1422,6 +1564,7 @@ where } } +// TODO EMCH delete impl<'a, L> TrieMut for TrieDBMutNoExt<'a, L> where L: TrieLayOut, From 3bd31274fdfdf87a0259cda2960bdcf4684b5846 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 9 Apr 2019 16:13:54 +0200 Subject: [PATCH 017/120] Fix merge and remove triedbmutnoext variant. --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/lib.rs | 2 +- trie-db/src/triedb.rs | 1 - trie-db/src/triedbmut.rs | 388 ++----------------------- 4 files changed, 24 insertions(+), 369 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index feecde8b..60547008 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -67,7 +67,7 @@ impl TrieLayOut for LayoutNew { pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodecNoExt>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, LayoutOri>; -pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMutNoExt<'a, LayoutNew>; +pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, LayoutNew>; pub type RefFatDB<'a> = trie_db::FatDB<'a, KeccakHasher, ReferenceNodeCodec>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 7680c0d7..70639178 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -92,7 +92,7 @@ mod iter_build; pub use hash_db::{HashDB, HashDBRef, Hasher}; pub use self::triedb::{TrieDB, TrieDBIterator}; -pub use self::triedbmut::{TrieDBMutNoExt, TrieDBMut, ChildReference}; +pub use self::triedbmut::{TrieDBMut, ChildReference}; pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedb::SecTrieDB; pub use self::fatdb::{FatDB, FatDBIterator}; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 27062109..d0eb8cbe 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -724,7 +724,6 @@ mod tests { let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); - let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), DBValue::from_slice(b"A"))); iter.seek(b"!").unwrap(); assert_eq!(d, iter.map(|x| x.unwrap().1).collect::>()); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index eed14a0d..e45d10ae 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -361,44 +361,6 @@ where layout: L, } -pub struct TrieDBMutNoExt<'a, L>(TrieDBMut<'a, L>) -where - L: TrieLayOut; - -impl<'a, L> TrieDBMutNoExt<'a, L> -where - L: TrieLayOut, -{ - /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut HashDB, root: &'a mut ::Out, layout: L) -> Self { - TrieDBMutNoExt(TrieDBMut::new(db, root, layout)) - } - - /// Create a new trie with the backing database `db` and `root. - /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Result, CError> { - Ok(TrieDBMutNoExt(TrieDBMut::from_existing(db, root, layout)?)) - } - - /// Get the backing database. - pub fn db(&self) -> &HashDB { - self.0.db() - } - - /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut HashDB { - self.0.db_mut() - } - - /// Commit the in-memory changes to disk, freeing their storage and - /// updating the state root. - pub fn commit(&mut self) { - self.0.commit() - } - -} - - impl<'a, L> TrieDBMut<'a, L> where L: TrieLayOut, @@ -415,7 +377,7 @@ where root_handle, death_row: HashSet::new(), hash_count: 0, - layout + layout } } @@ -559,22 +521,6 @@ where Ok((self.storage.alloc(new_stored), changed)) } - /// insert a key-value pair into the trie, creating new nodes if necessary. TODO rem : no - /// additional meaning - fn insert_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { - let h = match handle { - NodeHandle::InMemory(h) => h, - NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, - }; - let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? - let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { - trie.insert_inspector_no_ext(stored, key, value, old_val).map(|a| a.into_action()) - })?.expect("Insertion never deletes."); - - Ok((self.storage.alloc(new_stored), changed)) - } - - /// the insertion inspector. fn insert_inspector(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); @@ -586,7 +532,7 @@ where InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) }, Node::Branch(mut children, stored_value) => { - debug_assert!(self.layout.uses_extension()); + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { @@ -619,7 +565,7 @@ where } }, Node::NibbledBranch(encoded, mut children, stored_value) => { - debug_assert!(!self.layout.uses_extension()); + debug_assert!(!self.layout.uses_extension()); trace!(target: "trie", "branch: ROUTE,AUGMENT"); let existing_key = NibbleSlice::from_encoded(&encoded).0; @@ -672,7 +618,7 @@ where key.advance(cp + 1); if let Some(child) = children[idx].take() { // original had something there. recurse down into it. - let (new_child, changed) = self.insert_at_no_ext(child, key, value, old_val)?; + let (new_child, changed) = self.insert_at(child, key, value, old_val)?; children[idx] = Some(new_child.into()); if !changed { // the new node we composed didn't change. that means our branch is untouched too. @@ -706,7 +652,7 @@ where false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), } } else if (self.layout.uses_extension() && cp == 0) - || (!self.layout.uses_extension() && cp < existing_key.len()) { + || (!self.layout.uses_extension() && cp < existing_key.len()) { trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // one of us isn't empty: transmute to branch here @@ -716,14 +662,14 @@ where Node::Branch(children, Some(stored_value)) } else { let idx = existing_key.at(cp) as usize; - let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); + let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - if self.layout.uses_extension() { - Node::Branch(children, None) - } else { - Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) - } + if self.layout.uses_extension() { + Node::Branch(children, None) + } else { + Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) + } }; // always replace because whatever we get out here is not the branch we started with. @@ -731,13 +677,17 @@ where InsertAction::Replace(branch_action) } else if !self.layout.uses_extension() { trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); - // fully-shared prefix for a branch using no_extension. - // make a stub branch - let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); + + // fully-shared prefix for an extension. + // make a stub branch + let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); + // augment the new branch. + let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); + InsertAction::Replace(branch) } else if cp == existing_key.len() { - debug_assert!(self.layout.uses_extension()); + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -751,7 +701,7 @@ where let branch_handle = self.storage.alloc(Stored::New(branch)).into(); InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) } else { - debug_assert!(self.layout.uses_extension()); + debug_assert!(self.layout.uses_extension()); trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared prefix for an extension. @@ -771,7 +721,7 @@ where } }, Node::Extension(encoded, child_branch) => { - debug_assert!(self.layout.uses_extension()); + debug_assert!(self.layout.uses_extension()); let existing_key = NibbleSlice::from_encoded(&encoded).0; let cp = partial.common_prefix(&existing_key); if cp == 0 { @@ -830,137 +780,6 @@ where }) } - // TODO EMCH delete (fused in insert_inspector) - fn insert_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { - let partial = key.mid(); - trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); - - Ok(match node { - Node::Empty => { - trace!(target: "trie", "empty: COMPOSE"); - InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) - } - Node::NibbledBranch(encoded, mut children, stored_value) => { - trace!(target: "trie", "branch: ROUTE,AUGMENT"); - let existing_key = NibbleSlice::from_encoded(&encoded).0; - - let cp = partial.common_prefix(&existing_key); - if cp == existing_key.len() && cp == partial.len() { - let unchanged = stored_value.as_ref() == Some(&value); - let branch = Node::NibbledBranch(existing_key.encoded(false), children, Some(value)); - *old_val = stored_value; - - match unchanged { - true => InsertAction::Restore(branch), - false => InsertAction::Replace(branch), - } - } else if cp < existing_key.len() { - // insert a branch value in between - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); - let low = Node::NibbledBranch(existing_key.mid(cp + 1).encoded(false), children, stored_value); - let ix = existing_key.at(cp); - let mut children = empty_children(); - let alloc_storage = self.storage.alloc(Stored::New(low)); - - - children[ix as usize] = Some(alloc_storage.into()); - - if partial.len() - cp == 0 { - InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded_leftmost(cp,false), - children, - Some(value), - ) - ) - } else { - let ix = partial.at(cp); - let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).encoded(true), value))); - - children[ix as usize] = Some(leaf.into()); - InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded_leftmost(cp,false), - children, - None, - ) - ) - - } - - } else { - // append after cp == existing_key and partial > cp - trace!(target: "trie", "branch: ROUTE,AUGMENT"); - let idx = partial.at(cp) as usize; - key.advance(cp + 1); - if let Some(child) = children[idx].take() { - // original had something there. recurse down into it. - let (new_child, changed) = self.insert_at_no_ext(child, key, value, old_val)?; - children[idx] = Some(new_child.into()); - if !changed { - // the new node we composed didn't change. that means our branch is untouched too. - return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.encoded(false), children, stored_value))); - } - } else { - // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); - children[idx] = Some(leaf.into()); - } - InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded(false), - children, - stored_value, - )) - - } - - }, - Node::Leaf(encoded, stored_value) => { - let existing_key = NibbleSlice::from_encoded(&encoded).0; - let cp = partial.common_prefix(&existing_key); - if cp == existing_key.len() && cp == partial.len() { - trace!(target: "trie", "equivalent-leaf: REPLACE"); - // equivalent leaf. - let unchanged = stored_value == value; - *old_val = Some(stored_value); - - match unchanged { - // unchanged. restore - true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), - false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), - } - } else if cp < existing_key.len() { - trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); - - // one of us isn't empty: transmute to branch here - let mut children = empty_children(); - let idx = existing_key.at(cp) as usize; - let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); - children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - - let branch = Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None); - - // always replace because whatever we get out here is not the branch we started with. - let branch_action = self.insert_inspector_no_ext(branch, key, value, old_val)?.unwrap_node(); - InsertAction::Replace(branch_action) - } else { - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); - - // fully-shared prefix for an extension. - // make a stub branch - let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); - // augment the new branch. - let branch = self.insert_inspector_no_ext(branch, key, value, old_val)?.unwrap_node(); - - InsertAction::Replace(branch) - } - }, - Node::Extension(..) => { - unreachable!() - }, - Node::Branch(..) => unreachable!(), - }) - } - - /// Remove a node from the trie based on key. fn remove_at(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { @@ -976,22 +795,6 @@ where Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } - /// Remove a node from the trie based on key. TODO EMCH delete - fn remove_at_no_ext(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { - let stored = match handle { - NodeHandle::InMemory(h) => self.storage.destroy(h), - NodeHandle::Hash(h) => { - let handle = self.cache(h, &key.encoded_prefix())?; - self.storage.destroy(handle) - } - }; - - let opt = self.inspect(stored, key, move |trie, node, key| trie.remove_inspector_no_ext(node, key, old_val))?; - - Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) - } - - /// the removal inspector fn remove_inspector(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); @@ -1065,7 +868,7 @@ where trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); let prefix = key.encoded_prefix(); key.advance(cp + 1); - match self.remove_at_no_ext(child, key, old_val)? { + match self.remove_at(child, key, old_val)? { Some((new, changed)) => { children[idx] = Some(new.into()); let branch = Node::NibbledBranch(encoded, children, value); @@ -1135,90 +938,6 @@ where }) } - /// the removal inspector for no extension variant TODO EMCH delete - fn remove_inspector_no_ext(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { - let partial = key.mid(); - Ok(match (node, partial.is_empty()) { - (Node::Empty, _) => Action::Delete, - (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), - (Node::NibbledBranch(n, children, Some(val)), true) => { - *old_val = Some(val); - // always replace since we took the value out. - Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.encoded_prefix())?) - }, - (Node::NibbledBranch(encoded, mut children, value), false) => { - let (cp, existing_len) = { - let existing_key = NibbleSlice::from_encoded(&encoded).0; - (existing_key.common_prefix(&partial), existing_key.len()) - }; - if cp == existing_len && cp == partial.len() { - - // replace val - if let Some(val) = value { - *old_val = Some(val); -// key.advance(cp + 1); - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); - Action::Replace(f?) - } else { - Action::Restore(Node::NibbledBranch(encoded, children, None)) - } - } else if cp < existing_len { - // partway through an extension -- nothing to do here. - Action::Restore(Node::NibbledBranch(encoded, children, value)) - } else { - // cp == existing_len && cp < partial.len() : check children - // TODO can merge with branch remove code - let idx = partial.at(cp) as usize; - - if let Some(child) = children[idx].take() { - trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); - let prefix = key.encoded_prefix(); - key.advance(cp + 1); - match self.remove_at_no_ext(child, key, old_val)? { - Some((new, changed)) => { - children[idx] = Some(new.into()); - let branch = Node::NibbledBranch(encoded, children, value); - match changed { - // child was changed, so we were too. - true => Action::Replace(branch), - // unchanged, so we are too. - false => Action::Restore(branch), - } - }, - None => { - // the child we took was deleted. - // the node may need fixing. - trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) - }, - } - } else { - // no change needed. - Action::Restore(Node::NibbledBranch(encoded, children, value)) - } - } - }, - (Node::Leaf(encoded, value), _) => { - if NibbleSlice::from_encoded(&encoded).0 == partial { - // this is the node we were looking for. Let's delete it. - *old_val = Some(value); - Action::Delete - } else { - // leaf the node alone. - trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); - Action::Restore(Node::Leaf(encoded, value)) - } - }, - (Node::Extension(..), _) => { - unreachable!() - } - (Node::Branch(..), _) => { - unreachable!() - } - }) - } - - /// Given a node which may be in an _invalid state_, fix it such that it is then in a valid /// state. /// @@ -1564,69 +1283,6 @@ where } } -// TODO EMCH delete -impl<'a, L> TrieMut for TrieDBMutNoExt<'a, L> -where - L: TrieLayOut, -{ - fn root(&mut self) -> &TrieHash { - self.0.root() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, TrieHash, CError> - where 'x: 'key - { - self.0.get(key) - } - - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { - if value.is_empty() { return self.remove(key) } - - let mut old_val = None; - - trace!(target: "trie", "insert: key={:#x?}, value={:#x?}", key, value); - - let root_handle = self.0.root_handle(); - let (new_handle, changed) = self.0.insert_at_no_ext( - root_handle, - &mut Partial::new(NibbleSlice::new(key)), - DBValue::from_slice(value), - &mut old_val, - )?; - - trace!(target: "trie", "insert: altered trie={}", changed); - self.0.root_handle = NodeHandle::InMemory(new_handle); - - Ok(old_val) - } - - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { - trace!(target: "trie", "remove: key={:#x?}", key); - - let root_handle = self.0.root_handle(); - let mut key = Partial::new(NibbleSlice::new(key)); - let mut old_val = None; - - match self.0.remove_at_no_ext(root_handle, &mut key, &mut old_val)? { - Some((handle, changed)) => { - trace!(target: "trie", "remove: altered trie={}", changed); - self.0.root_handle = NodeHandle::InMemory(handle); - } - None => { - trace!(target: "trie", "remove: obliterated trie"); - self.0.root_handle = NodeHandle::Hash(L::C::hashed_null_node()); - *self.0.root = L::C::hashed_null_node(); - } - } - - Ok(old_val) - } -} - impl<'a, L> Drop for TrieDBMut<'a, L> where L: TrieLayOut, From a8b73f665a0f808557094598488ca2ffa74405df Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 9 Apr 2019 16:50:09 +0200 Subject: [PATCH 018/120] No instance for Layout (NodeCodec should have one at some point), to benefit from constant. --- test-support/reference-trie/src/lib.rs | 54 +++++++++---------- test-support/trie-bench/src/lib.rs | 6 +-- trie-db/src/fatdb.rs | 2 +- trie-db/src/fatdbmut.rs | 12 ++--- trie-db/src/iter_build.rs | 2 +- trie-db/src/lib.rs | 27 +++++----- trie-db/src/recorder.rs | 2 +- trie-db/src/sectriedb.rs | 2 +- trie-db/src/sectriedbmut.rs | 10 ++-- trie-db/src/triedb.rs | 30 +++++------ trie-db/src/triedbmut.rs | 75 +++++++++++++------------- 11 files changed, 105 insertions(+), 117 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 60547008..809e41bd 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -40,28 +40,22 @@ use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record, TrieLayOut}; pub use trie_root::TrieStream; -#[derive(Clone,Default)] /// trie layout similar to parity-ethereum pub struct LayoutOri; impl TrieLayOut for LayoutOri { - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodec; - - fn uses_extension(&self) -> bool { true } - fn new_codec(&self) -> Self::C { ReferenceNodeCodec } + const USE_EXTENSION: bool = true; + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodec; } -#[derive(Clone,Default)] /// trie layout similar to substrate one pub struct LayoutNew; impl TrieLayOut for LayoutNew { - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; - - fn uses_extension(&self) -> bool { false } - fn new_codec(&self) -> Self::C { ReferenceNodeCodecNoExt } + const USE_EXTENSION: bool = false; + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodecNoExt; } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; @@ -700,7 +694,7 @@ pub fn compare_impl + Eq> ( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, LayoutOri); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } @@ -742,7 +736,7 @@ pub fn compare_root( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, LayoutOri); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } @@ -818,17 +812,17 @@ pub fn compare_impl_no_ext( }; let root = { let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); } t.root().clone() }; -/* { +/* { let db : &dyn hash_db::HashDB<_,_> = &memdb; let t = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t); - }*/ + }*/ if root != root_new { { @@ -860,7 +854,7 @@ pub fn compare_impl_no_ext_unordered( let mut b_map = std::collections::btree_map::BTreeMap::new(); let root = { let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); b_map.insert(data[i].0.clone(),data[i].1.clone()); @@ -903,13 +897,13 @@ pub fn compare_no_ext_insert_remove( let mut root = Default::default(); let mut a = 0; { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, LayoutNew); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); t.commit(); } while a < data.len() { // new triemut every 3 element root = { - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root, LayoutNew).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); for _ in 0..3 { if data[a].0 { // remove @@ -930,7 +924,7 @@ pub fn compare_no_ext_insert_remove( *t.root() }; } - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root, LayoutNew).unwrap(); + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. assert_eq!(*t.root(), calc_root_no_ext(data2)); @@ -944,13 +938,13 @@ pub fn compare_no_ext_insert_remove( #[should_panic] #[test] fn too_big_nibble_len () { - // + 1 for 0 added byte of nibble encode - let input = vec![0u8; (noext_cst::LEAF_NODE_OVER as usize + 256) / 2 + 1]; - let enc = ReferenceNodeCodecNoExt::leaf_node(&input, &[1]); - let dec = ReferenceNodeCodecNoExt::decode(&enc).unwrap(); - let o_sl = if let Node::Leaf(sl,_) = dec { - //assert_eq!(&input[..], &sl.encoded(false)[..]); - Some(sl) - } else { None }; - //assert!(o_sl.is_some()); + // + 1 for 0 added byte of nibble encode + let input = vec![0u8; (noext_cst::LEAF_NODE_OVER as usize + 256) / 2 + 1]; + let enc = ReferenceNodeCodecNoExt::leaf_node(&input, &[1]); + let dec = ReferenceNodeCodecNoExt::decode(&enc).unwrap(); + let o_sl = if let Node::Leaf(sl,_) = dec { + //assert_eq!(&input[..], &sl.encoded(false)[..]); + Some(sl) + } else { None }; + //assert!(o_sl.is_some()); } diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index dee40d92..c789048f 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -55,9 +55,9 @@ where trie_root::(d.0.clone()) })), Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut ||{ - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::C::empty_node()[..]); + let mut memdb = MemoryDB::<_, HashKey, _>::new(&L::C::empty_node()[..]); let mut root = >::default(); - let mut t = TrieDBMut::new(&mut memdb, &mut root, L::default()); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } @@ -66,7 +66,7 @@ where let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::C::empty_node()[..]); let mut root = >::default(); { - let mut t = TrieDBMut::new(&mut memdb, &mut root, L::default()); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 5c72936e..64aad601 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -134,7 +134,7 @@ mod test { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefFatDB::new(&memdb, &root).unwrap(); diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 61fc2b84..95c8e62e 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -34,15 +34,15 @@ where /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Self { - FatDBMut { raw: TrieDBMut::new(db, root, layout) } + pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash) -> Self { + FatDBMut { raw: TrieDBMut::new(db, root) } } /// Create a new trie with the backing database `db` and `root`. /// /// Returns an error if root does not exist. - pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Result, CError> { - Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root, layout)? }) + pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash) -> Result, CError> { + Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. @@ -114,7 +114,7 @@ mod test { let mut memdb = MemoryDB::, _>::default(); let mut root = Default::default(); { - let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); @@ -129,7 +129,7 @@ mod test { let val = [0x01u8, 0x24]; let key_hash = KeccakHasher::hash(&key); let aux_hash = KeccakHasher::hash(&key_hash); - let mut t = RefFatDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&key, &val).unwrap(); assert_eq!(t.get(&key), Ok(Some(DBValue::from_slice(&val)))); assert_eq!(t.db().get(&aux_hash, &[]), Some(DBValue::from_slice(&key))); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index c4c93192..a0d0374d 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -258,7 +258,7 @@ where self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); // TODO EMCH !!!!!!!!!!! debug that it may be shifted from a unit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); cb_ext.process(&encoded_key[..], encoded, is_root) } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 70639178..5ec32122 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -342,11 +342,11 @@ where } /// Create new mutable instance of Trie. - pub fn create(&self, db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Box + 'db> { + pub fn create(&self, db: &'db mut HashDB, root: &'db mut TrieHash) -> Box + 'db> { match self.spec { - TrieSpec::Generic => Box::new(TrieDBMut::new(db, root, layout)), - TrieSpec::Secure => Box::new(SecTrieDBMut::new(db, root, layout)), - TrieSpec::Fat => Box::new(FatDBMut::new(db, root, layout)), + TrieSpec::Generic => Box::new(TrieDBMut::::new(db, root)), + TrieSpec::Secure => Box::new(SecTrieDBMut::::new(db, root)), + TrieSpec::Fat => Box::new(FatDBMut::::new(db, root)), } } @@ -355,12 +355,11 @@ where &self, db: &'db mut HashDB, root: &'db mut TrieHash, - layout: L, ) -> Result + 'db>, TrieHash, CError> { match self.spec { - TrieSpec::Generic => Ok(Box::new(TrieDBMut::from_existing(db, root, layout)?)), - TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::from_existing(db, root, layout)?)), - TrieSpec::Fat => Ok(Box::new(FatDBMut::from_existing(db, root, layout)?)), + TrieSpec::Generic => Ok(Box::new(TrieDBMut::::from_existing(db, root)?)), + TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::::from_existing(db, root)?)), + TrieSpec::Fat => Ok(Box::new(FatDBMut::::from_existing(db, root)?)), } } @@ -370,17 +369,15 @@ where /// trait with definition of trie layout -pub trait TrieLayOut: Clone + Send + Sync + Default { +pub trait TrieLayOut { + /// does the trie use extension before its branch + const USE_EXTENSION: bool; type H: Hasher; type C: NodeCodec; - - /// does the trie use extension before its branch - fn uses_extension(&self) -> bool; - fn new_codec(&self) -> Self::C; } /// alias to acces hasher hash output type from a `TrieLayout` -pub type TrieHash = ::Out; +pub type TrieHash = <::H as Hasher>::Out; /// alias to acces `NodeCodec` `Error` type from a `TrieLayout` -pub type CError = >::Error; +pub type CError = <::C as NodeCodec<::H>>::Error; diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index f6653428..0d6bd3ea 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -137,7 +137,7 @@ mod tests { let mut db = MemoryDB::, _>::default(); let mut root = Default::default(); { - let mut x = RefTrieDBMut::new(&mut db, &mut root, Default::default()); + let mut x = RefTrieDBMut::new(&mut db, &mut root); x.insert(b"dog", b"cat").unwrap(); x.insert(b"lunch", b"time").unwrap(); diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index d130ad6b..1c5a50d1 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -91,7 +91,7 @@ mod test { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut db, &mut root); t.insert(&KeccakHasher::hash(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap(); } let t = RefSecTrieDB::new(&db, &root).unwrap(); diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index c7b675a0..ab590ae1 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -32,15 +32,15 @@ where /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Self { - SecTrieDBMut { raw: TrieDBMut::new(db, root, layout) } + pub fn new(db: &'db mut HashDB, root: &'db mut TrieHash) -> Self { + SecTrieDBMut { raw: TrieDBMut::new(db, root) } } /// Create a new trie with the backing database `db` and `root`. /// /// Returns an error if root does not exist. - pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash, layout: L) -> Result, CError> { - Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root, layout)? }) + pub fn from_existing(db: &'db mut HashDB, root: &'db mut TrieHash) -> Result, CError> { + Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. @@ -94,7 +94,7 @@ mod test { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefSecTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefSecTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index d0eb8cbe..eb1d36cb 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -58,7 +58,7 @@ use elastic_array::ElasticArray36; /// fn main() { /// let mut memdb = MemoryDB::, _>::default(); /// let mut root = Default::default(); -/// RefTrieDBMut::new(&mut memdb, &mut root, Default::default()).insert(b"foo", b"bar").unwrap(); +/// RefTrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// let t = RefTrieDB::new(&memdb, &root).unwrap(); /// assert!(t.contains(b"foo").unwrap()); /// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); @@ -183,7 +183,7 @@ where trie: self.trie, node_key: item, partial_key: &self.partial_key, - index: None, + index: None, }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { @@ -394,8 +394,8 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { self.key_nibbles.extend(slice.iter()); self.key_nibbles.push(i); if let Some(ref child) = nodes[i as usize] { - full_key_nibbles += slice.len() + 1; - partial = partial.mid(slice.len() + 1); + full_key_nibbles += slice.len() + 1; + partial = partial.mid(slice.len() + 1); let child = self.db.get_raw_or_lookup(&*child, &key.encoded_leftmost(full_key_nibbles, false))?; child } else { @@ -571,7 +571,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -598,7 +598,7 @@ mod tests { let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -627,7 +627,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -654,7 +654,7 @@ mod tests { let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } @@ -679,7 +679,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } @@ -697,7 +697,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } @@ -716,7 +716,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } @@ -755,7 +755,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } @@ -771,7 +771,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } @@ -790,7 +790,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let root = { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } @@ -858,7 +858,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index e45d10ae..fea88dc1 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -336,7 +336,7 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// fn main() { /// let mut memdb = MemoryDB::, DBValue>::default(); /// let mut root = Default::default(); -/// let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); +/// let mut t = RefTrieDBMut::new(&mut memdb, &mut root); /// assert!(t.is_empty()); /// assert_eq!(*t.root(), KeccakHasher::hash(&[0u8][..])); /// t.insert(b"foo", b"bar").unwrap(); @@ -358,7 +358,6 @@ where /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, - layout: L, } impl<'a, L> TrieDBMut<'a, L> @@ -366,7 +365,7 @@ where L: TrieLayOut, { /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Self { + pub fn new(db: &'a mut HashDB, root: &'a mut TrieHash) -> Self { *root = L::C::hashed_null_node(); let root_handle = NodeHandle::Hash(L::C::hashed_null_node()); @@ -377,13 +376,12 @@ where root_handle, death_row: HashSet::new(), hash_count: 0, - layout } } /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash, layout: L) -> Result, CError> { + pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { if !db.contains(root, nibbleslice::EMPTY_ENCODED) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -396,7 +394,6 @@ where root_handle, death_row: HashSet::new(), hash_count: 0, - layout, }) } /// Get the backing database. @@ -532,7 +529,7 @@ where InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) }, Node::Branch(mut children, stored_value) => { - debug_assert!(self.layout.uses_extension()); + debug_assert!(L::USE_EXTENSION); trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { @@ -565,7 +562,7 @@ where } }, Node::NibbledBranch(encoded, mut children, stored_value) => { - debug_assert!(!self.layout.uses_extension()); + debug_assert!(!L::USE_EXTENSION); trace!(target: "trie", "branch: ROUTE,AUGMENT"); let existing_key = NibbleSlice::from_encoded(&encoded).0; @@ -651,13 +648,13 @@ where true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), } - } else if (self.layout.uses_extension() && cp == 0) - || (!self.layout.uses_extension() && cp < existing_key.len()) { + } else if (L::USE_EXTENSION && cp == 0) + || (!L::USE_EXTENSION && cp < existing_key.len()) { trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // one of us isn't empty: transmute to branch here let mut children = empty_children(); - let branch = if self.layout.uses_extension() && existing_key.is_empty() { + let branch = if L::USE_EXTENSION && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) } else { @@ -665,7 +662,7 @@ where let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - if self.layout.uses_extension() { + if L::USE_EXTENSION { Node::Branch(children, None) } else { Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) @@ -675,7 +672,7 @@ where // always replace because whatever we get out here is not the branch we started with. let branch_action = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch_action) - } else if !self.layout.uses_extension() { + } else if !L::USE_EXTENSION { trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -687,7 +684,7 @@ where InsertAction::Replace(branch) } else if cp == existing_key.len() { - debug_assert!(self.layout.uses_extension()); + debug_assert!(L::USE_EXTENSION); trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -701,7 +698,7 @@ where let branch_handle = self.storage.alloc(Stored::New(branch)).into(); InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) } else { - debug_assert!(self.layout.uses_extension()); + debug_assert!(L::USE_EXTENSION); trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared prefix for an extension. @@ -721,7 +718,7 @@ where } }, Node::Extension(encoded, child_branch) => { - debug_assert!(self.layout.uses_extension()); + debug_assert!(L::USE_EXTENSION); let existing_key = NibbleSlice::from_encoded(&encoded).0; let cp = partial.common_prefix(&existing_key); if cp == 0 { @@ -1308,7 +1305,7 @@ mod tests { root: &'db mut ::Out, v: &[(Vec, Vec)] ) -> RefTrieDBMut<'db> { - let mut t = RefTrieDBMut::new(db, root, Default::default()); + let mut t = RefTrieDBMut::new(db, root); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -1329,7 +1326,7 @@ mod tests { root: &'db mut ::Out, v: &[(Vec, Vec)] ) -> RefTrieDBMutNoExt<'db> { - let mut t = RefTrieDBMutNoExt::new(db, root, Default::default()); + let mut t = RefTrieDBMutNoExt::new(db, root); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -1395,7 +1392,7 @@ mod tests { fn init() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); } @@ -1403,7 +1400,7 @@ mod tests { fn insert_on_empty() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); } @@ -1414,12 +1411,12 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root); t1.insert(&[0x01, 0x23], big_value).unwrap(); t1.insert(&[0x01, 0x34], big_value).unwrap(); let mut memdb2 = MemoryDB::, DBValue>::default(); let mut root2 = Default::default(); - let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2, Default::default()); + let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2); t2.insert(&[0x01], big_value).unwrap(); t2.insert(&[0x01, 0x23], big_value).unwrap(); @@ -1438,10 +1435,10 @@ mod tests { { let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root, Default::default()); + let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root); //t1.insert(&[0x01, 0x23], big_value).unwrap(); //t1.insert(&[0x01, 0x34], big_value).unwrap(); - let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2, Default::default()); + let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2); t2.insert(&[0x01, 0x23], big_value3).unwrap(); t2.insert(&[0x01], big_value2).unwrap(); @@ -1461,7 +1458,7 @@ mod tests { fn insert_replace_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); @@ -1471,7 +1468,7 @@ mod tests { fn insert_make_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1484,7 +1481,7 @@ mod tests { fn insert_into_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -1499,7 +1496,7 @@ mod tests { fn insert_value_into_branch_root() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[], &[0x0]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1512,7 +1509,7 @@ mod tests { fn insert_split_leaf() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1525,7 +1522,7 @@ mod tests { fn insert_split_extenstion() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap(); t.insert(&[0x01, 0xf3, 0x45], &[0x02]).unwrap(); t.insert(&[0x01, 0xf3, 0xf5], &[0x03]).unwrap(); @@ -1543,7 +1540,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x11u8, 0x23], big_value1).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1558,7 +1555,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x11u8, 0x23], big_value).unwrap(); assert_eq!(*t.root(), ref_trie_root(vec![ @@ -1571,7 +1568,7 @@ mod tests { fn test_at_empty() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let t = RefTrieDBMut::new(&mut memdb, &mut root); assert_eq!(t.get(&[0x5]).unwrap(), None); } @@ -1579,7 +1576,7 @@ mod tests { fn test_at_one() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x1u8, 0x23])); t.commit(); @@ -1590,7 +1587,7 @@ mod tests { fn test_at_three() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -1648,12 +1645,12 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut db, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } { - let _ = RefTrieDBMut::from_existing(&mut db, &mut root, Default::default()); + let _ = RefTrieDBMut::from_existing(&mut db, &mut root); } } @@ -1670,7 +1667,7 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut db, &mut root); for &(ref key, ref value) in &x { t.insert(key, value).unwrap(); } @@ -1698,7 +1695,7 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root, Default::default()); + let mut t = RefTrieDBMut::new(&mut db, &mut root); for &(ref key, ref value) in &x { assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); From 1c347451b31c5751257ceee12b207ca70027f501 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 9 Apr 2019 17:13:40 +0200 Subject: [PATCH 019/120] Apply layout trait to non mut trie too. --- test-support/reference-trie/src/lib.rs | 8 +- test-support/trie-bench/src/lib.rs | 2 +- trie-db/src/fatdb.rs | 72 ++++++++--------- trie-db/src/lib.rs | 20 ++--- trie-db/src/sectriedb.rs | 37 ++++----- trie-db/src/triedb.rs | 103 ++++++++++++------------- 6 files changed, 112 insertions(+), 130 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 809e41bd..46af09e4 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -58,13 +58,13 @@ impl TrieLayOut for LayoutNew { type C = ReferenceNodeCodecNoExt; } -pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; -pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodecNoExt>; +pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; +pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, LayoutNew>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, LayoutOri>; pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, LayoutNew>; -pub type RefFatDB<'a> = trie_db::FatDB<'a, KeccakHasher, ReferenceNodeCodec>; +pub type RefFatDB<'a> = trie_db::FatDB<'a, LayoutOri>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; -pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; +pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, LayoutOri>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, LayoutOri>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodec, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodecNoExt, Q>; diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index c789048f..dba47e29 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -72,7 +72,7 @@ where } } b.iter(&mut ||{ - let t = TrieDB::::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); for n in t.iter().unwrap() { black_box(n).unwrap(); } diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 64aad601..b38ad14d 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -13,8 +13,7 @@ // limitations under the License. use hash_db::{HashDBRef, Hasher}; -use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query}; -use node_codec::NodeCodec; +use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, TrieLayOut, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -23,69 +22,64 @@ use alloc::boxed::Box; /// Additionaly it stores inserted hash-key mappings for later retrieval. /// /// Use it as a `Trie` or `TrieMut` trait object. -pub struct FatDB<'db, H, C> +pub struct FatDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayOut, { - raw: TrieDB<'db, H, C>, + raw: TrieDB<'db, L>, } -impl<'db, H, C> FatDB<'db, H, C> +impl<'db, L> FatDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db HashDBRef, root: &'db H::Out) -> Result { + pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { Ok(FatDB { raw: TrieDB::new(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &HashDBRef { self.raw.db() } + pub fn db(&self) -> &HashDBRef { self.raw.db() } } -impl<'db, H, C> Trie for FatDB<'db, H, C> +impl<'db, L> Trie for FatDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&self) -> &H::Out { self.raw.root() } + fn root(&self) -> &TrieHash { self.raw.root() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::H::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(H::hash(key).as_ref(), query) + self.raw.get_with(L::H::hash(key).as_ref(), query) } - fn iter<'a>(&'a self) -> Result> + 'a>, ::Out, C::Error> { - FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) } } /// Itarator over inserted pairs of key values. -pub struct FatDBIterator<'db, H, C> +pub struct FatDBIterator<'db, L> where - H: Hasher + 'db, - C: NodeCodec + 'db + L: TrieLayOut, { - trie_iterator: TrieDBIterator<'db, H, C>, - trie: &'db TrieDB<'db, H, C>, + trie_iterator: TrieDBIterator<'db, L>, + trie: &'db TrieDB<'db, L>, } -impl<'db, H, C> FatDBIterator<'db, H, C> +impl<'db, L> FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Creates new iterator. - pub fn new(trie: &'db TrieDB) -> Result { + pub fn new(trie: &'db TrieDB) -> Result, CError> { Ok(FatDBIterator { trie_iterator: TrieDBIterator::new(trie)?, trie: trie, @@ -93,29 +87,27 @@ where } } -impl<'db, H, C> TrieIterator for FatDBIterator<'db, H, C> +impl<'db, L> TrieIterator for FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> { - let hashed_key = H::hash(key); + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + let hashed_key = L::H::hash(key); self.trie_iterator.seek(hashed_key.as_ref()) } } -impl<'db, H, C> Iterator for FatDBIterator<'db, H, C> +impl<'db, L> Iterator for FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - type Item = TrieItem<'db, H::Out, C::Error>; + type Item = TrieItem<'db, TrieHash, CError>; fn next(&mut self) -> Option { self.trie_iterator.next() .map(|res| { res.map(|(hash, value)| { - let aux_hash = H::hash(&hash); + let aux_hash = L::H::hash(&hash); (self.trie.db().get(&aux_hash, &[]).expect("Missing fatdb hash").into_vec(), value) }) }) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 5ec32122..f8c7d098 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -275,13 +275,13 @@ pub struct TrieFactory { /// All different kinds of tries. /// This is used to prevent a heap allocation for every created trie. -pub enum TrieKinds<'db, H: Hasher + 'db, C: NodeCodec> { +pub enum TrieKinds<'db, L: TrieLayOut> { /// A generic trie db. - Generic(TrieDB<'db, H, C>), + Generic(TrieDB<'db, L>), /// A secure trie db. - Secure(SecTrieDB<'db, H, C>), + Secure(SecTrieDB<'db, L>), /// A fat trie db. - Fat(FatDB<'db, H, C>), + Fat(FatDB<'db, L>), } // wrapper macro for making the match easier to deal with. @@ -295,8 +295,8 @@ macro_rules! wrapper { } } -impl<'db, H: Hasher, C: NodeCodec> Trie for TrieKinds<'db, H, C> { - fn root(&self) -> &H::Out { +impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { + fn root(&self) -> &TrieHash { wrapper!(self, root,) } @@ -304,17 +304,17 @@ impl<'db, H: Hasher, C: NodeCodec> Trie for TrieKinds<'db, H, C> { wrapper!(self, is_empty,) } - fn contains(&self, key: &[u8]) -> Result { + fn contains(&self, key: &[u8]) -> Result, CError> { wrapper!(self, contains, key) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key { wrapper!(self, get_with, key, query) } - fn iter<'a>(&'a self) -> Result> + 'a>, H::Out, C::Error> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { wrapper!(self, iter,) } } @@ -333,7 +333,7 @@ where &self, db: &'db HashDBRef, root: &'db TrieHash - ) -> Result, TrieHash, CError> { + ) -> Result, TrieHash, CError> { match self.spec { TrieSpec::Generic => Ok(TrieKinds::Generic(TrieDB::new(db, root)?)), TrieSpec::Secure => Ok(TrieKinds::Secure(SecTrieDB::new(db, root)?)), diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 1c5a50d1..3ec5b992 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -14,7 +14,7 @@ use hash_db::{HashDBRef, Hasher}; use super::triedb::TrieDB; -use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query}; +use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayOut, CError, TrieHash}; use node_codec::NodeCodec; #[cfg(not(feature = "std"))] @@ -23,57 +23,54 @@ use alloc::boxed::Box; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// /// Use it as a `Trie` trait object. You can use `raw()` to get the backing `TrieDB` object. -pub struct SecTrieDB<'db, H, C> +pub struct SecTrieDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayOut, { - raw: TrieDB<'db, H, C> + raw: TrieDB<'db, L> } -impl<'db, H, C> SecTrieDB<'db, H, C> +impl<'db, L> SecTrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with the backing database `db` and empty `root` /// /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. /// Returns an error if root does not exist. - pub fn new(db: &'db HashDBRef, root: &'db H::Out) -> Result { + pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { Ok(SecTrieDB { raw: TrieDB::new(db, root)? }) } /// Get a reference to the underlying raw `TrieDB` struct. - pub fn raw(&self) -> &TrieDB { + pub fn raw(&self) -> &TrieDB { &self.raw } /// Get a mutable reference to the underlying raw `TrieDB` struct. - pub fn raw_mut(&mut self) -> &mut TrieDB<'db, H, C> { + pub fn raw_mut(&mut self) -> &mut TrieDB<'db, L> { &mut self.raw } } -impl<'db, H, C> Trie for SecTrieDB<'db, H, C> +impl<'db, L> Trie for SecTrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&self) -> &H::Out { self.raw.root() } + fn root(&self) -> &TrieHash { self.raw.root() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::H::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(H::hash(key).as_ref(), query) + self.raw.get_with(L::H::hash(key).as_ref(), query) } - fn iter<'a>(&'a self) -> Result> + 'a>, H::Out, C::Error> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { TrieDB::iter(&self.raw) } } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index eb1d36cb..468ff068 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -17,7 +17,7 @@ use nibbleslice::{self, NibbleSlice, combine_encoded}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; -use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query}; +use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; use ::core_::marker::PhantomData; #[cfg(feature = "std")] @@ -64,38 +64,35 @@ use elastic_array::ElasticArray36; /// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); /// } /// ``` -pub struct TrieDB<'db, H, C> +pub struct TrieDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayOut, { - db: &'db HashDBRef, - root: &'db H::Out, + db: &'db HashDBRef, + root: &'db TrieHash, /// The number of hashes performed so far in operations on this trie. hash_count: usize, - codec_marker: PhantomData, } -impl<'db, H, C> TrieDB<'db, H, C> +impl<'db, L> TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist - pub fn new(db: &'db HashDBRef, root: &'db H::Out) -> Result { + pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { if !db.contains(root, nibbleslice::EMPTY_ENCODED) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { - Ok(TrieDB {db, root, hash_count: 0, codec_marker: PhantomData}) + Ok(TrieDB {db, root, hash_count: 0}) } } /// Get the backing database. - pub fn db(&'db self) -> &'db HashDBRef { self.db } + pub fn db(&'db self) -> &'db HashDBRef { self.db } /// Get the data of the root node. - pub fn root_data(&self) -> Result { + pub fn root_data(&self) -> Result, CError> { self.db .get(self.root, nibbleslice::EMPTY_ENCODED) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) @@ -106,8 +103,8 @@ where /// may require a database lookup. If `is_root_data` then this is root-data and /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. - fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: &[u8]) -> Result, H::Out, C::Error> { - match (partial_key == nibbleslice::EMPTY_ENCODED, C::try_decode_hash(node)) { + fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: &[u8]) -> Result, TrieHash, CError> { + match (partial_key == nibbleslice::EMPTY_ENCODED, L::C::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) @@ -119,25 +116,24 @@ where } } -impl<'db, H, C> Trie for TrieDB<'db, H, C> +impl<'db, L> Trie for TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { - fn root(&self) -> &H::Out { self.root } + fn root(&self) -> &TrieHash { self.root } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key { Lookup { db: self.db, query: query, hash: self.root.clone(), - marker: PhantomData::, + marker: PhantomData::, }.look_up(NibbleSlice::new(key)) } - fn iter<'a>(&'a self) -> Result> + 'a>, H::Out, C::Error> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) } } @@ -145,26 +141,24 @@ where #[cfg(feature="std")] // This is for pretty debug output only -struct TrieAwareDebugNode<'db, 'a, H, C> +struct TrieAwareDebugNode<'db, 'a, L> where - H: Hasher + 'db, - C: NodeCodec + 'db + L: TrieLayOut, { - trie: &'db TrieDB<'db, H, C>, + trie: &'db TrieDB<'db, L>, node_key: &'a[u8], partial_key: &'a ElasticArray36, index: Option, } #[cfg(feature="std")] -impl<'db, 'a, H, C> fmt::Debug for TrieAwareDebugNode<'db, 'a, H, C> +impl<'db, 'a, L> fmt::Debug for TrieAwareDebugNode<'db, 'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, &self.partial_key) { - match C::decode(&node) { + match L::C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { (ref mut d, Some(i)) => d.field("index", &i), @@ -187,7 +181,7 @@ where }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { - let nodes: Vec> = nodes.into_iter() + let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { @@ -206,7 +200,7 @@ where .finish() }, Ok(Node::NibbledBranch(slice, nodes, value)) => { - let nodes: Vec> = nodes.into_iter() + let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { @@ -243,10 +237,9 @@ where } #[cfg(feature="std")] -impl<'db, H, C> fmt::Debug for TrieDB<'db, H, C> +impl<'db, L> fmt::Debug for TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let root_rlp = self.root_data().unwrap(); @@ -293,28 +286,28 @@ impl Crumb { } /// Iterator for going through all values in the trie. -pub struct TrieDBIterator<'a, H: Hasher + 'a, C: NodeCodec + 'a> { - db: &'a TrieDB<'a, H, C>, +pub struct TrieDBIterator<'a, L: TrieLayOut> { + db: &'a TrieDB<'a, L>, trail: Vec, key_nibbles: Vec, } -impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { +impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Create a new iterator. - pub fn new(db: &'a TrieDB) -> Result, H::Out, C::Error> { + pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: Vec::with_capacity(64) }; db.root_data().and_then(|root_data| r.descend(&root_data))?; Ok(r) } - fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key>) -> Result<(), H::Out, C::Error> { + fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key>) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; let mut full_key_nibbles = 0; loop { let data = { - let node = C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; + let node = L::C::decode(&node_data) + .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; match node { Node::Leaf(slice, _) => { if slice >= partial { @@ -413,10 +406,10 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { } /// Descend into a payload. - fn descend(&mut self, d: &[u8]) -> Result<(), H::Out, C::Error> { + fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { let node_data = &self.db.get_raw_or_lookup(d, &self.encoded_key())?; - let node = C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; + let node = L::C::decode(&node_data) + .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) } @@ -460,9 +453,9 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { } } -impl<'a, H: Hasher, C: NodeCodec> TrieIterator for TrieDBIterator<'a, H, C> { +impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { /// Position the iterator on the first element with key >= `key` - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> { + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { self.trail.clear(); self.key_nibbles.clear(); let root_node = self.db.root_data()?; @@ -470,8 +463,8 @@ impl<'a, H: Hasher, C: NodeCodec> TrieIterator for TrieDBIterator<'a, H } } -impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { - type Item = TrieItem<'a, H::Out, C::Error>; +impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { + type Item = TrieItem<'a, TrieHash, CError>; fn next(&mut self) -> Option { enum IterStep<'b, O, E> { @@ -509,7 +502,7 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { return Some(Ok((self.key(), v.clone()))); }, (Status::At, &OwnedNode::Extension(_, ref d)) => { - IterStep::Descend::(self.db.get_raw_or_lookup(&*d, &self.encoded_key())) + IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, &self.encoded_key())) }, (Status::At, &OwnedNode::Branch(_)) | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, @@ -521,7 +514,7 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { i => *self.key_nibbles.last_mut() .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, } - IterStep::Descend::(self.db.get_raw_or_lookup( + IterStep::Descend::, CError>(self.db.get_raw_or_lookup( &branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), &self.encoded_key())) }, @@ -540,11 +533,11 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { IterStep::PopTrail => { self.trail.pop(); }, - IterStep::Descend::(Ok(d)) => { - let node = C::decode(&d).ok()?; + IterStep::Descend::, CError>(Ok(d)) => { + let node = L::C::decode(&d).ok()?; self.descend_into_node(node.into()) }, - IterStep::Descend::(Err(e)) => { + IterStep::Descend::, CError>(Err(e)) => { return Some(Err(e)) } IterStep::Continue => {}, From 12842a67b9f55db5aef7595816563186d87f0486 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 10 Apr 2019 17:46:34 +0200 Subject: [PATCH 020/120] Nibbleslice using inner trait to define multiple encoding: this is awkward for some unresolved types. --- test-support/reference-trie/src/lib.rs | 44 ++--- trie-db/src/fatdb.rs | 6 +- trie-db/src/fatdbmut.rs | 5 +- trie-db/src/iter_build.rs | 88 ++++----- trie-db/src/lib.rs | 58 +++--- trie-db/src/lookup.rs | 23 ++- trie-db/src/nibbleslice.rs | 237 ++++++++++++++++--------- trie-db/src/nibblevec.rs | 25 +-- trie-db/src/node.rs | 21 +-- trie-db/src/node_codec.rs | 8 +- trie-db/src/sectriedb.rs | 5 +- trie-db/src/sectriedbmut.rs | 2 +- trie-db/src/triedb.rs | 29 ++- trie-db/src/triedbmut.rs | 106 +++++------ 14 files changed, 374 insertions(+), 283 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 46af09e4..485619f8 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -37,25 +37,27 @@ use trie_db::{ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record, TrieLayOut}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record, TrieLayOut, NibblePreHalf, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum pub struct LayoutOri; impl TrieLayOut for LayoutOri { - const USE_EXTENSION: bool = true; + const USE_EXTENSION: bool = true; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodec; + type N = NibblePreHalf; } /// trie layout similar to substrate one pub struct LayoutNew; impl TrieLayOut for LayoutNew { - const USE_EXTENSION: bool = false; + const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; + type N = NibblePreHalf; } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; @@ -66,8 +68,8 @@ pub type RefFatDB<'a> = trie_db::FatDB<'a, LayoutOri>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, LayoutOri>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, LayoutOri>; -pub type RefLookup<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodec, Q>; -pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodecNoExt, Q>; +pub type RefLookup<'a, Q> = trie_db::Lookup<'a, LayoutOri, Q>; +pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, LayoutNew, Q>; pub fn ref_trie_root(input: I) -> ::Out where I: IntoIterator, @@ -460,14 +462,14 @@ fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { // `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` // but due to the current limitations of Rust const evaluation we can't // do `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. Perhaps one day soon? -impl NodeCodec for ReferenceNodeCodec { +impl NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; fn hashed_null_node() -> ::Out { KeccakHasher::hash(&[0u8][..]) } - fn decode(data: &[u8]) -> ::std::result::Result { + fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { let input = &mut &*data; match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), @@ -572,14 +574,14 @@ impl NodeCodec for ReferenceNodeCodec { } -impl NodeCodec for ReferenceNodeCodecNoExt { +impl NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; fn hashed_null_node() -> ::Out { - ReferenceNodeCodec::hashed_null_node() + >::hashed_null_node() } - fn decode(data: &[u8]) -> ::std::result::Result { + fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { let input = &mut &*data; let head = NodeHeaderNoExt::decode(input).ok_or(ReferenceError::BadFormat)?; match head { @@ -621,7 +623,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn try_decode_hash(data: &[u8]) -> Option<::Out> { - ReferenceNodeCodec::try_decode_hash(data) + >::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { @@ -689,7 +691,7 @@ pub fn compare_impl + Eq> ( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -731,7 +733,7 @@ pub fn compare_root( ) { let root_new = { let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -751,7 +753,7 @@ pub fn compare_unhashed( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed(data); @@ -764,7 +766,7 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -783,7 +785,7 @@ pub fn calc_root( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -796,7 +798,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -807,7 +809,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -863,7 +865,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -940,8 +942,8 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (noext_cst::LEAF_NODE_OVER as usize + 256) / 2 + 1]; - let enc = ReferenceNodeCodecNoExt::leaf_node(&input, &[1]); - let dec = ReferenceNodeCodecNoExt::decode(&enc).unwrap(); + let enc = >::leaf_node(&input, &[1]); + let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { //assert_eq!(&input[..], &sl.encoded(false)[..]); Some(sl) diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index b38ad14d..0e7c8d04 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -44,7 +44,7 @@ where pub fn db(&self) -> &HashDBRef { self.raw.db() } } -impl<'db, L> Trie for FatDB<'db, L> +impl<'db, L> Trie for FatDB<'db, L> where L: TrieLayOut, { @@ -60,7 +60,7 @@ where self.raw.get_with(L::H::hash(key).as_ref(), query) } - fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) } } @@ -87,7 +87,7 @@ where } } -impl<'db, L> TrieIterator for FatDBIterator<'db, L> +impl<'db, L> TrieIterator for FatDBIterator<'db, L> where L: TrieLayOut, { diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 95c8e62e..58d5f4f9 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -14,7 +14,6 @@ use hash_db::{HashDB, Hasher}; use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayOut, TrieHash, CError}; -use node_codec::NodeCodec; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. @@ -56,7 +55,7 @@ where } } -impl<'db, L> TrieMut for FatDBMut<'db, L> +impl<'db, L> TrieMut for FatDBMut<'db, L> where L: TrieLayOut, { @@ -87,7 +86,7 @@ where Ok(out) } - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { let hash = L::H::hash(key); let out = self.raw.remove(hash.as_ref())?; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index a0d0374d..92eb540a 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -18,9 +18,11 @@ use hash_db::{Hasher, HashDB}; use std::marker::PhantomData; use crate::triedbmut::{ChildReference}; use crate::nibbleslice::NibbleSlice; +use crate::nibbleslice::NibbleOps; use node_codec::NodeCodec; - +// TODO EMCH use L instead of HC (aka TrieLayout) +// TODO EMCH move to NibbleOps to use right constants fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { // sorted assertion preventing out of bound for a in 0..v1.len() { @@ -68,7 +70,7 @@ type CacheNode = Option>; // TODO test others layout // first usize to get nb of added value, second usize last added index // second str is in branch value -struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE], bool, Option)>,PhantomData<(H,C)>); +struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE], bool, Option)>,PhantomData<(H,C,N)>); #[inline(always)] fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { @@ -84,10 +86,11 @@ fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { const INITIAL_DEPTH: usize = 10; const NIBBLE_SIZE: usize = 16; const NIBBLE_PER_BYTES: usize = 2; // 2 ^ 8 / 2 ^ NIBBLE_SIZE -impl CacheAccum +impl CacheAccum where H: Hasher, - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, V: AsRef<[u8]>, { @@ -129,14 +132,14 @@ where ) { let nibble_value = nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],target_depth+1); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); // Note: fwiu, having fixed key size, all values are in leaf (no value in // branch). TODO run metrics on a node to count branch with values let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes // in fact or TODO put Vec in the trait? - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); + let encoded_key = NibbleSlice::::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); let hash = cb_ext.process(&encoded_key[..], encoded, false); // insert hash in branch (first level branch only at this point) @@ -225,13 +228,13 @@ where let v = self.0[branch_d].2.take(); let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); + let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); let branch_hash = cb_ext.process(&encoded_key[..], encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let nib = NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); + let nib = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); let encoded = C::ext_node(&nib[..], branch_hash); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); + let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); let h = cb_ext.process(&encoded_key[..], encoded, is_root); h } else { @@ -251,54 +254,57 @@ where // enc branch let v = self.0[branch_d].2.take(); let enc_nkey = nkey.as_ref() - .map(|nkeyix|NibbleSlice::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); + .map(|nkeyix|NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); enc_nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let encoded_key = NibbleSlice::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); + let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); cb_ext.process(&encoded_key[..], encoded, is_root) } } -pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) +pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, F: ProcessEncodedNode<::Out>, { - trie_visit_inner::(input, cb_ext, true) + trie_visit_inner::(input, cb_ext, true) } -pub fn trie_visit(input: I, cb_ext: &mut F) +pub fn trie_visit(input: I, cb_ext: &mut F) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, F: ProcessEncodedNode<::Out>, { - trie_visit_inner::(input, cb_ext, false) + trie_visit_inner::(input, cb_ext, false) } // put no_ext as a trait: probably not worth it (fn designed for that)? -fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) +fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, F: ProcessEncodedNode<::Out>, { - let mut depth_queue = CacheAccum::::new(); + let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); if let Some(mut prev_val) = iter_input.next() { @@ -338,9 +344,9 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) && !depth_queue.touched(0) { // one single element corner case let (k2, v2) = prev_val; - let nkey = NibbleSlice::new_offset(&k2.as_ref()[..],prev_depth); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],prev_depth); let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); - let encoded_key = NibbleSlice::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); + let encoded_key = NibbleSlice::::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); cb_ext.process(&encoded_key[..], encoded, true); } else { //println!("fbvl {}", prev_depth); @@ -531,10 +537,10 @@ mod test { let hashdb = MemoryDB::::default(); reference_trie::compare_impl_no_ext_unordered_rem(data, rem, memdb, hashdb); }*/ - fn compare_no_ext_insert_remove(data: Vec<(bool, Vec,Vec)>) { + fn compare_no_ext_insert_remove(data: Vec<(bool, Vec,Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); reference_trie::compare_no_ext_insert_remove(data, memdb); - } + } fn compare_root(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); reference_trie::compare_root(data, memdb); @@ -669,12 +675,12 @@ mod test { #[test] fn fuzz_noext_ins_rem_pref () { - let data = vec![ - (false, vec![0], vec![251, 255]), - (false, vec![0,1], vec![251, 255]), - (false, vec![0,1,2], vec![255; 32]), - (true, vec![0,1], vec![0, 251]), - ]; + let data = vec![ + (false, vec![0], vec![251, 255]), + (false, vec![0,1], vec![251, 255]), + (false, vec![0,1,2], vec![255; 32]), + (true, vec![0,1], vec![0, 251]), + ]; compare_no_ext_insert_remove(data); } @@ -688,14 +694,14 @@ mod test { compare_impl_no_ext_pk(data.clone()); } #[test] - #[should_panic] + #[should_panic] fn too_big_nibble_len_old () { compare_impl_h(vec![ (vec![01u8;64],vec![0;32]), ]); } #[test] - #[should_panic] + #[should_panic] fn too_big_nibble_len_new () { compare_impl_no_ext(vec![ (vec![01u8;64 + 255],vec![0;32]), @@ -706,13 +712,13 @@ mod test { /* #[test] fn fdispc () { - let data = vec![ - (vec![0], vec![251;32]), - (vec![0,1], vec![251; 32]), - (vec![0,1,2], vec![251; 32]), - ]; - compare_impl_no_ext_pk(data); - panic!("dd"); - } - */ + let data = vec![ + (vec![0], vec![251;32]), + (vec![0,1], vec![251; 32]), + (vec![0,1,2], vec![251; 32]), + ]; + compare_impl_no_ext_pk(data); + panic!("dd"); + } + */ } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index f8c7d098..b25df82b 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -56,8 +56,6 @@ use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use core_::marker::PhantomData; - #[cfg(feature = "std")] use std::error::Error; @@ -99,7 +97,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::NibbleSlice; +pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf}; pub use node_codec::NodeCodec; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; @@ -188,65 +186,65 @@ impl<'a, F, T, H: Hasher> Query for (&'a mut Recorder, F) where F: Fn } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait Trie> { +pub trait Trie { /// Return the root of the trie. - fn root(&self) -> &H::Out; + fn root(&self) -> &TrieHash; /// Is the trie empty? - fn is_empty(&self) -> bool { *self.root() == C::hashed_null_node() } + fn is_empty(&self) -> bool { *self.root() == L::C::hashed_null_node() } /// Does the trie contain a given key? - fn contains(&self, key: &[u8]) -> Result { + fn contains(&self, key: &[u8]) -> Result, CError> { self.get(key).map(|x|x.is_some() ) } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> where 'a: 'key { + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { self.get_with(key, DBValue::from_slice) } /// Search for the key with the given query parameter. See the docs of the `Query` /// trait for more details. - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q - ) -> Result, H::Out, C::Error> where 'a: 'key; + ) -> Result, TrieHash, CError> where 'a: 'key; /// Returns a depth-first iterator over the elements of trie. - fn iter<'a>(&'a self) -> Result> + 'a>, H::Out, C::Error>; + fn iter<'a>(&'a self) -> Result, CError >> + 'a>, TrieHash, CError>; } /// A key-value datastore implemented as a database-backed modified Merkle tree. /// TODO EMCH switch to use only H::Out and C::Error as trait params?? -pub trait TrieMut> { +pub trait TrieMut { /// Return the root of the trie. - fn root(&mut self) -> &H::Out; + fn root(&mut self) -> &TrieHash; /// Is the trie empty? fn is_empty(&self) -> bool; /// Does the trie contain a given key? - fn contains(&self, key: &[u8]) -> Result { + fn contains(&self, key: &[u8]) -> Result, CError> { self.get(key).map(|x| x.is_some()) } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> where 'a: 'key; + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key; /// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing /// `key` from the trie. Returns the old value associated with this key, if it existed. - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error>; + fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError>; /// Remove a `key` from the trie. Equivalent to making it equal to the empty /// value. Returns the old value associated with this key, if it existed. - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error>; + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError>; } /// A trie iterator that also supports random access (`seek()`). -pub trait TrieIterator>: Iterator { +pub trait TrieIterator: Iterator { /// Position the iterator on the first element with key >= `key` - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, >::Error>; + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError>; } /// Trie types @@ -270,7 +268,7 @@ impl Default for TrieSpec { #[derive(Default, Clone)] pub struct TrieFactory { spec: TrieSpec, - layout: L, + layout: L, } /// All different kinds of tries. @@ -295,7 +293,7 @@ macro_rules! wrapper { } } -impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { +impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { fn root(&self) -> &TrieHash { wrapper!(self, root,) } @@ -314,7 +312,7 @@ impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { wrapper!(self, get_with, key, query) } - fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { wrapper!(self, iter,) } } @@ -342,7 +340,7 @@ where } /// Create new mutable instance of Trie. - pub fn create(&self, db: &'db mut HashDB, root: &'db mut TrieHash) -> Box + 'db> { + pub fn create(&self, db: &'db mut HashDB, root: &'db mut TrieHash) -> Box + 'db> { match self.spec { TrieSpec::Generic => Box::new(TrieDBMut::::new(db, root)), TrieSpec::Secure => Box::new(SecTrieDBMut::::new(db, root)), @@ -355,7 +353,7 @@ where &self, db: &'db mut HashDB, root: &'db mut TrieHash, - ) -> Result + 'db>, TrieHash, CError> { + ) -> Result + 'db>, TrieHash, CError> { match self.spec { TrieSpec::Generic => Ok(Box::new(TrieDBMut::::from_existing(db, root)?)), TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::::from_existing(db, root)?)), @@ -367,17 +365,17 @@ where pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } - /// trait with definition of trie layout pub trait TrieLayOut { - /// does the trie use extension before its branch - const USE_EXTENSION: bool; - type H: Hasher; - type C: NodeCodec; + /// does the trie use extension before its branch + const USE_EXTENSION: bool; + type H: Hasher; + type C: NodeCodec; + type N: NibbleOps; } /// alias to acces hasher hash output type from a `TrieLayout` pub type TrieHash = <::H as Hasher>::Out; /// alias to acces `NodeCodec` `Error` type from a `TrieLayout` -pub type CError = <::C as NodeCodec<::H>>::Error; +pub type CError = <::C as NodeCodec<::H, ::N>>::Error; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index a428d582..34629beb 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -18,32 +18,29 @@ use hash_db::{HashDBRef, Hasher}; use nibbleslice::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query}; -use ::core_::marker::PhantomData; +use super::{DBValue, Result, TrieError, Query, TrieLayOut, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; /// Trie lookup helper object. -pub struct Lookup<'a, H: Hasher + 'a, C: NodeCodec, Q: Query> { +pub struct Lookup<'a, L: TrieLayOut, Q: Query> { /// database to query from. - pub db: &'a HashDBRef, + pub db: &'a HashDBRef, /// Query object to record nodes and transform data. pub query: Q, /// Hash to start at - pub hash: H::Out, - pub marker: PhantomData, // TODO: probably not needed when all is said and done? When Query is made generic? + pub hash: TrieHash, } -impl<'a, H, C, Q> Lookup<'a, H, C, Q> +impl<'a, L, Q> Lookup<'a, L, Q> where - H: Hasher, - C: NodeCodec, - Q: Query, + L: TrieLayOut, + Q: Query, { /// Look up the given key. If the value is found, it will be passed to the given /// function to decode or copy. - pub fn look_up(mut self, key: NibbleSlice) -> Result, H::Out, C::Error> { + pub fn look_up(mut self, key: NibbleSlice) -> Result, TrieHash, CError> { let mut partial = key; let mut hash = self.hash; let mut key_nibbles = 0; @@ -64,7 +61,7 @@ where // without incrementing the depth. let mut node_data = &node_data[..]; loop { - let decoded = match C::decode(node_data) { + let decoded = match L::C::decode(node_data) { Ok(node) => node, Err(e) => { return Err(Box::new(TrieError::DecoderError(hash, e))) @@ -117,7 +114,7 @@ where } // check if new node data is inline or hash. - if let Some(h) = C::try_decode_hash(&node_data) { + if let Some(h) = L::C::try_decode_hash(&node_data) { hash = h; break } diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 51f4b48c..f4f00d2f 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -17,23 +17,126 @@ use ::core_::cmp::*; use ::core_::fmt; use elastic_array::ElasticArray36; +use ::core_::marker::PhantomData; + +// until const fn for pow +const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; + +/// Nibble specific variants +pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy { + /// variant repr + const REPR : ByteLayout; + /// Number of bit per nibble + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); + /// Number of nibble per byte + const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; + /// Number of nibble per node (must be power of 2 and under 256) + const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); + /// Empty nibble encoded + const EMPTY_ENCODED: &'static [u8]; + /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). + fn from_encoded(data: &[u8]) -> (NibbleSlice, bool); + + /// Get the nibble at position `i`. + fn at(&NibbleSlice, i: usize) -> u8; + + /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. + #[inline] + fn encoded(s: &NibbleSlice, is_leaf: bool) -> ElasticArray36 { + Self::encoded_leftmost_unchecked(s, s.len(), is_leaf) + } + + /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, + /// noting whether it `is_leaf`. + fn encoded_leftmost(s: &NibbleSlice, n: usize, is_leaf: bool) -> ElasticArray36 { + let l = min(s.len(), n); + Self::encoded_leftmost_unchecked(s, l, is_leaf) + } + + /// encoded leftmost without checking end bound + fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36; + +} + +/// half byte nibble prepend encoding +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +pub struct NibblePreHalf; + + +/// Type of nibble in term of byte size +#[repr(usize)] +pub enum ByteLayout { + /// nibble of one bit length + Bit = 0, // 1, 8, 2 + /// nibble of a quarter byte length + Quarter = 1, // 2, 4, 4 + /// nibble of a half byte length + Half = 2, // 4, 2, 16 + /// nibble of one byte length + Full = 3, // 8, 1, 256 +} + +impl NibbleOps for NibblePreHalf { + const EMPTY_ENCODED: &'static [u8] = &[0]; + const REPR: ByteLayout = ByteLayout::Half; + + fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { + if data.is_empty() { + (NibbleSlice::::new(&[]), false) + } else { + (NibbleSlice::::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32) + } + } + + #[inline(always)] + fn at(s: &NibbleSlice, i: usize) -> u8 { + let l = s.data.len() * Self::NIBBLE_PER_BYTE - s.offset; + if i < l { + if (s.offset + i) & 1 == 1 { + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 + } + else { + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 + } + } else { + let i = i - l; + if (s.offset_encode_suffix + i) & 1 == 1 { + s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] & 15u8 + } + else { + s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] >> 4 + } + } + } + + fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 {NIBBLE_ODD_MASK + Self::at(s, 0)} else {0} + + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); + while i < l { + r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); + i += 2; + } + r + } -// TODO EMCH Nibbleslice encoder trait that notably contains output type. -// EMPTY_ENCODED must be part of the trait +} + +// TODO EMCH NIBBLE api from full key without concat?? -/// Empty slice encoded as non-leaf partial key pub const EMPTY_ENCODED: &[u8] = &[0]; /// mask for nibble encoded first byte for extension -pub const NIBBLE_EXT_MASK: u8 = 0x00; +const NIBBLE_EXT_MASK: u8 = 0x00; /// mask for nibble encoded first byte for leaf -pub const NIBBLE_ODD_MASK: u8 = 0x10; +const NIBBLE_ODD_MASK: u8 = 0x10; /// mask for nibble encoded first byte for leaf -pub const NIBBLE_LEAF_MASK: u8 = 0x20; +const NIBBLE_LEAF_MASK: u8 = 0x20; /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// @@ -57,21 +160,22 @@ pub const NIBBLE_LEAF_MASK: u8 = 0x20; /// assert_eq!(n2.mid(3).common_prefix(&n1), 3); /// } /// ``` -#[derive(Copy, Clone, Eq, Ord)] -pub struct NibbleSlice<'a> { +#[derive(Copy, Clone)] +pub struct NibbleSlice<'a, N> { data: &'a [u8], offset: usize, data_encode_suffix: &'a [u8], offset_encode_suffix: usize, + marker: PhantomData, } /// Iterator type for a nibble slice. -pub struct NibbleSliceIterator<'a> { - p: &'a NibbleSlice<'a>, +pub struct NibbleSliceIterator<'a, N> { + p: &'a NibbleSlice<'a, N>, i: usize, } -impl<'a> Iterator for NibbleSliceIterator<'a> { +impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { type Item = u8; fn next(&mut self) -> Option { self.i += 1; @@ -82,7 +186,7 @@ impl<'a> Iterator for NibbleSliceIterator<'a> { } } -impl<'a> NibbleSlice<'a> { +impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice. pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_offset(data, 0) } @@ -92,32 +196,30 @@ impl<'a> NibbleSlice<'a> { data, offset, data_encode_suffix: &b""[..], - offset_encode_suffix: 0 + offset_encode_suffix: 0, + marker: PhantomData, } } /// Create a composed nibble slice; one followed by the other. - pub fn new_composed(a: &NibbleSlice<'a>, b: &NibbleSlice<'a>) -> Self { + pub fn new_composed(a: &Self, b: &Self) -> Self { NibbleSlice { data: a.data, offset: a.offset, data_encode_suffix: b.data, - offset_encode_suffix: b.offset + offset_encode_suffix: b.offset, + marker: PhantomData, } } /// Get an iterator for the series of nibbles. - pub fn iter(&'a self) -> NibbleSliceIterator<'a> { + pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { NibbleSliceIterator { p: self, i: 0 } } /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). - pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) { - if data.is_empty() { - (Self::new(&[]), false) - } else { - (Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32) - } + pub fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { + N::from_encoded(data) } /// Is this an empty slice? @@ -125,38 +227,22 @@ impl<'a> NibbleSlice<'a> { /// Get the length (in nibbles, naturally) of this slice. #[inline] - pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * 2 - self.offset - self.offset_encode_suffix } + pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * N::NIBBLE_PER_BYTE - self.offset - self.offset_encode_suffix } /// Get the nibble at position `i`. #[inline(always)] pub fn at(&self, i: usize) -> u8 { - let l = self.data.len() * 2 - self.offset; - if i < l { - if (self.offset + i) & 1 == 1 { - self.data[(self.offset + i) / 2] & 15u8 - } - else { - self.data[(self.offset + i) / 2] >> 4 - } - } - else { - let i = i - l; - if (self.offset_encode_suffix + i) & 1 == 1 { - self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] & 15u8 - } - else { - self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] >> 4 - } - } + N::at(&self, i) } /// Return object which represents a view on to this slice (further) offset by `i` nibbles. - pub fn mid(&self, i: usize) -> NibbleSlice<'a> { + pub fn mid(&self, i: usize) -> NibbleSlice<'a, N> { NibbleSlice { data: self.data, offset: self.offset + i, data_encode_suffix: &b""[..], - offset_encode_suffix: 0 + offset_encode_suffix: 0, + marker: PhantomData, } } @@ -177,56 +263,46 @@ impl<'a> NibbleSlice<'a> { /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. #[inline] pub fn encoded(&self, is_leaf: bool) -> ElasticArray36 { - let l = self.len(); - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {NIBBLE_ODD_MASK + self.at(0)} else {0} - + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); - while i < l { - r.push(self.at(i) * 16 + self.at(i + 1)); - i += 2; - } - r + N::encoded(self, is_leaf) } /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, /// noting whether it `is_leaf`. pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36 { - let l = min(self.len(), n); - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {NIBBLE_ODD_MASK + self.at(0)} else {0} - + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); - while i < l { - r.push(self.at(i) * 16 + self.at(i + 1)); - i += 2; - } - r + N::encoded_leftmost(self, n, is_leaf) } } -impl<'a> PartialEq for NibbleSlice<'a> { +impl<'a, N: NibbleOps> PartialEq for NibbleSlice<'a, N> { fn eq(&self, them: &Self) -> bool { self.len() == them.len() && self.starts_with(them) } } -impl<'a> PartialOrd for NibbleSlice<'a> { +impl<'a, N: NibbleOps> Eq for NibbleSlice<'a, N> { } + +impl<'a, N: NibbleOps> PartialOrd for NibbleSlice<'a, N> { fn partial_cmp(&self, them: &Self) -> Option { + Some(self.cmp(them)) + } +} + +impl<'a, N: NibbleOps> Ord for NibbleSlice<'a, N> { + fn cmp(&self, them: &Self) -> Ordering { let s = min(self.len(), them.len()); let mut i = 0usize; while i < s { match self.at(i).partial_cmp(&them.at(i)).unwrap() { - Ordering::Less => return Some(Ordering::Less), - Ordering::Greater => return Some(Ordering::Greater), + Ordering::Less => return Ordering::Less, + Ordering::Greater => return Ordering::Greater, _ => i += 1, } } - self.len().partial_cmp(&them.len()) + self.len().cmp(&them.len()) } } -impl<'a> fmt::Debug for NibbleSlice<'a> { +impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in 0..self.len() { match i { @@ -239,27 +315,28 @@ impl<'a> fmt::Debug for NibbleSlice<'a> { } /// Join two encoded nibble slices. -pub fn combine_encoded(prefix: &[u8], extension: &[u8]) -> ElasticArray36 { - let slice = NibbleSlice::new_composed(&NibbleSlice::from_encoded(&prefix).0, &NibbleSlice::from_encoded(extension).0); +pub fn combine_encoded(prefix: &[u8], extension: &[u8]) -> ElasticArray36 { + let slice = NibbleSlice::::new_composed(&NibbleSlice::from_encoded(&prefix).0, &NibbleSlice::from_encoded(extension).0); slice.encoded(false) } #[cfg(test)] mod tests { use super::NibbleSlice; + use super::NibblePreHalf; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; #[test] fn basics() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); assert_eq!(n.len(), 6); assert!(!n.is_empty()); - let n = NibbleSlice::new_offset(D, 6); + let n = NibbleSlice::::new_offset(D, 6); assert!(n.is_empty()); - let n = NibbleSlice::new_offset(D, 3); + let n = NibbleSlice::::new_offset(D, 3); assert_eq!(n.len(), 3); for i in 0..3 { assert_eq!(n.at(i), i as u8 + 3); @@ -268,7 +345,7 @@ mod tests { #[test] fn iterator() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); let mut nibbles: Vec = vec![]; nibbles.extend(n.iter()); assert_eq!(nibbles, (0u8..6).collect::>()) @@ -276,7 +353,7 @@ mod tests { #[test] fn mid() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); let m = n.mid(2); for i in 0..4 { assert_eq!(m.at(i), i as u8 + 2); @@ -289,7 +366,7 @@ mod tests { #[test] fn encoded() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45])); @@ -298,7 +375,7 @@ mod tests { #[test] fn from_encoded() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45])); assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45])); assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45])); @@ -307,7 +384,7 @@ mod tests { #[test] fn shared() { - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; let m = NibbleSlice::new(other); @@ -324,7 +401,7 @@ mod tests { #[test] fn compare() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; - let n = NibbleSlice::new(D); + let n = NibbleSlice::::new(D); let m = NibbleSlice::new(other); assert!(n != m); diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index 242919d4..f4cc2ec3 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -15,26 +15,30 @@ //! An owning, nibble-oriented byte vector. use elastic_array::ElasticArray36; use nibbleslice::NibbleSlice; +use nibbleslice::NibbleOps; +use ::core_::marker::PhantomData; /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct NibbleVec { +pub struct NibbleVec { inner: ElasticArray36, len: usize, + marker: PhantomData, } -impl Default for NibbleVec { +impl Default for NibbleVec { fn default() -> Self { - NibbleVec::new() + NibbleVec::::new() } } -impl NibbleVec { +impl NibbleVec { /// Make a new `NibbleVec` pub fn new() -> Self { NibbleVec { inner: ElasticArray36::new(), - len: 0 + len: 0, + marker: PhantomData, } } @@ -87,7 +91,7 @@ impl NibbleVec { } /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. - pub fn as_nibbleslice(&self) -> Option { + pub fn as_nibbleslice(&self) -> Option> { if self.len % 2 == 0 { Some(NibbleSlice::new(self.inner())) } else { @@ -101,8 +105,8 @@ impl NibbleVec { } } -impl<'a> From> for NibbleVec { - fn from(s: NibbleSlice<'a>) -> Self { +impl<'a, N: NibbleOps> From> for NibbleVec { + fn from(s: NibbleSlice<'a, N>) -> Self { let mut v = NibbleVec::new(); for i in 0..s.len() { v.push(s.at(i)); @@ -114,10 +118,11 @@ impl<'a> From> for NibbleVec { #[cfg(test)] mod tests { use super::NibbleVec; + use nibbleslice::NibblePreHalf; #[test] fn push_pop() { - let mut v = NibbleVec::new(); + let mut v = NibbleVec::::new(); for i in 0..16 { v.push(i); @@ -138,7 +143,7 @@ mod tests { v.push(i); } - let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); + let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); assert_eq!(v, v2); } } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 187f6fc6..ab1a182c 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -14,6 +14,7 @@ use elastic_array::ElasticArray36; use nibbleslice::NibbleSlice; +use nibbleslice::NibbleOps; use nibblevec::NibbleVec; use super::DBValue; @@ -25,17 +26,17 @@ pub type NodeKey = ElasticArray36; /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Debug, Clone)] -pub enum Node<'a> { +pub enum Node<'a, N: NibbleOps> { /// Null trie node; could be an empty root or an empty branch entry. Empty, /// Leaf node; has key slice and value. Value may not be empty. - Leaf(NibbleSlice<'a>, &'a [u8]), + Leaf(NibbleSlice<'a, N>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. - Extension(NibbleSlice<'a>, &'a [u8]), + Extension(NibbleSlice<'a, N>, &'a [u8]), /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), /// Branch node with support for a nibble (to avoid extension node) - NibbledBranch(NibbleSlice<'a>, [Option<&'a [u8]>; 16], Option<&'a [u8]>), + NibbledBranch(NibbleSlice<'a, N>, [Option<&'a [u8]>; 16], Option<&'a [u8]>), } /// A Sparse (non mutable) owned vector struct to hold branch keys and value @@ -93,21 +94,21 @@ impl Branch { /// An owning node type. Useful for trie iterators. #[derive(Debug, PartialEq, Eq)] -pub enum OwnedNode { +pub enum OwnedNode { /// Empty trie node. Empty, /// Leaf node: partial key and value. - Leaf(NibbleVec, DBValue), + Leaf(NibbleVec, DBValue), /// Extension node: partial key and child node. - Extension(NibbleVec, DBValue), + Extension(NibbleVec, DBValue), /// Branch node: 16 children and an optional value. Branch(Branch), /// Branch node: 16 children and an optional value. - NibbledBranch(NibbleVec, Branch), + NibbledBranch(NibbleVec, Branch), } -impl<'a> From> for OwnedNode { - fn from(node: Node<'a>) -> Self { +impl<'a, N: NibbleOps> From> for OwnedNode { + fn from(node: Node<'a, N>) -> Self { match node { Node::Empty => OwnedNode::Empty, Node::Leaf(k, v) => OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 8dfb3fc5..0c3adcfa 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -17,6 +17,7 @@ use hash_db::Hasher; use node::Node; +use nibbleslice::NibbleOps; use ChildReference; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -42,15 +43,18 @@ impl Error for T {} /// Trait for trie node encoding/decoding /// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> /// avoid Vec by all means. -pub trait NodeCodec: Sized { +pub trait NodeCodec: Sized { /// Codec error type type Error: Error; + // TODO EMCH since refact to use nibble ops: use of this method is super awkward + // -> making reference code implement statically other nibble could be the simpler + // way of fixing that /// Get the hashed null node. fn hashed_null_node() -> H::Out; /// Decode bytes to a `Node`. Returns `Self::E` on failure. - fn decode(data: &[u8]) -> Result; + fn decode(data: &[u8]) -> Result, Self::Error>; /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. fn try_decode_hash(data: &[u8]) -> Option; diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 3ec5b992..801b3f04 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -15,7 +15,6 @@ use hash_db::{HashDBRef, Hasher}; use super::triedb::TrieDB; use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayOut, CError, TrieHash}; -use node_codec::NodeCodec; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -54,7 +53,7 @@ where } } -impl<'db, L> Trie for SecTrieDB<'db, L> +impl<'db, L> Trie for SecTrieDB<'db, L> where L: TrieLayOut, { @@ -70,7 +69,7 @@ where self.raw.get_with(L::H::hash(key).as_ref(), query) } - fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { TrieDB::iter(&self.raw) } } diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index ab590ae1..81194878 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -50,7 +50,7 @@ where pub fn db_mut(&mut self) -> &mut HashDB { self.raw.db_mut() } } -impl<'db, L> TrieMut for SecTrieDBMut<'db, L> +impl<'db, L> TrieMut for SecTrieDBMut<'db, L> where L: TrieLayOut, { diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 468ff068..be21a28a 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{Hasher, HashDBRef}; -use nibbleslice::{self, NibbleSlice, combine_encoded}; +use nibbleslice::{self, NibbleSlice, combine_encoded, NibbleOps}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; @@ -116,24 +116,23 @@ where } } -impl<'db, L> Trie for TrieDB<'db, L> +impl<'db, L> Trie for TrieDB<'db, L> where L: TrieLayOut, { fn root(&self) -> &TrieHash { self.root } fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> - where 'a: 'key + where 'a: 'key, { - Lookup { + Lookup:: { // TODO EMCH rem type db: self.db, query: query, hash: self.root.clone(), - marker: PhantomData::, }.look_up(NibbleSlice::new(key)) } - fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) } } @@ -264,12 +263,12 @@ enum Status { } #[derive(Eq, PartialEq, Debug)] -struct Crumb { - node: OwnedNode, +struct Crumb { + node: OwnedNode, status: Status, } -impl Crumb { +impl Crumb { /// Move on to next status in the node's sequence. fn increment(&mut self) { self.status = match (&self.status, &self.node) { @@ -288,7 +287,7 @@ impl Crumb { /// Iterator for going through all values in the trie. pub struct TrieDBIterator<'a, L: TrieLayOut> { db: &'a TrieDB<'a, L>, - trail: Vec, + trail: Vec>, key_nibbles: Vec, } @@ -300,7 +299,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { Ok(r) } - fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key>) -> Result<(), TrieHash, CError> { + fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key, L::N>) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; let mut full_key_nibbles = 0; @@ -414,7 +413,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } /// Descend into a payload. - fn descend_into_node(&mut self, node: OwnedNode) { + fn descend_into_node(&mut self, node: OwnedNode) { self.trail.push(Crumb { status: Status::Entering, node }); match &self.trail.last().expect("just pushed item; qed").node { &OwnedNode::Leaf(ref n, _) @@ -444,7 +443,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Encoded key for storage lookup fn encoded_key(&self) -> ElasticArray36 { let key = self.key(); - let slice = NibbleSlice::new(&key); + let slice = NibbleSlice::::new(&key); if self.key_nibbles.len() % 2 == 1 { NibbleSlice::new_composed(&slice, &NibbleSlice::new_offset(&self.key_nibbles[(self.key_nibbles.len() - 1)..], 1)).encoded(false) } else { @@ -453,7 +452,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } } -impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { +impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { /// Position the iterator on the first element with key >= `key` fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { self.trail.clear(); @@ -860,7 +859,7 @@ mod tests { // query for an invalid data type to trigger an error let q = |x: &[u8]| x.len() < 64; - let lookup = RefLookup { db: t.db(), query: q, hash: root, marker: PhantomData }; + let lookup = RefLookup { db: t.db(), query: q, hash: root }; let query_result = lookup.look_up(NibbleSlice::new(b"A")); assert_eq!(query_result.unwrap().unwrap(), true); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index fea88dc1..f5d01804 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -21,7 +21,7 @@ use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; use hash_db::{HashDB, Hasher}; -use nibbleslice::{self, NibbleSlice, combine_encoded}; +use nibbleslice::{self, NibbleSlice, combine_encoded, NibbleOps}; use ::core_::marker::PhantomData; use ::core_::mem; @@ -70,13 +70,13 @@ fn empty_children() -> Box<[Option>; 16]> { ]) } -struct Partial<'key> { - key: NibbleSlice<'key>, +struct Partial<'key, N> { + key: NibbleSlice<'key, N>, split: usize, } -impl<'key> Partial<'key> { - fn new(key: NibbleSlice) -> Partial { +impl<'key, N: NibbleOps> Partial<'key, N> { + fn new(key: NibbleSlice) -> Partial { Partial { key, split: 0, @@ -87,7 +87,7 @@ impl<'key> Partial<'key> { self.split += by; } - fn mid(&self) -> NibbleSlice<'key> { + fn mid(&self) -> NibbleSlice<'key, N> { self.key.mid(self.split) } @@ -121,31 +121,32 @@ where O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy { // load an inline node into memory or get the hash to do the lookup later. - fn inline_or_hash( + fn inline_or_hash( node: &[u8], db: &HashDB, storage: &mut NodeStorage ) -> NodeHandle where - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, H: Hasher, { C::try_decode_hash(&node) .map(NodeHandle::Hash) .unwrap_or_else(|| { - let child = Node::from_encoded::(node, db, storage); + let child = Node::from_encoded::(node, db, storage); NodeHandle::InMemory(storage.alloc(Stored::New(child))) }) } // decode a node from encoded bytes without getting its children. - fn from_encoded<'a, 'b, C, H>(data: &'a[u8], db: &HashDB, storage: &'b mut NodeStorage) -> Self - where C: NodeCodec, H: Hasher, + fn from_encoded<'a, 'b, C, H, N>(data: &'a[u8], db: &HashDB, storage: &'b mut NodeStorage) -> Self + where N: NibbleOps, C: NodeCodec, H: Hasher, { let dec_children = |encoded_children: &[Option<&'a [u8]>; 16], storage: &'b mut NodeStorage| { let mut child = |i:usize| { encoded_children[i].map(|data| - Self::inline_or_hash::(data, db, storage) + Self::inline_or_hash::(data, db, storage) ) }; @@ -163,7 +164,7 @@ where EncodedNode::Extension(key, cb) => { Node::Extension( key.encoded(false), - Self::inline_or_hash::(cb, db, storage)) + Self::inline_or_hash::(cb, db, storage)) }, EncodedNode::Branch(encoded_children, val) => { let children = dec_children(&encoded_children, storage); @@ -177,9 +178,10 @@ where } // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec + fn into_encoded(self, mut child_cb: F) -> Vec where - C: NodeCodec, + N: NibbleOps, + C: NodeCodec, F: FnMut(NodeHandle, &NodeKey) -> ChildReference, H: Hasher, { @@ -194,7 +196,7 @@ where .map(Option::take) .enumerate() .map(|(i, maybe_child)| - maybe_child.map(|child| child_cb(child, &NibbleSlice::new_offset(&[i as u8], 1).encoded(false))) + maybe_child.map(|child| child_cb(child, &NibbleSlice::::new_offset(&[i as u8], 1).encoded(false))) ), value.as_ref().map(|v|&v[..]) ) @@ -209,7 +211,7 @@ where .map(|(i, maybe_child)|{ let branch_ix = [i as u8]; // costy - let nibble = NibbleSlice::new_composed(&NibbleSlice::from_encoded(&partial).0, &NibbleSlice::new_offset(&branch_ix, 1)); + let nibble = NibbleSlice::::new_composed(&NibbleSlice::from_encoded(&partial).0, &NibbleSlice::new_offset(&branch_ix, 1)); maybe_child.map(|child| child_cb(child, &nibble.encoded(false))) }), value.as_ref().map(|v|&v[..]) @@ -409,7 +411,7 @@ where // cache a node by hash fn cache(&mut self, hash: TrieHash, key: &[u8]) -> Result, CError> { let node_encoded = self.db.get(&hash, key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + let node = Node::from_encoded::( &node_encoded, &*self.db, &mut self.storage @@ -419,8 +421,8 @@ where // inspect a node, choosing either to replace, restore, or delete it. // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored>, key: &mut Partial, inspector: F) -> Result>, bool)>, TrieHash, CError> - where F: FnOnce(&mut Self, Node>, &mut Partial) -> Result>, TrieHash, CError> { + fn inspect(&mut self, stored: Stored>, key: &mut Partial, inspector: F) -> Result>, bool)>, TrieHash, CError> + where F: FnOnce(&mut Self, Node>, &mut Partial) -> Result>, TrieHash, CError> { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -442,17 +444,16 @@ where } // walk the trie, attempting to find the key's node. - fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle>) -> Result, TrieHash, CError> + fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key, L::N>, handle: &NodeHandle>) -> Result, TrieHash, CError> where 'x: 'key { let mut handle = handle; loop { let (mid, child) = match *handle { - NodeHandle::Hash(ref hash) => return Lookup { + NodeHandle::Hash(ref hash) => return Lookup:: { db: &self.db, query: DBValue::from_slice, hash: hash.clone(), - marker: PhantomData::, }.look_up(partial), NodeHandle::InMemory(ref handle) => match self.storage[handle] { Node::Empty => return Ok(None), @@ -505,7 +506,7 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { + fn insert_at(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, @@ -519,7 +520,7 @@ where } /// the insertion inspector. - fn insert_inspector(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn insert_inspector(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); @@ -778,7 +779,7 @@ where } /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { + fn remove_at(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { @@ -793,7 +794,7 @@ where } /// the removal inspector - fn remove_inspector(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn remove_inspector(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, @@ -896,7 +897,7 @@ where Action::Delete } else { // leaf the node alone. - trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); + trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::::from_encoded(&encoded).0); Action::Restore(Node::Leaf(encoded, value)) } }, @@ -967,7 +968,7 @@ where (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. make an extension. - let new_partial = NibbleSlice::new_offset(&[a], 1).encoded(false); + let new_partial = NibbleSlice::::new_offset(&[a], 1).encoded(false); let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); self.fix(new_node, key) @@ -975,7 +976,7 @@ where (UsedIndex::None, Some(value)) => { // make a leaf. trace!(target: "trie", "fixing: branch -> leaf"); - Ok(Node::Leaf(NibbleSlice::new(&[]).encoded(true), value)) + Ok(Node::Leaf(NibbleSlice::::new(&[]).encoded(true), value)) } (_, value) => { // all is well. @@ -1013,12 +1014,12 @@ where NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { // TODO EMCH optimize this concat (new_partial_tmp may be calc again afterward) - let new_partial_tmp = NibbleSlice::new_composed( + let new_partial_tmp = NibbleSlice::::new_composed( &NibbleSlice::from_encoded(&enc_nibble).0, &NibbleSlice::new_offset(&[a], 1) ).encoded(false); - let handle = self.cache(h, &combine_encoded(&key, &new_partial_tmp))?; + let handle = self.cache(h, &combine_encoded::(&key, &new_partial_tmp))?; self.storage.destroy(handle) } }; @@ -1033,11 +1034,11 @@ where Node::Leaf(sub_partial, value) => { let ix = [a]; // warning cannot compose more than one time - let new_partial_tmp = NibbleSlice::new_composed( + let new_partial_tmp = NibbleSlice::::new_composed( &NibbleSlice::from_encoded(&enc_nibble).0, &NibbleSlice::new_offset(&ix, 1) ).encoded(false); - let new_partial = NibbleSlice::new_composed( + let new_partial = NibbleSlice::::new_composed( &NibbleSlice::from_encoded(&new_partial_tmp).0, &NibbleSlice::from_encoded(&sub_partial).0); Ok(Node::Leaf(new_partial.encoded(false), value)) @@ -1045,11 +1046,11 @@ where Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let ix = [a]; // warning cannot compose more than one time - let new_partial_tmp = NibbleSlice::new_composed( + let new_partial_tmp = NibbleSlice::::new_composed( &NibbleSlice::from_encoded(&enc_nibble).0, &NibbleSlice::new_offset(&ix, 1) ).encoded(false); - let new_partial = NibbleSlice::new_composed( + let new_partial = NibbleSlice::::new_composed( &NibbleSlice::from_encoded(&new_partial_tmp).0, &NibbleSlice::from_encoded(&sub_partial).0); Ok(Node::NibbledBranch(new_partial.encoded(false), ch_children, ch_value)) @@ -1060,7 +1061,7 @@ where (UsedIndex::None, Some(value)) => { // make a leaf. trace!(target: "trie", "fixing: branch -> leaf"); - let partial = NibbleSlice::from_encoded(&enc_nibble).0; + let partial = NibbleSlice::::from_encoded(&enc_nibble).0; // TODO encoded switch can be almost costless instead of this enc/dec Ok(Node::Leaf(partial.encoded(true), value)) }, @@ -1075,7 +1076,7 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &combine_encoded(&key, &partial))?; + let handle = self.cache(h, &combine_encoded::(&key, &partial))?; self.storage.destroy(handle) } }; @@ -1092,7 +1093,7 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, key.clone())); } - let partial = NibbleSlice::from_encoded(&partial).0; + let partial = NibbleSlice::::from_encoded(&partial).0; let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); @@ -1106,7 +1107,7 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, key)); } - let partial = NibbleSlice::from_encoded(&partial).0; + let partial = NibbleSlice::::from_encoded(&partial).0; let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); @@ -1149,7 +1150,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let encoded_root = node.into_encoded::<_, L::C, L::H>(|child, k| { + let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, k| { self.commit_child(child, k) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); @@ -1180,10 +1181,10 @@ where Stored::New(node) => { let encoded = { let commit_child = |node_handle, partial: &NodeKey| { - let combined = combine_encoded(&prefix, partial); + let combined = combine_encoded::(&prefix, partial); self.commit_child(node_handle, &combined) }; - node.into_encoded::<_, L::C, L::H>(commit_child) + node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; if encoded.len() >= L::H::LENGTH { let hash = self.db.insert(&prefix, &encoded[..]); @@ -1211,7 +1212,7 @@ where } } -impl<'a, L> TrieMut for TrieDBMut<'a, L> +impl<'a, L> TrieMut for TrieDBMut<'a, L> where L: TrieLayOut, { @@ -1297,8 +1298,8 @@ mod tests { use memory_db::{MemoryDB, PrefixedKey, HashKey}; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, NodeCodec, - ReferenceNodeCodec, ref_trie_root, RefTrieDB, RefTrieDBNoExt}; + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, + ReferenceNodeCodec, ref_trie_root, RefTrieDB, RefTrieDBNoExt, LayoutOri}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1376,15 +1377,16 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie(&mut memtrie, &x); memtrie.commit(); - if *memtrie.root() != ReferenceNodeCodec::hashed_null_node() { + let hashed_null_node = ::N>>::hashed_null_node(); + if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); - println!("{:#x?} vs {:#x?}", memtrie.root(), ReferenceNodeCodec::hashed_null_node()); + println!("{:#x?} vs {:#x?}", memtrie.root(), hashed_null_node); for i in &x { println!("{:#x?} -> {:#x?}", i.0, i.1); } } - assert_eq!(*memtrie.root(), ReferenceNodeCodec::hashed_null_node()); + assert_eq!(*memtrie.root(), hashed_null_node); } } @@ -1393,7 +1395,8 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); + let hashed_null_node = ::N>>::hashed_null_node(); + assert_eq!(*t.root(), hashed_null_node); } #[test] @@ -1679,7 +1682,8 @@ mod tests { } assert!(t.is_empty()); - assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); + let hashed_null_node = ::N>>::hashed_null_node(); + assert_eq!(*t.root(), hashed_null_node); } #[test] From 4478ecefb39f5cf97119554ee9a6955b0e91a85d Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 10 Apr 2019 18:12:45 +0200 Subject: [PATCH 021/120] NibbleVec move size arelated methods. --- trie-db/src/nibbleslice.rs | 48 ++++++++++++++++++++++++++++++++ trie-db/src/nibblevec.rs | 56 ++++---------------------------------- 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index f4f00d2f..5ccb7613 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -18,6 +18,7 @@ use ::core_::cmp::*; use ::core_::fmt; use elastic_array::ElasticArray36; use ::core_::marker::PhantomData; +use nibblevec::NibbleVec; // until const fn for pow const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; @@ -57,6 +58,15 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// encoded leftmost without checking end bound fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36; + /// Try to get the nibble at the given offset. + fn vec_at(s: &NibbleVec, idx: usize) -> u8; + + /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. + fn push(s: &mut NibbleVec, nibble: u8); + + /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. + fn pop(s: &mut NibbleVec) -> Option; + } /// half byte nibble prepend encoding @@ -122,6 +132,44 @@ impl NibbleOps for NibblePreHalf { r } + #[inline] + fn vec_at(s: &NibbleVec, idx: usize) -> u8 { + if idx % 2 == 0 { + s.inner[idx / 2] >> 4 + } else { + s.inner[idx / 2] & 0x0F + } + } + + fn push(s: &mut NibbleVec, nibble: u8) { + let nibble = nibble & 0x0F; + + if s.len % 2 == 0 { + s.inner.push(nibble << 4); + } else { + *s.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + + s.len += 1; + } + + fn pop(s: &mut NibbleVec) -> Option { + if s.is_empty() { + return None; + } + + let byte = s.inner.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if s.len % 2 == 0 { + s.inner.push(byte & 0xF0); + byte & 0x0F + } else { + byte >> 4 + }; + + s.len -= 1; + Some(nibble) + } + } // TODO EMCH NIBBLE api from full key without concat?? diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index f4cc2ec3..537d3bf3 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -18,11 +18,12 @@ use nibbleslice::NibbleSlice; use nibbleslice::NibbleOps; use ::core_::marker::PhantomData; +// TODO EMCH change crate layout to give access to nibble vec field to nibble ops and avoid pub(crate) /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. #[derive(Clone, PartialEq, Eq, Debug)] pub struct NibbleVec { - inner: ElasticArray36, - len: usize, + pub(crate) inner: ElasticArray36, + pub(crate) len: usize, marker: PhantomData, } @@ -52,51 +53,17 @@ impl NibbleVec { /// Try to get the nibble at the given offset. #[inline] pub fn at(&self, idx: usize) -> u8 { - if idx % 2 == 0 { - self.inner[idx / 2] >> 4 - } else { - self.inner[idx / 2] & 0x0F - } + N::vec_at(self, idx) } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. pub fn push(&mut self, nibble: u8) { - let nibble = nibble & 0x0F; - - if self.len % 2 == 0 { - self.inner.push(nibble << 4); - } else { - *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - } - - self.len += 1; + N::push(self, nibble) } /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { - if self.is_empty() { - return None; - } - - let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if self.len % 2 == 0 { - self.inner.push(byte & 0xF0); - byte & 0x0F - } else { - byte >> 4 - }; - - self.len -= 1; - Some(nibble) - } - - /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. - pub fn as_nibbleslice(&self) -> Option> { - if self.len % 2 == 0 { - Some(NibbleSlice::new(self.inner())) - } else { - None - } + N::pop(self) } /// Get the underlying byte slice. @@ -135,15 +102,4 @@ mod tests { assert_eq!(v.len(), i as usize); } } - - #[test] - fn nibbleslice_conv() { - let mut v = NibbleVec::new(); - for i in 0..10 { - v.push(i); - } - - let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); - assert_eq!(v, v2); - } } From 318e7c891b2b7020ef003db3f81f6d88bbe85920 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 11 Apr 2019 19:13:44 +0200 Subject: [PATCH 022/120] Reverse padding in nibble --- test-support/reference-trie/src/lib.rs | 85 +++++--- trie-db/src/iter_build.rs | 2 +- trie-db/src/lib.rs | 1 - trie-db/src/nibbleslice.rs | 256 ++++++++++++++++++++++--- trie-db/src/node.rs | 4 + trie-db/src/triedb.rs | 6 +- trie-db/src/triedbmut.rs | 6 +- 7 files changed, 298 insertions(+), 62 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 485619f8..ecc36107 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -151,6 +151,7 @@ enum NodeKindNoExt { BranchWithValue, } +// TODO EMCH version for end padding!! /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 84 + 255 and if /// you attempt to do so *IT WILL PANIC*. @@ -429,19 +430,28 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { Some(r) } -fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { - let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; +fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; + let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); + let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if nibble_count % 2 == 1 { - output.push(partial[0] & 0x0f); - } - output.extend_from_slice(&partial[1..]); + if !N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[..hpe_pos]); + } + if nb_nibble_hpe > 0 { + output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); + } + if N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[1..]); + } output } -fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { - let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; +fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; + let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); + let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < noext_cst::LEAF_NODE_OVER as usize + 256); @@ -451,10 +461,15 @@ fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; - if nibble_count % 2 == 1 { - output.push(partial[0] & 0x0f); - } - output.extend_from_slice(&partial[1..]); + if !N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[..hpe_pos]); + } + if nb_nibble_hpe > 0 { + output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); + } + if N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[1..]); + } output } @@ -466,7 +481,7 @@ impl NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; fn hashed_null_node() -> ::Out { - KeccakHasher::hash(&[0u8][..]) + KeccakHasher::hash(&N::EMPTY_ENCODED[..]) } fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { @@ -474,6 +489,7 @@ impl NodeCodec for ReferenceNodeCodec { match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { + // TODO EMCH var len bitmap up to 256 from 2 see NibbleOps variant let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -481,9 +497,10 @@ impl NodeCodec for ReferenceNodeCodec { } else { None }; + // TODO EMCH could not parameterized this on associated constant let mut children = [None; 16]; let mut pot_cursor = 1; - for i in 0..16 { + for i in 0..N::NIBBLE_LEN { if bitmap & pot_cursor != 0 { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); @@ -493,14 +510,16 @@ impl NodeCodec for ReferenceNodeCodec { Ok(Node::Branch(children, value)) } NodeHeader::Extension(nibble_count) => { - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) + .ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } NodeHeader::Leaf(nibble_count) => { - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) + .ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -526,13 +545,13 @@ impl NodeCodec for ReferenceNodeCodec { } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { - let mut output = partial_to_key(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); + let mut output = partial_to_key::(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); value.encode_to(&mut output); output } fn ext_node(partial: &[u8], child: ChildReference<::Out>) -> Vec { - let mut output = partial_to_key(partial, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + let mut output = partial_to_key::(partial, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -587,11 +606,14 @@ impl NodeCodec for ReferenceNodeCodecNoExt { match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { - if nibble_count % 2 == 1 && input[0] & 0xf0 != 0x00 { + let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; + if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) + .ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -601,7 +623,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { }; let mut children = [None; 16]; let mut pot_cursor = 1; - for i in 0..16 { + for i in 0..N::NIBBLE_LEN { if bitmap & pot_cursor != 0 { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); @@ -611,11 +633,14 @@ impl NodeCodec for ReferenceNodeCodecNoExt { Ok(Node::NibbledBranch(nibble_slice, children, value)) } NodeHeaderNoExt::Leaf(nibble_count) => { - if nibble_count % 2 == 1 && input[0] & 0xf0 != 0x00 { + let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; + if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); + let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) + .ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -635,7 +660,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { - let mut output = partial_enc(partial, NodeKindNoExt::Leaf); + let mut output = partial_enc::(partial, NodeKindNoExt::Leaf); value.encode_to(&mut output); output } @@ -655,9 +680,9 @@ impl NodeCodec for ReferenceNodeCodecNoExt { children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { let mut output = if maybe_value.is_some() { - partial_enc(partial, NodeKindNoExt::BranchWithValue) + partial_enc::(partial, NodeKindNoExt::BranchWithValue) } else { - partial_enc(partial, NodeKindNoExt::BranchNoValue) + partial_enc::(partial, NodeKindNoExt::BranchNoValue) }; let bm_ix = output.len(); output.push(0); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 92eb540a..2746a58a 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -357,7 +357,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) } } else { // nothing null root corner case TODO warning hardcoded empty nibbleslice - cb_ext.process(&[0], C::empty_node(), true); + cb_ext.process(N::EMPTY_ENCODED, C::empty_node(), true); } } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index b25df82b..13a9cb65 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -11,7 +11,6 @@ // 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. - #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 5ccb7613..44810a1c 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -22,8 +22,9 @@ use nibblevec::NibbleVec; // until const fn for pow const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; - /// Nibble specific variants +/// Note that some function are defined here but ideally it should just be a set of +/// constant (with function handling all constant case). pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy { /// variant repr const REPR : ByteLayout; @@ -35,6 +36,25 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); /// Empty nibble encoded const EMPTY_ENCODED: &'static [u8]; + /// Define wether we should pad eneven byte at start or at end + const PADD_AT_BEGIN: bool; + /// padding value to apply (default to 0) + const PADDING_VALUE: u8 = 0; + /// padding bitmasks (could be calculated with a constant function). + const PADDING_BITMASK: &'static [u8]; + /// mask for nibble encoded first byte for leaf + const NIBBLE_ODD_MASK: u8; + /// mask for nibble encoded first byte for extension TODO consider removal (unused) + const NIBBLE_EXT_MASK: u8; + /// mask for nibble encoded first byte for leaf + const NIBBLE_LEAF_MASK: u8; + + + // TODO redesign nibble slice to run as cursor over a full key (concat could really benefit from + // it) + /// type for storing padding length (nothing for prefixed impl) + /// should default to no padding + type PADDING_LEN: Clone + Default + Copy + Into; /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). fn from_encoded(data: &[u8]) -> (NibbleSlice, bool); @@ -67,6 +87,8 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. fn pop(s: &mut NibbleVec) -> Option; + /// get nb nibble from hpe + fn nb_nibble_hpe(hpe: u8) -> usize; } /// half byte nibble prepend encoding @@ -87,15 +109,33 @@ pub enum ByteLayout { Full = 3, // 8, 1, 256 } +/// `()` with a conversion to 0 +#[derive(Clone, Default, Copy)] +pub struct Empty; + +impl Into for Empty { + fn into(self) -> usize { 0 } +} + impl NibbleOps for NibblePreHalf { + const PADD_AT_BEGIN: bool = true; const EMPTY_ENCODED: &'static [u8] = &[0]; const REPR: ByteLayout = ByteLayout::Half; + const PADDING_BITMASK: &'static [u8] = &[0x0f]; + const NIBBLE_ODD_MASK: u8 = 0x10; + const NIBBLE_EXT_MASK: u8 = 0x00; + const NIBBLE_LEAF_MASK: u8 = 0x20; + + + type PADDING_LEN = Empty; fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { if data.is_empty() { (NibbleSlice::::new(&[]), false) } else { - (NibbleSlice::::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32) + (NibbleSlice::::new_offset(data, + if data[0] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK {1} else {2}), + data[0] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK) } } @@ -123,8 +163,8 @@ impl NibbleOps for NibblePreHalf { fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { let mut r = ElasticArray36::new(); let mut i = l % 2; - r.push(if i == 1 {NIBBLE_ODD_MASK + Self::at(s, 0)} else {0} - + if is_leaf {NIBBLE_LEAF_MASK} else {NIBBLE_EXT_MASK}); + r.push(if i == 1 {Self::NIBBLE_ODD_MASK + Self::at(s, 0)} else {0} + + if is_leaf {Self::NIBBLE_LEAF_MASK} else {Self::NIBBLE_EXT_MASK}); while i < l { r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); i += 2; @@ -170,21 +210,124 @@ impl NibbleOps for NibblePreHalf { Some(nibble) } + #[inline] + fn nb_nibble_hpe(hpe: u8) -> usize { + if hpe & 16 == 16 { 1 } else { 0 } + } } -// TODO EMCH NIBBLE api from full key without concat?? +/// half byte nibble padding at end when encoded +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +pub struct NibblePostHalf; + + +impl NibbleOps for NibblePostHalf { + const PADD_AT_BEGIN: bool = false; + const EMPTY_ENCODED: &'static [u8] = &[0]; + const REPR: ByteLayout = ByteLayout::Half; + const PADDING_BITMASK: &'static [u8] = &[0xf0]; + const NIBBLE_ODD_MASK: u8 = 0x01; + const NIBBLE_EXT_MASK: u8 = 0x00; + const NIBBLE_LEAF_MASK: u8 = 0x02; + type PADDING_LEN = bool; + + fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { + if data.is_empty() { + (NibbleSlice::::new(&[]), false) + } else { + let end_ix = data.len() - 1; + let is_leaf = data[end_ix] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK; + let (padding, data) = if data[end_ix] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { + (true, &data[..]) + } else { + (false, &data[..end_ix]) + }; + (NibbleSlice::::new_padded(data, padding), is_leaf) + } + } + + #[inline(always)] + fn at(s: &NibbleSlice, i: usize) -> u8 { + let l = s.data.len() * Self::NIBBLE_PER_BYTE - s.offset - s.end_padding as usize; + if i < l { + if (s.offset + i) & 1 == 1 { + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 + } + else { + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 + } + } else { + let i = i - l; + debug_assert!( + i < s.data_encode_suffix.len() * Self::NIBBLE_PER_BYTE + - s.offset_encode_suffix - s.end_padding_suffix as usize); + if (s.offset_encode_suffix + i) & 1 == 1 { + s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] & 15u8 + } + else { + s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] >> 4 + } + } + } + + fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { + let mut r = ElasticArray36::new(); + let odd = l % Self::NIBBLE_PER_BYTE; + let mut i = 0; + while i < l - odd { + r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); + i += 2; + } + r.push(if odd == 1 {Self::NIBBLE_ODD_MASK + Self::at(s, l - 1) * 16} else {0} + + if is_leaf {Self::NIBBLE_LEAF_MASK} else {Self::NIBBLE_EXT_MASK}); + r + } + + #[inline] + fn vec_at(s: &NibbleVec, idx: usize) -> u8 { + if idx % 2 == 0 { + s.inner[idx / 2] >> 4 + } else { + s.inner[idx / 2] & 0x0F + } + } + + fn push(s: &mut NibbleVec, nibble: u8) { + let nibble = nibble & 0x0F; -pub const EMPTY_ENCODED: &[u8] = &[0]; + if s.len % 2 == 0 { + s.inner.push(nibble << 4); + } else { + *s.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + s.len += 1; + } -/// mask for nibble encoded first byte for extension -const NIBBLE_EXT_MASK: u8 = 0x00; + fn pop(s: &mut NibbleVec) -> Option { + if s.is_empty() { + return None; + } + + let byte = s.inner.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if s.len % 2 == 0 { + s.inner.push(byte & 0xF0); + byte & 0x0F + } else { + byte >> 4 + }; + + s.len -= 1; + Some(nibble) + } + + #[inline] + fn nb_nibble_hpe(hpe: u8) -> usize { + if hpe & 16 == 16 { 1 } else { 0 } + } +} -/// mask for nibble encoded first byte for leaf -const NIBBLE_ODD_MASK: u8 = 0x10; -/// mask for nibble encoded first byte for leaf -const NIBBLE_LEAF_MASK: u8 = 0x20; /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// @@ -209,16 +352,18 @@ const NIBBLE_LEAF_MASK: u8 = 0x20; /// } /// ``` #[derive(Copy, Clone)] -pub struct NibbleSlice<'a, N> { +pub struct NibbleSlice<'a, N: NibbleOps> { data: &'a [u8], offset: usize, + end_padding: N::PADDING_LEN, data_encode_suffix: &'a [u8], offset_encode_suffix: usize, + end_padding_suffix: N::PADDING_LEN, marker: PhantomData, } /// Iterator type for a nibble slice. -pub struct NibbleSliceIterator<'a, N> { +pub struct NibbleSliceIterator<'a, N: NibbleOps> { p: &'a NibbleSlice<'a, N>, i: usize, } @@ -240,22 +385,37 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice with a nibble offset. pub fn new_offset(data: &'a [u8], offset: usize) -> Self { + Self::new_slice(data, offset, Default::default()) + } + + fn new_slice(data: &'a [u8], offset: usize, end_padding: N::PADDING_LEN) -> Self { NibbleSlice { data, offset, + end_padding, data_encode_suffix: &b""[..], offset_encode_suffix: 0, + end_padding_suffix: Default::default(), marker: PhantomData, } } + + /// Create a new nibble slice with the given byte-slice and a padding at the end. + /// This is only use for building encoded slice with end padding + fn new_padded(data: &'a [u8], pad: N::PADDING_LEN) -> Self { + Self::new_slice(data, 0, pad) + } + /// Create a composed nibble slice; one followed by the other. pub fn new_composed(a: &Self, b: &Self) -> Self { NibbleSlice { data: a.data, offset: a.offset, + end_padding: a.end_padding, data_encode_suffix: b.data, offset_encode_suffix: b.offset, + end_padding_suffix: b.end_padding, marker: PhantomData, } } @@ -275,7 +435,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Get the length (in nibbles, naturally) of this slice. #[inline] - pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * N::NIBBLE_PER_BYTE - self.offset - self.offset_encode_suffix } + pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * N::NIBBLE_PER_BYTE - self.offset - self.offset_encode_suffix - self.end_padding.into() - self.end_padding_suffix.into() } /// Get the nibble at position `i`. #[inline(always)] @@ -288,8 +448,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { NibbleSlice { data: self.data, offset: self.offset + i, + end_padding: self.end_padding, data_encode_suffix: &b""[..], offset_encode_suffix: 0, + end_padding_suffix: self.end_padding_suffix, marker: PhantomData, } } @@ -372,19 +534,25 @@ pub fn combine_encoded(prefix: &[u8], extension: &[u8]) -> Elastic mod tests { use super::NibbleSlice; use super::NibblePreHalf; + use super::NibblePostHalf; + use super::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; #[test] fn basics() { - let n = NibbleSlice::::new(D); + basics_inner::(); + basics_inner::(); + } + fn basics_inner() { + let n = NibbleSlice::::new(D); assert_eq!(n.len(), 6); assert!(!n.is_empty()); - let n = NibbleSlice::::new_offset(D, 6); + let n = NibbleSlice::::new_offset(D, 6); assert!(n.is_empty()); - let n = NibbleSlice::::new_offset(D, 3); + let n = NibbleSlice::::new_offset(D, 3); assert_eq!(n.len(), 3); for i in 0..3 { assert_eq!(n.at(i), i as u8 + 3); @@ -393,7 +561,11 @@ mod tests { #[test] fn iterator() { - let n = NibbleSlice::::new(D); + iterator_inner::(); + iterator_inner::(); + } + fn iterator_inner() { + let n = NibbleSlice::::new(D); let mut nibbles: Vec = vec![]; nibbles.extend(n.iter()); assert_eq!(nibbles, (0u8..6).collect::>()) @@ -401,7 +573,11 @@ mod tests { #[test] fn mid() { - let n = NibbleSlice::::new(D); + mid_inner::(); + mid_inner::(); + } + fn mid_inner() { + let n = NibbleSlice::::new(D); let m = n.mid(2); for i in 0..4 { assert_eq!(m.at(i), i as u8 + 2); @@ -413,7 +589,7 @@ mod tests { } #[test] - fn encoded() { + fn encoded_pre() { let n = NibbleSlice::::new(D); assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); @@ -422,17 +598,45 @@ mod tests { } #[test] - fn from_encoded() { + fn encoded_post() { + let n = NibbleSlice::::new(D); + assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x01, 0x23, 0x45, 0x00])); + assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x01, 0x23, 0x45, 0x02])); + assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); + assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); + let n = NibbleSlice::::from_encoded(&[0x12, 0x34, 0x51]).0; // unaligned end + assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); + assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); + assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x23, 0x45, 0x00])); + assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x23, 0x45, 0x02])); + } + + + #[test] + fn from_encoded_pre() { let n = NibbleSlice::::new(D); assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45])); assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45])); assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45])); assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45])); } + #[test] + fn from_encoded_post() { + let n = NibbleSlice::::new(D); + assert_eq!((n, false), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x00])); + assert_eq!((n, true), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x02])); + assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x12, 0x34, 0x51])); + assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x12, 0x34, 0x53])); + } + #[test] fn shared() { - let n = NibbleSlice::::new(D); + shared_inner::(); + shared_inner::(); + } + fn shared_inner() { + let n = NibbleSlice::::new(D); let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; let m = NibbleSlice::new(other); @@ -448,8 +652,12 @@ mod tests { #[test] fn compare() { + compare_inner::(); + compare_inner::(); + } + fn compare_inner() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; - let n = NibbleSlice::::new(D); + let n = NibbleSlice::::new(D); let m = NibbleSlice::new(other); assert!(n != m); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ab1a182c..a31f902f 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -33,6 +33,10 @@ pub enum Node<'a, N: NibbleOps> { Leaf(NibbleSlice<'a, N>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. Extension(NibbleSlice<'a, N>, &'a [u8]), + // TODO EMCH var length for children array is stuck behind https://github.com/rust-lang/rust/issues/43408 + // So we should also put it as associated type of N, but generic_associated_types will be needed + // for lifetime, so we should ultimately use something similar to struct `Branch` it decodes from + // a slice that is already aligned (need 2* bound in case there is some headers). /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), /// Branch node with support for a nibble (to avoid extension node) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index be21a28a..f506656e 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -81,7 +81,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + if !db.contains(root, L::N::EMPTY_ENCODED) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { Ok(TrieDB {db, root, hash_count: 0}) @@ -94,7 +94,7 @@ where /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, nibbleslice::EMPTY_ENCODED) + .get(self.root, L::N::EMPTY_ENCODED) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } @@ -104,7 +104,7 @@ where /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: &[u8]) -> Result, TrieHash, CError> { - match (partial_key == nibbleslice::EMPTY_ENCODED, L::C::try_decode_hash(node)) { + match (partial_key == L::N::EMPTY_ENCODED, L::C::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index f5d01804..a241e0dd 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -70,7 +70,7 @@ fn empty_children() -> Box<[Option>; 16]> { ]) } -struct Partial<'key, N> { +struct Partial<'key, N: NibbleOps> { key: NibbleSlice<'key, N>, split: usize, } @@ -384,7 +384,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + if !db.contains(root, L::N::EMPTY_ENCODED) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -1154,7 +1154,7 @@ where self.commit_child(child, k) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); + *self.root = self.db.insert(L::N::EMPTY_ENCODED, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); From b30698a76d37a3f904922ffea5325ec8316b9934 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 11 Apr 2019 20:12:50 +0200 Subject: [PATCH 023/120] start testing, first broken part see triedbmut advance println --- test-support/reference-trie/src/lib.rs | 57 +++++----- trie-db/src/lib.rs | 2 +- trie-db/src/nibbleslice.rs | 140 ++++++++++++------------- trie-db/src/nibblevec.rs | 8 +- trie-db/src/node.rs | 8 +- trie-db/src/triedbmut.rs | 4 + 6 files changed, 114 insertions(+), 105 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index ecc36107..428dacbd 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -37,7 +37,8 @@ use trie_db::{ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record, TrieLayOut, NibblePreHalf, NibbleOps}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder}; +pub use trie_db::{Record, TrieLayOut, NibblePreHalf, NibbleOps, NibblePostHalf}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum @@ -57,7 +58,7 @@ impl TrieLayOut for LayoutNew { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; - type N = NibblePreHalf; + type N = NibblePostHalf; } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; @@ -431,26 +432,26 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { } fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; - let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; + let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if !N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[..hpe_pos]); - } + if !N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[..hpe_pos]); + } if nb_nibble_hpe > 0 { output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); } - if N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[1..]); - } + if N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[1..]); + } output } fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; - let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; + let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < noext_cst::LEAF_NODE_OVER as usize + 256); @@ -461,15 +462,15 @@ fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; - if !N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[..hpe_pos]); - } + if !N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[..hpe_pos]); + } if nb_nibble_hpe > 0 { output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); } - if N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[1..]); - } + if N::PADD_AT_BEGIN { + output.extend_from_slice(&partial[1..]); + } output } @@ -489,7 +490,7 @@ impl NodeCodec for ReferenceNodeCodec { match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - // TODO EMCH var len bitmap up to 256 from 2 see NibbleOps variant + // TODO EMCH var len bitmap up to 256 from 2 see NibbleOps variant let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -497,7 +498,7 @@ impl NodeCodec for ReferenceNodeCodec { } else { None }; - // TODO EMCH could not parameterized this on associated constant + // TODO EMCH could not parameterized this on associated constant let mut children = [None; 16]; let mut pot_cursor = 1; for i in 0..N::NIBBLE_LEN { @@ -511,14 +512,14 @@ impl NodeCodec for ReferenceNodeCodec { } NodeHeader::Extension(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } NodeHeader::Leaf(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) @@ -606,13 +607,13 @@ impl NodeCodec for ReferenceNodeCodecNoExt { match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { - let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; + let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { @@ -633,13 +634,13 @@ impl NodeCodec for ReferenceNodeCodecNoExt { Ok(Node::NibbledBranch(nibble_slice, children, value)) } NodeHeaderNoExt::Leaf(nibble_count) => { - let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; + let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; + let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 13a9cb65..1dfba8b8 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -96,7 +96,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf}; +pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf, NibblePostHalf}; pub use node_codec::NodeCodec; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 44810a1c..e7c0a882 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -38,23 +38,23 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const EMPTY_ENCODED: &'static [u8]; /// Define wether we should pad eneven byte at start or at end const PADD_AT_BEGIN: bool; - /// padding value to apply (default to 0) + /// padding value to apply (default to 0) const PADDING_VALUE: u8 = 0; - /// padding bitmasks (could be calculated with a constant function). + /// padding bitmasks (could be calculated with a constant function). const PADDING_BITMASK: &'static [u8]; - /// mask for nibble encoded first byte for leaf - const NIBBLE_ODD_MASK: u8; - /// mask for nibble encoded first byte for extension TODO consider removal (unused) - const NIBBLE_EXT_MASK: u8; - /// mask for nibble encoded first byte for leaf - const NIBBLE_LEAF_MASK: u8; + /// mask for nibble encoded first byte for leaf + const NIBBLE_ODD_MASK: u8; + /// mask for nibble encoded first byte for extension TODO consider removal (unused) + const NIBBLE_EXT_MASK: u8; + /// mask for nibble encoded first byte for leaf + const NIBBLE_LEAF_MASK: u8; - // TODO redesign nibble slice to run as cursor over a full key (concat could really benefit from - // it) - /// type for storing padding length (nothing for prefixed impl) - /// should default to no padding - type PADDING_LEN: Clone + Default + Copy + Into; + // TODO redesign nibble slice to run as cursor over a full key (concat could really benefit from + // it) + /// type for storing padding length (nothing for prefixed impl) + /// should default to no padding + type PADDING_LEN: Clone + Default + Copy + Into; /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). fn from_encoded(data: &[u8]) -> (NibbleSlice, bool); @@ -114,7 +114,7 @@ pub enum ByteLayout { pub struct Empty; impl Into for Empty { - fn into(self) -> usize { 0 } + fn into(self) -> usize { 0 } } impl NibbleOps for NibblePreHalf { @@ -122,20 +122,20 @@ impl NibbleOps for NibblePreHalf { const EMPTY_ENCODED: &'static [u8] = &[0]; const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [u8] = &[0x0f]; - const NIBBLE_ODD_MASK: u8 = 0x10; - const NIBBLE_EXT_MASK: u8 = 0x00; - const NIBBLE_LEAF_MASK: u8 = 0x20; + const NIBBLE_ODD_MASK: u8 = 0x10; + const NIBBLE_EXT_MASK: u8 = 0x00; + const NIBBLE_LEAF_MASK: u8 = 0x20; - type PADDING_LEN = Empty; + type PADDING_LEN = Empty; fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { if data.is_empty() { (NibbleSlice::::new(&[]), false) } else { (NibbleSlice::::new_offset(data, - if data[0] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK {1} else {2}), - data[0] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK) + if data[0] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK {1} else {2}), + data[0] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK) } } @@ -210,10 +210,10 @@ impl NibbleOps for NibblePreHalf { Some(nibble) } - #[inline] + #[inline] fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & 16 == 16 { 1 } else { 0 } - } + if hpe & 16 == 16 { 1 } else { 0 } + } } /// half byte nibble padding at end when encoded @@ -226,24 +226,24 @@ impl NibbleOps for NibblePostHalf { const EMPTY_ENCODED: &'static [u8] = &[0]; const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [u8] = &[0xf0]; - const NIBBLE_ODD_MASK: u8 = 0x01; - const NIBBLE_EXT_MASK: u8 = 0x00; - const NIBBLE_LEAF_MASK: u8 = 0x02; - type PADDING_LEN = bool; + const NIBBLE_ODD_MASK: u8 = 0x01; + const NIBBLE_EXT_MASK: u8 = 0x00; + const NIBBLE_LEAF_MASK: u8 = 0x02; + type PADDING_LEN = bool; fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { if data.is_empty() { (NibbleSlice::::new(&[]), false) } else { - let end_ix = data.len() - 1; - let is_leaf = data[end_ix] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK; - let (padding, data) = if data[end_ix] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { - (true, &data[..]) - } else { - (false, &data[..end_ix]) - }; + let end_ix = data.len() - 1; + let is_leaf = data[end_ix] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK; + let (padding, data) = if data[end_ix] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { + (true, &data[..]) + } else { + (false, &data[..end_ix]) + }; (NibbleSlice::::new_padded(data, padding), is_leaf) - } + } } #[inline(always)] @@ -258,9 +258,9 @@ impl NibbleOps for NibblePostHalf { } } else { let i = i - l; - debug_assert!( - i < s.data_encode_suffix.len() * Self::NIBBLE_PER_BYTE - - s.offset_encode_suffix - s.end_padding_suffix as usize); + debug_assert!( + i < s.data_encode_suffix.len() * Self::NIBBLE_PER_BYTE + - s.offset_encode_suffix - s.end_padding_suffix as usize); if (s.offset_encode_suffix + i) & 1 == 1 { s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] & 15u8 } @@ -273,7 +273,7 @@ impl NibbleOps for NibblePostHalf { fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { let mut r = ElasticArray36::new(); let odd = l % Self::NIBBLE_PER_BYTE; - let mut i = 0; + let mut i = 0; while i < l - odd { r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); i += 2; @@ -321,10 +321,10 @@ impl NibbleOps for NibblePostHalf { Some(nibble) } - #[inline] + #[inline] fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & 16 == 16 { 1 } else { 0 } - } + if hpe & 16 == 16 { 1 } else { 0 } + } } @@ -355,10 +355,10 @@ impl NibbleOps for NibblePostHalf { pub struct NibbleSlice<'a, N: NibbleOps> { data: &'a [u8], offset: usize, - end_padding: N::PADDING_LEN, + end_padding: N::PADDING_LEN, data_encode_suffix: &'a [u8], offset_encode_suffix: usize, - end_padding_suffix: N::PADDING_LEN, + end_padding_suffix: N::PADDING_LEN, marker: PhantomData, } @@ -385,37 +385,37 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice with a nibble offset. pub fn new_offset(data: &'a [u8], offset: usize) -> Self { - Self::new_slice(data, offset, Default::default()) + Self::new_slice(data, offset, Default::default()) } fn new_slice(data: &'a [u8], offset: usize, end_padding: N::PADDING_LEN) -> Self { NibbleSlice { data, offset, - end_padding, + end_padding, data_encode_suffix: &b""[..], offset_encode_suffix: 0, - end_padding_suffix: Default::default(), + end_padding_suffix: Default::default(), marker: PhantomData, } } /// Create a new nibble slice with the given byte-slice and a padding at the end. - /// This is only use for building encoded slice with end padding + /// This is only use for building encoded slice with end padding fn new_padded(data: &'a [u8], pad: N::PADDING_LEN) -> Self { - Self::new_slice(data, 0, pad) - } + Self::new_slice(data, 0, pad) + } /// Create a composed nibble slice; one followed by the other. pub fn new_composed(a: &Self, b: &Self) -> Self { NibbleSlice { data: a.data, offset: a.offset, - end_padding: a.end_padding, + end_padding: a.end_padding, data_encode_suffix: b.data, offset_encode_suffix: b.offset, - end_padding_suffix: b.end_padding, + end_padding_suffix: b.end_padding, marker: PhantomData, } } @@ -448,10 +448,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { NibbleSlice { data: self.data, offset: self.offset + i, - end_padding: self.end_padding, + end_padding: self.end_padding, data_encode_suffix: &b""[..], offset_encode_suffix: 0, - end_padding_suffix: self.end_padding_suffix, + end_padding_suffix: self.end_padding_suffix, marker: PhantomData, } } @@ -541,9 +541,9 @@ mod tests { #[test] fn basics() { - basics_inner::(); - basics_inner::(); - } + basics_inner::(); + basics_inner::(); + } fn basics_inner() { let n = NibbleSlice::::new(D); assert_eq!(n.len(), 6); @@ -561,9 +561,9 @@ mod tests { #[test] fn iterator() { - iterator_inner::(); - iterator_inner::(); - } + iterator_inner::(); + iterator_inner::(); + } fn iterator_inner() { let n = NibbleSlice::::new(D); let mut nibbles: Vec = vec![]; @@ -573,9 +573,9 @@ mod tests { #[test] fn mid() { - mid_inner::(); - mid_inner::(); - } + mid_inner::(); + mid_inner::(); + } fn mid_inner() { let n = NibbleSlice::::new(D); let m = n.mid(2); @@ -605,7 +605,7 @@ mod tests { assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); let n = NibbleSlice::::from_encoded(&[0x12, 0x34, 0x51]).0; // unaligned end - assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); + assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x23, 0x45, 0x00])); assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x23, 0x45, 0x02])); @@ -632,9 +632,9 @@ mod tests { #[test] fn shared() { - shared_inner::(); - shared_inner::(); - } + shared_inner::(); + shared_inner::(); + } fn shared_inner() { let n = NibbleSlice::::new(D); @@ -652,9 +652,9 @@ mod tests { #[test] fn compare() { - compare_inner::(); - compare_inner::(); - } + compare_inner::(); + compare_inner::(); + } fn compare_inner() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; let n = NibbleSlice::::new(D); diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index 537d3bf3..b03ac5bc 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -85,11 +85,15 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { use super::NibbleVec; - use nibbleslice::NibblePreHalf; + use nibbleslice::{NibblePreHalf, NibblePostHalf, NibbleOps}; #[test] fn push_pop() { - let mut v = NibbleVec::::new(); + push_pop_inner::(); + push_pop_inner::(); + } + fn push_pop_inner() { + let mut v = NibbleVec::::new(); for i in 0..16 { v.push(i); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index a31f902f..ede04952 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -33,10 +33,10 @@ pub enum Node<'a, N: NibbleOps> { Leaf(NibbleSlice<'a, N>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. Extension(NibbleSlice<'a, N>, &'a [u8]), - // TODO EMCH var length for children array is stuck behind https://github.com/rust-lang/rust/issues/43408 - // So we should also put it as associated type of N, but generic_associated_types will be needed - // for lifetime, so we should ultimately use something similar to struct `Branch` it decodes from - // a slice that is already aligned (need 2* bound in case there is some headers). + // TODO EMCH var length for children array is stuck behind https://github.com/rust-lang/rust/issues/43408 + // So we should also put it as associated type of N, but generic_associated_types will be needed + // for lifetime, so we should ultimately use something similar to struct `Branch` it decodes from + // a slice that is already aligned (need 2* bound in case there is some headers). /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), /// Branch node with support for a nibble (to avoid extension node) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index a241e0dd..95790143 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -613,7 +613,11 @@ where // append after cp == existing_key and partial > cp trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; + println!("cp{:x?}", key.encoded_prefix()); + println!("cp{:x?}", key.mid()); key.advance(cp + 1); + println!("cp{:x?}", key.encoded_prefix()); + println!("cp{:x?}", key.mid()); if let Some(child) = children[idx].take() { // original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; From 097fec5f6c637b43cf8cb173db3c37833fa31926 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 12 Apr 2019 11:13:27 +0200 Subject: [PATCH 024/120] Fix offset issue of nodecodec --- test-support/reference-trie/src/lib.rs | 12 ++++-- trie-db/src/nibbleslice.rs | 51 ++++++++++++++++++++++---- trie-db/src/triedb.rs | 9 ++++- trie-db/src/triedbmut.rs | 4 -- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 428dacbd..2058daee 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -513,14 +513,16 @@ impl NodeCodec for ReferenceNodeCodec { NodeHeader::Extension(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); + let nibble_slice = NibbleSlice::new_padded(nibble_data, + nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } NodeHeader::Leaf(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); + let nibble_slice = NibbleSlice::new_padded(nibble_data, + nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -614,7 +616,8 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); + let nibble_slice = NibbleSlice::new_padded(nibble_data, + nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -641,7 +644,8 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); + let nibble_slice = NibbleSlice::new_padded(nibble_data, + nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index e7c0a882..a112abb9 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -25,7 +25,7 @@ const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Nibble specific variants /// Note that some function are defined here but ideally it should just be a set of /// constant (with function handling all constant case). -pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy { +pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { /// variant repr const REPR : ByteLayout; /// Number of bit per nibble @@ -54,7 +54,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy // it) /// type for storing padding length (nothing for prefixed impl) /// should default to no padding - type PADDING_LEN: Clone + Default + Copy + Into; + type PADDING_LEN: Clone + Default + Copy + Into + Eq; /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). fn from_encoded(data: &[u8]) -> (NibbleSlice, bool); @@ -89,6 +89,11 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// get nb nibble from hpe fn nb_nibble_hpe(hpe: u8) -> usize; + /// get nb padding in hpe from nibble size + fn nb_padding(len: usize) -> usize; + + /// conveniance convert to avoid new type + fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN; } /// half byte nibble prepend encoding @@ -110,13 +115,15 @@ pub enum ByteLayout { } /// `()` with a conversion to 0 -#[derive(Clone, Default, Copy)] +#[derive(Clone, Default, Copy, PartialEq, Eq, Debug)] pub struct Empty; impl Into for Empty { fn into(self) -> usize { 0 } } +// TODO EMCH some method can be fuse with post half (see bug solving: eg new_offset and +// new_padded_end merged impl NibbleOps for NibblePreHalf { const PADD_AT_BEGIN: bool = true; const EMPTY_ENCODED: &'static [u8] = &[0]; @@ -212,7 +219,16 @@ impl NibbleOps for NibblePreHalf { #[inline] fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & 16 == 16 { 1 } else { 0 } + if hpe & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { 1 } else { 0 } + } + #[inline] + fn nb_padding(len: usize) -> usize { + if len % Self::NIBBLE_PER_BYTE == 1 { 1 } else { 0 } + } + #[inline] + fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN { + debug_assert!(nb < 1); + Empty } } @@ -242,7 +258,7 @@ impl NibbleOps for NibblePostHalf { } else { (false, &data[..end_ix]) }; - (NibbleSlice::::new_padded(data, padding), is_leaf) + (NibbleSlice::::new_padded_end(data, padding), is_leaf) } } @@ -323,7 +339,16 @@ impl NibbleOps for NibblePostHalf { #[inline] fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & 16 == 16 { 1 } else { 0 } + if hpe & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { 1 } else { 0 } + } + #[inline] + fn nb_padding(len: usize) -> usize { + if len % Self::NIBBLE_PER_BYTE == 1 { 1 } else { 0 } + } + #[inline] + fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN { + debug_assert!(nb < 2); + nb == 1 } } @@ -381,12 +406,22 @@ impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice. - pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_offset(data, 0) } + pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0, Default::default()) } /// Create a new nibble slice with the given byte-slice with a nibble offset. pub fn new_offset(data: &'a [u8], offset: usize) -> Self { Self::new_slice(data, offset, Default::default()) } + /// Create a new nibble slice with the given padding + #[inline] + pub fn new_padded(data: &'a [u8], pad: usize) -> Self { + if N::PADD_AT_BEGIN { + Self::new_offset(data, pad) + } else { + Self::new_padded_end(data, N::lossy_into_padding(pad)) + } + } + fn new_slice(data: &'a [u8], offset: usize, end_padding: N::PADDING_LEN) -> Self { NibbleSlice { @@ -403,7 +438,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice and a padding at the end. /// This is only use for building encoded slice with end padding - fn new_padded(data: &'a [u8], pad: N::PADDING_LEN) -> Self { + fn new_padded_end(data: &'a [u8], pad: N::PADDING_LEN) -> Self { Self::new_slice(data, 0, pad) } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index f506656e..15135aef 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -426,6 +426,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } } + // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead /// The present key. fn key(&self) -> Vec { // collapse the key_nibbles down to bytes. @@ -444,8 +445,12 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { fn encoded_key(&self) -> ElasticArray36 { let key = self.key(); let slice = NibbleSlice::::new(&key); - if self.key_nibbles.len() % 2 == 1 { - NibbleSlice::new_composed(&slice, &NibbleSlice::new_offset(&self.key_nibbles[(self.key_nibbles.len() - 1)..], 1)).encoded(false) + let nb_padd = L::N::nb_padding(self.key_nibbles.len()); + if nb_padd > 0 { + // TODO EMCH costy new_composed when slice build just above??? + NibbleSlice::new_composed(&slice, + &NibbleSlice::new_padded(&self.key_nibbles[(self.key_nibbles.len() - 1)..], nb_padd)) + .encoded(false) } else { slice.encoded(false) } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 95790143..a241e0dd 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -613,11 +613,7 @@ where // append after cp == existing_key and partial > cp trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; - println!("cp{:x?}", key.encoded_prefix()); - println!("cp{:x?}", key.mid()); key.advance(cp + 1); - println!("cp{:x?}", key.encoded_prefix()); - println!("cp{:x?}", key.mid()); if let Some(child) = children[idx].take() { // original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; From f892c76cd86a2521e48602126200ed5e71fe62da Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 12 Apr 2019 11:54:46 +0200 Subject: [PATCH 025/120] Switch remaining tests to use postfix for no ext --- test-support/reference-trie/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 2058daee..523cc1d6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -828,7 +828,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -839,7 +839,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -895,7 +895,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; From 57a86c5c133514c3ea1a4aaeee4436201f10919b Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 12 Apr 2019 18:06:09 +0200 Subject: [PATCH 026/120] Switch header encoding scheme. Failing test is related to the trie_stream_no_ext having not switch to the new scheme. --- test-support/reference-trie/src/lib.rs | 168 ++++++++++++++++++------- trie-db/src/iter_build.rs | 2 +- trie-db/src/node_codec.rs | 2 +- trie-db/src/triedbmut.rs | 2 +- 4 files changed, 123 insertions(+), 51 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 523cc1d6..28961ffa 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -130,6 +130,16 @@ mod noext_cst { pub const BRANCH_NODE_NO_VALUE_LAST: u8 = BRANCH_NODE_NO_VALUE_BIG - 1; } +/// constant use with trie simplification codec +mod s_cst { + pub const EMPTY_TRIE: u8 = 0; + pub const NIBBLE_SIZE_BOUND: usize = u16::max_value() as usize; + pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6; + pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6; + pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; +} + + /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 125 and if /// you attempt to do so *IT WILL PANIC*. @@ -332,29 +342,96 @@ impl Encode for NodeHeader { } } +/// bounding size to storage in a u16 variable to avoid dos +fn s_encode_size_and_prefix(size: usize, prefix: u8, out: &mut T) { + let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, size); + let l1 = std::cmp::min(62, size); + let mut rem = size - l1; + if rem == 0 { + out.push_byte(prefix + l1 as u8); + return; + } else { + out.push_byte(prefix + 63); + } + while rem > 0 { + if rem < 256 { + out.push_byte((rem - 1) as u8); + return; + } else { + out.push_byte(255); + rem = rem.saturating_sub(255); + } + } +} +fn s_decode_size(first: u8, input: &mut I) -> Option { + let mut result = (first & 255u8 >> 2) as usize; + if result < 63 { + return Some(result); + } + result -= 1; + while result <= s_cst::NIBBLE_SIZE_BOUND { + let n = input.read_byte()? as usize; + if n < 255 { + return Some(result + n + 1); + } + result += 255; + } + Some(s_cst::NIBBLE_SIZE_BOUND) +} + +#[test] +fn test_encoding_simple_trie() { + for prefix in [ + s_cst::LEAF_PREFIX_MASK, + s_cst::BRANCH_WITHOUT_MASK, + s_cst::BRANCH_WITH_MASK, + ].iter() { + for i in (0..1000) + .chain(s_cst::NIBBLE_SIZE_BOUND - 2..s_cst::NIBBLE_SIZE_BOUND + 2) { + let mut output = Vec::new(); + s_encode_size_and_prefix(i, *prefix, &mut output); + let input = &mut &output[..]; + let first = input.read_byte().unwrap(); + assert_eq!(first & (0b11 << 6), *prefix); + let v = s_decode_size(first, input); + assert_eq!(Some(std::cmp::min(i, s_cst::NIBBLE_SIZE_BOUND)), v); + } + + } +} + +/* +#[test] +fn test_mal() { + let mut unique = std::collections::BTreeMap::new(); + // test over 32 bit only is still 4 byte & this bruteforce takes way to long... + for i in (0..u32::max_value() / 4) { + let enc = i.to_be_bytes(); + assert!(enc[0] >> 6 == 0); + let input = &mut &enc[..]; + let first = input.read_byte().unwrap(); + if let Some(v) = s_decode_size(first, input) { + let mut rem = 0; + while let Some(_) = input.read_byte() { rem += 1 } + if let Some((prev,prem)) = unique.insert(v, (enc.to_vec(),rem)) { + assert_eq!(&enc[..4 - rem], &prev[..4 - prem], + "duplicated key val {} {} {:x?} {:x?}", v, i, prev, enc); + } + } + } +} +*/ + impl Encode for NodeHeaderNoExt { fn encode_to(&self, output: &mut T) { match self { - NodeHeaderNoExt::Null => output.push_byte(noext_cst::EMPTY_TRIE), - - NodeHeaderNoExt::Branch(true, nibble_count) if *nibble_count < noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize => - output.push_byte(noext_cst::BRANCH_NODE_WITH_VALUE + *nibble_count as u8), - NodeHeaderNoExt::Branch(true, nibble_count) => { - output.push_byte(noext_cst::BRANCH_NODE_WITH_VALUE_BIG); - output.push_byte((*nibble_count - noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize) as u8); - }, - NodeHeaderNoExt::Branch(false, nibble_count) if *nibble_count < noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize => - output.push_byte(noext_cst::BRANCH_NODE_NO_VALUE + *nibble_count as u8), - NodeHeaderNoExt::Branch(false, nibble_count) => { - output.push_byte(noext_cst::BRANCH_NODE_NO_VALUE_BIG); - output.push_byte((*nibble_count - noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize) as u8); - }, - NodeHeaderNoExt::Leaf(nibble_count) if *nibble_count < noext_cst::LEAF_NODE_OVER as usize => - output.push_byte(noext_cst::LEAF_NODE_OFFSET + *nibble_count as u8), - NodeHeaderNoExt::Leaf(nibble_count) => { - output.push_byte(noext_cst::LEAF_NODE_BIG); - output.push_byte((*nibble_count - noext_cst::LEAF_NODE_OVER as usize) as u8); - } + NodeHeaderNoExt::Null => output.push_byte(s_cst::EMPTY_TRIE), + NodeHeaderNoExt::Branch(true, nibble_count) => + s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITH_MASK, output), + NodeHeaderNoExt::Branch(false, nibble_count) => + s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITHOUT_MASK, output), + NodeHeaderNoExt::Leaf(nibble_count) => + s_encode_size_and_prefix(*nibble_count, s_cst::LEAF_PREFIX_MASK, output), } } } @@ -373,33 +450,27 @@ impl Decode for NodeHeader { impl Decode for NodeHeaderNoExt { fn decode(input: &mut I) -> Option { - Some(match input.read_byte()? { - noext_cst::EMPTY_TRIE => NodeHeaderNoExt::Null, - - i @ noext_cst::LEAF_NODE_OFFSET ... noext_cst::LEAF_NODE_LAST => - NodeHeaderNoExt::Leaf((i - noext_cst::LEAF_NODE_OFFSET) as usize), - noext_cst::LEAF_NODE_BIG => - NodeHeaderNoExt::Leaf(input.read_byte()? as usize + noext_cst::LEAF_NODE_OVER as usize), - - i @ noext_cst::BRANCH_NODE_WITH_VALUE ... noext_cst::BRANCH_NODE_WITH_VALUE_LAST => - NodeHeaderNoExt::Branch(true, (i - noext_cst::BRANCH_NODE_WITH_VALUE) as usize), - noext_cst::BRANCH_NODE_WITH_VALUE_BIG => - NodeHeaderNoExt::Branch(true, input.read_byte()? as usize + noext_cst::BRANCH_NODE_WITH_VALUE_OVER as usize), - - i @ noext_cst::BRANCH_NODE_NO_VALUE ... noext_cst::BRANCH_NODE_NO_VALUE_LAST => - NodeHeaderNoExt::Branch(false, (i - noext_cst::BRANCH_NODE_NO_VALUE) as usize), - noext_cst::BRANCH_NODE_NO_VALUE_BIG => - NodeHeaderNoExt::Branch(false, input.read_byte()? as usize + noext_cst::BRANCH_NODE_NO_VALUE_OVER as usize), - - }) + let i = input.read_byte()?; + if i == s_cst::EMPTY_TRIE { + return Some(NodeHeaderNoExt::Null); + } + match i & (0b11 << 6) { + s_cst::LEAF_PREFIX_MASK => Some(NodeHeaderNoExt::Leaf(s_decode_size(i, input)?)), + s_cst::BRANCH_WITHOUT_MASK => Some(NodeHeaderNoExt::Branch(false, s_decode_size(i, input)?)), + s_cst::BRANCH_WITH_MASK => Some(NodeHeaderNoExt::Branch(true, s_decode_size(i, input)?)), + // do not allow any special encoding + _ => None, + } } } /// Simple reference implementation of a `NodeCodec`. +/// This is similar to ethereum implementation. #[derive(Default, Clone)] pub struct ReferenceNodeCodec; /// Simple reference implementation of a `NodeCodec`. +/// This is following https://github.com/w3f/polkadot-re-spec/issues/8 #[derive(Default, Clone)] pub struct ReferenceNodeCodecNoExt; @@ -482,7 +553,7 @@ impl NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; fn hashed_null_node() -> ::Out { - KeccakHasher::hash(&N::EMPTY_ENCODED[..]) + KeccakHasher::hash(>::empty_node()) } fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { @@ -540,11 +611,11 @@ impl NodeCodec for ReferenceNodeCodec { } fn is_empty_node(data: &[u8]) -> bool { - data == &[EMPTY_TRIE][..] + data == >::empty_node() } - fn empty_node() -> Vec { - vec![EMPTY_TRIE] + fn empty_node() -> &'static[u8] { + &[EMPTY_TRIE] } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { @@ -600,7 +671,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; fn hashed_null_node() -> ::Out { - >::hashed_null_node() + KeccakHasher::hash(>::empty_node()) } fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { @@ -657,11 +728,11 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn is_empty_node(data: &[u8]) -> bool { - data == &[noext_cst::EMPTY_TRIE][..] + data == >::empty_node() } - fn empty_node() -> Vec { - vec![EMPTY_TRIE] + fn empty_node() -> &'static [u8] { + &[s_cst::EMPTY_TRIE] } fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { @@ -796,7 +867,8 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + // TODO EMCH siwtch this to post and implement post on ref_trie_root_unhashed_no_ext!! + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 2746a58a..f1cf0bc4 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -357,7 +357,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) } } else { // nothing null root corner case TODO warning hardcoded empty nibbleslice - cb_ext.process(N::EMPTY_ENCODED, C::empty_node(), true); + cb_ext.process(N::EMPTY_ENCODED, C::empty_node().to_vec(), true); } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 0c3adcfa..0cb60227 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -63,7 +63,7 @@ pub trait NodeCodec: Sized { fn is_empty_node(data: &[u8]) -> bool; /// Returns an empty node - fn empty_node() -> Vec; + fn empty_node() -> &'static [u8]; /// Returns an encoded leaf node fn leaf_node(partial: &[u8], value: &[u8]) -> Vec; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index a241e0dd..0e2ff4a6 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -186,7 +186,7 @@ where H: Hasher, { match self { - Node::Empty => C::empty_node(), + Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => C::leaf_node(&partial, &value), Node::Extension(partial, child) => C::ext_node(&partial, child_cb(child, &partial)), Node::Branch(mut children, value) => { From 0cab2eb2e5c7d1b91b27891550144bba2b940386 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 16 Apr 2019 17:00:46 +0200 Subject: [PATCH 027/120] TrieStream implementation : warning it is not parameterized. --- test-support/reference-trie/src/lib.rs | 226 ++++++++++++------------- trie-db/src/iter_build.rs | 5 +- 2 files changed, 114 insertions(+), 117 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 28961ffa..5530dba1 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -162,30 +162,18 @@ enum NodeKindNoExt { BranchWithValue, } -// TODO EMCH version for end padding!! -/// Create a leaf/extension node, encoding a number of nibbles. Note that this -/// cannot handle a number of nibbles that is zero or greater than 84 + 255 and if -/// you attempt to do so *IT WILL PANIC*. +/// Create a leaf/branch node, encoding a number of nibbles. fn fuse_nibbles_node_noext<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl Iterator + 'a { - debug_assert!(nibbles.len() < 255 + 84, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); - // We use two ranges of possible values; one for leafs and the other for extensions. - // Each range encodes zero following nibbles up to some maximum. If the maximum is - // reached, then it is considered "big" and a second byte follows it in order to - // encode a further offset to the number of nibbles of up to 255. Beyond that, we - // cannot encode. This shouldn't be a problem though since that allows for keys of - // up to 380 nibbles (190 bytes) and we expect key sizes to be generally 128-bit (16 - // bytes) or, at a push, 384-bit (48 bytes). - - let (first_byte_small, big_threshold) = match kind { - NodeKindNoExt::Leaf => (noext_cst::LEAF_NODE_OFFSET, noext_cst::LEAF_NODE_BIG as usize), - NodeKindNoExt::BranchNoValue => (noext_cst::BRANCH_NODE_NO_VALUE, noext_cst::BRANCH_NODE_NO_VALUE_BIG as usize), - NodeKindNoExt::BranchWithValue => (noext_cst::BRANCH_NODE_WITH_VALUE, noext_cst::BRANCH_NODE_WITH_VALUE_BIG as usize), + let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibbles.len()); + + let iter_start = match kind { + NodeKindNoExt::Leaf => s_size_and_prefix_iter(size, s_cst::LEAF_PREFIX_MASK), + NodeKindNoExt::BranchNoValue => s_size_and_prefix_iter(size, s_cst::BRANCH_WITHOUT_MASK), + NodeKindNoExt::BranchWithValue => s_size_and_prefix_iter(size, s_cst::BRANCH_WITH_MASK), }; - let first_byte = first_byte_small + nibbles.len().min(big_threshold) as u8; - once(first_byte) - .chain(if nibbles.len() >= big_threshold { Some((nibbles.len() - big_threshold) as u8) } else { None }) - .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) - .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) + iter_start + .chain(nibbles[..nibbles.len() - (nibbles.len() % 2)].chunks(2).map(|ch| ch[0] << 4 | ch[1])) + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[nibbles.len() - 1] << 4) } else { None }) } pub fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { @@ -270,7 +258,7 @@ impl TrieStream for ReferenceTrieStreamNoExt { } fn append_empty_data(&mut self) { - self.buffer.push(noext_cst::EMPTY_TRIE); + self.buffer.push(s_cst::EMPTY_TRIE); } fn append_leaf(&mut self, key: &[u8], value: &[u8]) { @@ -342,83 +330,95 @@ impl Encode for NodeHeader { } } +fn s_size_and_prefix_iter(size: usize, prefix: u8) -> impl Iterator { + let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, size); + + let l1 = std::cmp::min(62, size); + let (first_byte, mut rem) = if size == l1 { + (once(prefix + l1 as u8), 0) + } else { + (once(prefix + 63), size - l1) + }; + let next_bytes = move || { + if rem > 0 { + if rem < 256 { + let res = rem - 1; + rem = 0; + Some(res as u8) + } else { + rem = rem.saturating_sub(255); + Some(255) + } + } else { + None + } + }; + first_byte.chain(::std::iter::from_fn(next_bytes)) +} + /// bounding size to storage in a u16 variable to avoid dos -fn s_encode_size_and_prefix(size: usize, prefix: u8, out: &mut T) { - let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, size); - let l1 = std::cmp::min(62, size); - let mut rem = size - l1; - if rem == 0 { - out.push_byte(prefix + l1 as u8); - return; - } else { - out.push_byte(prefix + 63); - } - while rem > 0 { - if rem < 256 { - out.push_byte((rem - 1) as u8); - return; - } else { - out.push_byte(255); - rem = rem.saturating_sub(255); - } - } +fn s_encode_size_and_prefix(size: usize, prefix: u8, out: &mut impl Output) { + for b in s_size_and_prefix_iter(size, prefix) { + out.push_byte(b) + } } + fn s_decode_size(first: u8, input: &mut I) -> Option { - let mut result = (first & 255u8 >> 2) as usize; - if result < 63 { - return Some(result); - } - result -= 1; - while result <= s_cst::NIBBLE_SIZE_BOUND { - let n = input.read_byte()? as usize; - if n < 255 { - return Some(result + n + 1); - } - result += 255; - } - Some(s_cst::NIBBLE_SIZE_BOUND) + let mut result = (first & 255u8 >> 2) as usize; + if result < 63 { + return Some(result); + } + result -= 1; + while result <= s_cst::NIBBLE_SIZE_BOUND { + let n = input.read_byte()? as usize; + if n < 255 { + return Some(result + n + 1); + } + result += 255; + } + Some(s_cst::NIBBLE_SIZE_BOUND) } #[test] fn test_encoding_simple_trie() { - for prefix in [ - s_cst::LEAF_PREFIX_MASK, - s_cst::BRANCH_WITHOUT_MASK, - s_cst::BRANCH_WITH_MASK, - ].iter() { - for i in (0..1000) - .chain(s_cst::NIBBLE_SIZE_BOUND - 2..s_cst::NIBBLE_SIZE_BOUND + 2) { - let mut output = Vec::new(); - s_encode_size_and_prefix(i, *prefix, &mut output); - let input = &mut &output[..]; - let first = input.read_byte().unwrap(); - assert_eq!(first & (0b11 << 6), *prefix); - let v = s_decode_size(first, input); - assert_eq!(Some(std::cmp::min(i, s_cst::NIBBLE_SIZE_BOUND)), v); - } - - } + for prefix in [ + s_cst::LEAF_PREFIX_MASK, + s_cst::BRANCH_WITHOUT_MASK, + s_cst::BRANCH_WITH_MASK, + ].iter() { + for i in (0..1000) + .chain(s_cst::NIBBLE_SIZE_BOUND - 2..s_cst::NIBBLE_SIZE_BOUND + 2) { + let mut output = Vec::new(); + s_encode_size_and_prefix(i, *prefix, &mut output); + let input = &mut &output[..]; + let first = input.read_byte().unwrap(); + assert_eq!(first & (0b11 << 6), *prefix); + let v = s_decode_size(first, input); + assert_eq!(Some(std::cmp::min(i, s_cst::NIBBLE_SIZE_BOUND)), v); + } + + } } /* #[test] fn test_mal() { - let mut unique = std::collections::BTreeMap::new(); - // test over 32 bit only is still 4 byte & this bruteforce takes way to long... - for i in (0..u32::max_value() / 4) { - let enc = i.to_be_bytes(); - assert!(enc[0] >> 6 == 0); - let input = &mut &enc[..]; - let first = input.read_byte().unwrap(); - if let Some(v) = s_decode_size(first, input) { - let mut rem = 0; - while let Some(_) = input.read_byte() { rem += 1 } - if let Some((prev,prem)) = unique.insert(v, (enc.to_vec(),rem)) { - assert_eq!(&enc[..4 - rem], &prev[..4 - prem], - "duplicated key val {} {} {:x?} {:x?}", v, i, prev, enc); - } - } - } + let mut unique = std::collections::BTreeMap::new(); + // test over 32 bit only is still 4 byte & this bruteforce takes way to long... + for i in (0..u32::max_value() / 4) { + let enc = i.to_be_bytes(); + assert!(enc[0] >> 6 == 0); + let input = &mut &enc[..]; + let first = input.read_byte().unwrap(); + if let Some(v) = s_decode_size(first, input) { + let mut rem = 0; + while let Some(_) = input.read_byte() { rem += 1 } + if let Some((prev,prem)) = unique.insert(v, (enc.to_vec(),rem)) { + assert_eq!(&enc[..4 - rem], &prev[..4 - prem], + "duplicated key val {} {} {:x?} {:x?}", v, i, prev, enc); + } + } + } } */ @@ -426,12 +426,12 @@ impl Encode for NodeHeaderNoExt { fn encode_to(&self, output: &mut T) { match self { NodeHeaderNoExt::Null => output.push_byte(s_cst::EMPTY_TRIE), - NodeHeaderNoExt::Branch(true, nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITH_MASK, output), + NodeHeaderNoExt::Branch(true, nibble_count) => + s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITH_MASK, output), NodeHeaderNoExt::Branch(false, nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITHOUT_MASK, output), + s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITHOUT_MASK, output), NodeHeaderNoExt::Leaf(nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::LEAF_PREFIX_MASK, output), + s_encode_size_and_prefix(*nibble_count, s_cst::LEAF_PREFIX_MASK, output), } } } @@ -451,16 +451,16 @@ impl Decode for NodeHeader { impl Decode for NodeHeaderNoExt { fn decode(input: &mut I) -> Option { let i = input.read_byte()?; - if i == s_cst::EMPTY_TRIE { - return Some(NodeHeaderNoExt::Null); - } - match i & (0b11 << 6) { - s_cst::LEAF_PREFIX_MASK => Some(NodeHeaderNoExt::Leaf(s_decode_size(i, input)?)), - s_cst::BRANCH_WITHOUT_MASK => Some(NodeHeaderNoExt::Branch(false, s_decode_size(i, input)?)), - s_cst::BRANCH_WITH_MASK => Some(NodeHeaderNoExt::Branch(true, s_decode_size(i, input)?)), - // do not allow any special encoding - _ => None, - } + if i == s_cst::EMPTY_TRIE { + return Some(NodeHeaderNoExt::Null); + } + match i & (0b11 << 6) { + s_cst::LEAF_PREFIX_MASK => Some(NodeHeaderNoExt::Leaf(s_decode_size(i, input)?)), + s_cst::BRANCH_WITHOUT_MASK => Some(NodeHeaderNoExt::Branch(false, s_decode_size(i, input)?)), + s_cst::BRANCH_WITH_MASK => Some(NodeHeaderNoExt::Branch(true, s_decode_size(i, input)?)), + // do not allow any special encoding + _ => None, + } } } @@ -525,7 +525,7 @@ fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec::default(); - // TODO EMCH siwtch this to post and implement post on ref_trie_root_unhashed_no_ext!! + // TODO EMCH siwtch this to post and implement post on ref_trie_root_unhashed_no_ext!! trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -1034,21 +1034,17 @@ pub fn compare_no_ext_insert_remove( assert_eq!(*t.root(), calc_root_no_ext(data2)); } -// TODO define how this should be handle: -// panic does not look really good: -// Either redesign encoding trait for for errors -// or bound it (currently the code overflow 255 to -// 0 and truncate). -#[should_panic] +// TODO to big is currently truncate, keep it that way?? #[test] fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode - let input = vec![0u8; (noext_cst::LEAF_NODE_OVER as usize + 256) / 2 + 1]; - let enc = >::leaf_node(&input, &[1]); - let dec = >::decode(&enc).unwrap(); + let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; + let enc = >::leaf_node(&input, &[1]); + let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { - //assert_eq!(&input[..], &sl.encoded(false)[..]); + assert_eq!(&input[..s_cst::NIBBLE_SIZE_BOUND as usize / 2], + &sl.encoded(false)[..s_cst::NIBBLE_SIZE_BOUND as usize / 2]); Some(sl) } else { None }; - //assert!(o_sl.is_some()); + assert!(o_sl.is_some()); } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index f1cf0bc4..ae15cd4c 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -701,10 +701,11 @@ mod test { ]); } #[test] - #[should_panic] fn too_big_nibble_len_new () { + // truncate keep things working in both situation (but will conflict for multiple common prefix + // val!!) compare_impl_no_ext(vec![ - (vec![01u8;64 + 255],vec![0;32]), + (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1],vec![0;32]), ]); } From f21398bf64f3cde66daaf8c0ae6956d722cbf5be Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 17 Apr 2019 14:49:50 +0200 Subject: [PATCH 028/120] Back to prefix padded nibble (TODO see if removal of all previous change or attempt at generalizing code). --- test-support/reference-trie/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 5530dba1..fc620e01 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -58,7 +58,7 @@ impl TrieLayOut for LayoutNew { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; - type N = NibblePostHalf; + type N = NibblePreHalf; } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; @@ -172,8 +172,10 @@ fn fuse_nibbles_node_noext<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl I NodeKindNoExt::BranchWithValue => s_size_and_prefix_iter(size, s_cst::BRANCH_WITH_MASK), }; iter_start - .chain(nibbles[..nibbles.len() - (nibbles.len() % 2)].chunks(2).map(|ch| ch[0] << 4 | ch[1])) - .chain(if nibbles.len() % 2 == 1 { Some(nibbles[nibbles.len() - 1] << 4) } else { None }) + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) + .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) + //.chain(nibbles[..nibbles.len() - (nibbles.len() % 2)].chunks(2).map(|ch| ch[0] << 4 | ch[1])) + //.chain(if nibbles.len() % 2 == 1 { Some(nibbles[nibbles.len() - 1] << 4) } else { None }) } pub fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { @@ -868,7 +870,7 @@ pub fn compare_unhashed_no_ext( let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); // TODO EMCH siwtch this to post and implement post on ref_trie_root_unhashed_no_ext!! - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -900,7 +902,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -911,7 +913,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -967,7 +969,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -1039,11 +1041,9 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node(&input, &[1]); - let dec = >::decode(&enc).unwrap(); + let enc = >::leaf_node(&input, &[1]); + let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { - assert_eq!(&input[..s_cst::NIBBLE_SIZE_BOUND as usize / 2], - &sl.encoded(false)[..s_cst::NIBBLE_SIZE_BOUND as usize / 2]); Some(sl) } else { None }; assert!(o_sl.is_some()); From d36ba7d40ec822b957d74f32b71bdc721c45aa1b Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 18 Apr 2019 20:40:08 +0200 Subject: [PATCH 029/120] Simplification of nibleslice, not stabilized yet (many unused and some new method could be rename to those). Optimizing some nibble arithmetic (no more concat, only a growable nibble for recursive encoding purpose). Tests fails, got get_len work but probably lot of things to fix. --- hash-db/src/lib.rs | 30 +- memory-db/src/lib.rs | 137 +++---- test-support/reference-trie/src/lib.rs | 103 ++--- trie-db/src/fatdb.rs | 2 +- trie-db/src/fatdbmut.rs | 12 +- trie-db/src/iter_build.rs | 50 +-- trie-db/src/lib.rs | 4 +- trie-db/src/lookup.rs | 2 +- trie-db/src/nibbleslice.rs | 516 ++++++++++--------------- trie-db/src/nibblevec.rs | 3 +- trie-db/src/node.rs | 4 +- trie-db/src/node_codec.rs | 7 +- trie-db/src/triedb.rs | 57 +-- trie-db/src/triedbmut.rs | 359 ++++++++++------- 14 files changed, 651 insertions(+), 635 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index c6aff276..fe65c834 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -32,6 +32,14 @@ pub trait MaybeDebug {} #[cfg(not(feature = "std"))] impl MaybeDebug for T {} + +/// Empty prefix constant +pub static EMPTY_PREFIX: Prefix<'static> = (&[], None); + +/// prefix for trie value, encoded as reference and a last padded byte to +/// match `left` function of nibbleslice +pub type Prefix<'a> = (&'a[u8], Option); + /// Trait describing an object that can hash a slice of bytes. Used to abstract /// other types over the hashing algorithm. Defines a single `hash` method and an /// `Out` associated type with the necessary bounds. @@ -94,42 +102,42 @@ impl<'a, K, V> PlainDBRef for &'a mut PlainDB { pub trait HashDB: Send + Sync + AsHashDB { /// Look up a given hash into the bytes that hash to it, returning None if the /// hash is not known. - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option; + fn get(&self, key: &H::Out, prefix: Prefix) -> Option; /// Check for the existance of a hash-key. - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool; + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool; /// Insert a datum item into the DB and return the datum's hash for a later lookup. Insertions /// are counted and the equivalent number of `remove()`s must be performed before the data /// is considered dead. - fn insert(&mut self, prefix: &[u8], value: &[u8]) -> H::Out; + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out; /// Like `insert()`, except you provide the key and the data is all moved. - fn emplace(&mut self, key: H::Out, prefix: &[u8], value: T); + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T); /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may /// happen without the data being eventually being inserted into the DB. It can be "owed" more than once. - fn remove(&mut self, key: &H::Out, prefix: &[u8]); + fn remove(&mut self, key: &H::Out, prefix: Prefix); } /// Trait for immutable reference of HashDB. pub trait HashDBRef { /// Look up a given hash into the bytes that hash to it, returning None if the /// hash is not known. - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option; + fn get(&self, key: &H::Out, prefix: Prefix) -> Option; /// Check for the existance of a hash-key. - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool; + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool; } impl<'a, H: Hasher, T> HashDBRef for &'a HashDB { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(*self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(*self, key, prefix) } } impl<'a, H: Hasher, T> HashDBRef for &'a mut HashDB { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(*self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(*self, key, prefix) } } /// Upcast trait for HashDB. diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 03c44f61..fece535a 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -26,7 +26,7 @@ extern crate hashmap_core; extern crate alloc; #[cfg(test)] extern crate keccak_hasher; -use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, AsHashDB, AsPlainDB}; +use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, AsHashDB, AsPlainDB, Prefix}; #[cfg(feature = "std")] use heapsize::HeapSizeOf; #[cfg(feature = "std")] @@ -71,38 +71,38 @@ use alloc::vec::Vec; /// extern crate keccak_hasher; /// extern crate memory_db; /// -/// use hash_db::{Hasher, HashDB}; +/// use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; /// use keccak_hasher::KeccakHasher; /// use memory_db::{MemoryDB, HashKey}; /// fn main() { /// let mut m = MemoryDB::, Vec>::default(); /// let d = "Hello world!".as_bytes(); /// -/// let k = m.insert(&[], d); -/// assert!(m.contains(&k, &[])); -/// assert_eq!(m.get(&k, &[]).unwrap(), d); +/// let k = m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); +/// assert_eq!(m.get(&k, EMPTY_PREFIX).unwrap(), d); /// -/// m.insert(&[], d); -/// assert!(m.contains(&k, &[])); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// -/// m.insert(&[], d); -/// assert!(!m.contains(&k, &[])); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); -/// m.insert(&[], d); -/// assert!(m.contains(&k, &[])); -/// assert_eq!(m.get(&k, &[]).unwrap(), d); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); +/// assert_eq!(m.get(&k, EMPTY_PREFIX).unwrap(), d); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// } /// ``` #[derive(Clone)] @@ -147,20 +147,21 @@ impl Eq for MemoryDB pub trait KeyFunction { type Key: Send + Sync + Clone + hash::Hash + Eq ; - fn key(hash: &H::Out, prefix: &[u8]) -> Self::Key; + fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; } /// Make database key from hash and prefix. -pub fn prefixed_key(key: &H::Out, prefix: &[u8]) -> Vec { - let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.len()); - prefixed_key.extend_from_slice(prefix); +pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + prefixed_key.extend_from_slice(prefix.0); + prefix.1.map(|v|prefixed_key.push(v)); prefixed_key.extend_from_slice(key.as_ref()); prefixed_key } /// Make database key from hash only. -pub fn hash_key(key: &H::Out, _prefix: &[u8]) -> H::Out { +pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { key.clone() } @@ -170,7 +171,7 @@ pub struct HashKey(PhantomData); impl KeyFunction for HashKey { type Key = H::Out; - fn key(hash: &H::Out, prefix: &[u8]) -> H::Out { + fn key(hash: &H::Out, prefix: Prefix) -> H::Out { hash_key::(hash, prefix) } } @@ -181,7 +182,7 @@ pub struct PrefixedKey(PhantomData); impl KeyFunction for PrefixedKey { type Key = Vec; - fn key(hash: &H::Out, prefix: &[u8]) -> Vec { + fn key(hash: &H::Out, prefix: Prefix) -> Vec { prefixed_key::(hash, prefix) } } @@ -206,7 +207,7 @@ where { /// Remove an element and delete it from storage if reference count reaches zero. /// If the value was purged, return the old value. - pub fn remove_and_purge(&mut self, key: &::Out, prefix: &[u8]) -> Option { + pub fn remove_and_purge(&mut self, key: &::Out, prefix: Prefix) -> Option { if key == &self.hashed_null_node { return None; } @@ -262,10 +263,10 @@ where /// fn main() { /// let mut m = MemoryDB::, Vec>::default(); /// let hello_bytes = "Hello world!".as_bytes(); - /// let hash = m.insert(&[], hello_bytes); - /// assert!(m.contains(&hash, &[])); + /// let hash = m.insert(EMPTY_PREFIX, hello_bytes); + /// assert!(m.contains(&hash, EMPTY_PREFIX)); /// m.clear(); - /// assert!(!m.contains(&hash, &[])); + /// assert!(!m.contains(&hash, EMPTY_PREFIX)); /// } /// ``` pub fn clear(&mut self) { @@ -287,7 +288,7 @@ where /// /// Even when Some is returned, the data is only guaranteed to be useful /// when the refs > 0. - pub fn raw(&self, key: &::Out, prefix: &[u8]) -> Option<(&T, i32)> { + pub fn raw(&self, key: &::Out, prefix: Prefix) -> Option<(&T, i32)> { if key == &self.hashed_null_node { return Some((&self.null_node_data, 1)); } @@ -404,19 +405,20 @@ where T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, KF: Send + Sync + KeyFunction, { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { if key == &self.hashed_null_node { return Some(self.null_node_data.clone()); } let key = KF::key(key, prefix); - match self.data.get(&key) { + let r = match self.data.get(&key) { Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), _ => None - } + }; + r } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { if key == &self.hashed_null_node { return true; } @@ -428,7 +430,7 @@ where } } - fn emplace(&mut self, key: H::Out, prefix: &[u8], value: T) { + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) { if value == self.null_node_data { return; } @@ -448,7 +450,7 @@ where } } - fn insert(&mut self, prefix: &[u8], value: &[u8]) -> H::Out { + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { if T::from(value) == self.null_node_data { return self.hashed_null_node.clone(); } @@ -458,7 +460,7 @@ where key } - fn remove(&mut self, key: &H::Out, prefix: &[u8]) { + fn remove(&mut self, key: &H::Out, prefix: Prefix) { if key == &self.hashed_null_node { return; } @@ -482,8 +484,8 @@ where T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, KF: Send + Sync + KeyFunction, { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(self, key, prefix) } } impl AsPlainDB for MemoryDB @@ -510,6 +512,7 @@ where #[cfg(test)] mod tests { use super::{MemoryDB, HashDB, KeyHasher, HashKey}; + use hash_db::EMPTY_PREFIX; use keccak_hasher::KeccakHasher; #[test] @@ -518,52 +521,52 @@ mod tests { let hello_key = KeccakHasher::hash(hello_bytes); let mut m = MemoryDB::, Vec>::default(); - m.remove(&hello_key, &[]); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); + m.remove(&hello_key, EMPTY_PREFIX); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); m.purge(); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); - m.insert(&[], hello_bytes); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, 0); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); + m.insert(EMPTY_PREFIX, hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, 0); m.purge(); - assert_eq!(m.raw(&hello_key, &[]), None); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX), None); let mut m = MemoryDB::, Vec>::default(); - assert!(m.remove_and_purge(&hello_key, &[]).is_none()); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); - m.insert(&[], hello_bytes); - m.insert(&[], hello_bytes); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, 1); - assert_eq!(&*m.remove_and_purge(&hello_key, &[]).unwrap(), hello_bytes); - assert_eq!(m.raw(&hello_key, &[]), None); - assert!(m.remove_and_purge(&hello_key, &[]).is_none()); + assert!(m.remove_and_purge(&hello_key, EMPTY_PREFIX).is_none()); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); + m.insert(EMPTY_PREFIX, hello_bytes); + m.insert(EMPTY_PREFIX, hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, 1); + assert_eq!(&*m.remove_and_purge(&hello_key, EMPTY_PREFIX).unwrap(), hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX), None); + assert!(m.remove_and_purge(&hello_key, EMPTY_PREFIX).is_none()); } #[test] fn consolidate() { let mut main = MemoryDB::, Vec>::default(); let mut other = MemoryDB::, Vec>::default(); - let remove_key = other.insert(&[], b"doggo"); - main.remove(&remove_key, &[]); + let remove_key = other.insert(EMPTY_PREFIX, b"doggo"); + main.remove(&remove_key, EMPTY_PREFIX); - let insert_key = other.insert(&[], b"arf"); - main.emplace(insert_key, &[], "arf".as_bytes().to_vec()); + let insert_key = other.insert(EMPTY_PREFIX, b"arf"); + main.emplace(insert_key, EMPTY_PREFIX, "arf".as_bytes().to_vec()); - let negative_remove_key = other.insert(&[], b"negative"); - other.remove(&negative_remove_key, &[]); // ref cnt: 0 - other.remove(&negative_remove_key, &[]); // ref cnt: -1 - main.remove(&negative_remove_key, &[]); // ref cnt: -1 + let negative_remove_key = other.insert(EMPTY_PREFIX, b"negative"); + other.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: 0 + other.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: -1 + main.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: -1 main.consolidate(other); - assert_eq!(main.raw(&remove_key, &[]).unwrap(), (&"doggo".as_bytes().to_vec(), 0)); - assert_eq!(main.raw(&insert_key, &[]).unwrap(), (&"arf".as_bytes().to_vec(), 2)); - assert_eq!(main.raw(&negative_remove_key, &[]).unwrap(), (&"negative".as_bytes().to_vec(), -2)); + assert_eq!(main.raw(&remove_key, EMPTY_PREFIX).unwrap(), (&"doggo".as_bytes().to_vec(), 0)); + assert_eq!(main.raw(&insert_key, EMPTY_PREFIX).unwrap(), (&"arf".as_bytes().to_vec(), 2)); + assert_eq!(main.raw(&negative_remove_key, EMPTY_PREFIX).unwrap(), (&"negative".as_bytes().to_vec(), -2)); } #[test] fn default_works() { let mut db = MemoryDB::, Vec>::default(); let hashed_null_node = KeccakHasher::hash(&[0u8][..]); - assert_eq!(db.insert(&[], &[0u8][..]), hashed_null_node); + assert_eq!(db.insert(EMPTY_PREFIX, &[0u8][..]), hashed_null_node); } } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index fc620e01..f7a8c556 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -33,12 +33,13 @@ use trie_db::{ trie_visit_no_ext, TrieBuilder, TrieRoot, + Partial, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder}; -pub use trie_db::{Record, TrieLayOut, NibblePreHalf, NibbleOps, NibblePostHalf}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; +pub use trie_db::{Record, TrieLayOut, NibblePreHalf, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum @@ -504,46 +505,58 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { Some(r) } -fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; - let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); - let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; +fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { + let nb_nibble_hpe = if partial.0.is_some() { 1 } else { 0 }; + let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if !N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[..hpe_pos]); - } - if nb_nibble_hpe > 0 { - output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); - } - if N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[1..]); + if let Some(v) = partial.0 { + output.push((v & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); } + output.extend_from_slice(&partial.1[..]); output } -fn partial_enc(partial: &[u8], node_kind: NodeKindNoExt) -> Vec { - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { partial.len() - 1 }; - let nb_nibble_hpe = N::nb_nibble_hpe(partial[hpe_pos]); - let nibble_count = (partial.len() - 1) * N::NIBBLE_PER_BYTE + nb_nibble_hpe; +fn partial_to_key_it>(partial: I, nibble_count: usize, offset: u8, over: u8) -> Vec { + assert!(nibble_count < over as usize); + let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); + output.push(offset + nibble_count as u8); + output.extend(partial); + output +} + +fn partial_enc_it>(partial: I, nibble_count: usize, node_kind: NodeKindNoExt) -> Vec { let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); - let mut output = Vec::with_capacity(2 + partial.len()); + let mut output = Vec::with_capacity(3 + nibble_count); match node_kind { NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; - if !N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[..hpe_pos]); - } - if nb_nibble_hpe > 0 { - output.push((partial[hpe_pos] & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); - } - if N::PADD_AT_BEGIN { - output.extend_from_slice(&partial[1..]); + output.extend(partial); + output +} + + + +fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec { + let nb_nibble_hpe = if partial.0.is_some() { 1 } else { 0 }; + let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; + + let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); + + let mut output = Vec::with_capacity(3 + partial.1.len()); + match node_kind { + NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + }; + if let Some(v) = partial.0 { + output.push((v & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); } + output.extend_from_slice(&partial.1[..]); output } @@ -586,7 +599,7 @@ impl NodeCodec for ReferenceNodeCodec { NodeHeader::Extension(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_padded(nibble_data, + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) @@ -594,7 +607,7 @@ impl NodeCodec for ReferenceNodeCodec { NodeHeader::Leaf(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_padded(nibble_data, + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) @@ -620,14 +633,14 @@ impl NodeCodec for ReferenceNodeCodec { &[EMPTY_TRIE] } - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { let mut output = partial_to_key::(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); value.encode_to(&mut output); output } - fn ext_node(partial: &[u8], child: ChildReference<::Out>) -> Vec { - let mut output = partial_to_key::(partial, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + fn ext_node(partial: impl Iterator, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { + let mut output = partial_to_key_it::(partial, nb_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -661,7 +674,8 @@ impl NodeCodec for ReferenceNodeCodec { } fn branch_node_nibbled( - _partial: &[u8], + _partial: impl Iterator, + _nb_nibble: usize, _children: impl Iterator::Out>>>>, _maybe_value: Option<&[u8]>) -> Vec { unreachable!() @@ -683,13 +697,12 @@ impl NodeCodec for ReferenceNodeCodecNoExt { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; - if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { + if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_padded(nibble_data, + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { @@ -711,13 +724,12 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } NodeHeaderNoExt::Leaf(nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - let hpe_pos = if N::PADD_AT_BEGIN { 0 } else { nibble_count / N::NIBBLE_PER_BYTE }; - if nb_nibble_hpe > 0 && input[hpe_pos] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { + if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_padded(nibble_data, + let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) @@ -737,13 +749,13 @@ impl NodeCodec for ReferenceNodeCodecNoExt { &[s_cst::EMPTY_TRIE] } - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { let mut output = partial_enc::(partial, NodeKindNoExt::Leaf); value.encode_to(&mut output); output } - fn ext_node(_partial: &[u8], _child: ChildReference<::Out>) -> Vec { + fn ext_node(_partial: impl Iterator, _nbnibble: usize, _child: ChildReference<::Out>) -> Vec { unreachable!() } @@ -754,13 +766,14 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn branch_node_nibbled( - partial: &[u8], + partial: impl Iterator, + nb_nibble: usize, children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { let mut output = if maybe_value.is_some() { - partial_enc::(partial, NodeKindNoExt::BranchWithValue) + partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchWithValue) } else { - partial_enc::(partial, NodeKindNoExt::BranchNoValue) + partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchNoValue) }; let bm_ix = output.len(); output.push(0); @@ -1041,7 +1054,7 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node(&input, &[1]); + let enc = >::leaf_node((None,&input), (&[1])); let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 0e7c8d04..361179ab 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -108,7 +108,7 @@ where .map(|res| { res.map(|(hash, value)| { let aux_hash = L::H::hash(&hash); - (self.trie.db().get(&aux_hash, &[]).expect("Missing fatdb hash").into_vec(), value) + (self.trie.db().get(&aux_hash, (&[], None)).expect("Missing fatdb hash").into_vec(), value) }) }) } diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 58d5f4f9..e53fd7f1 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{HashDB, Hasher}; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayOut, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. @@ -81,7 +81,7 @@ where // insert if it doesn't exist. if out.is_none() { let aux_hash = L::H::hash(hash.as_ref()); - db.emplace(aux_hash, &[], DBValue::from_slice(key)); + db.emplace(aux_hash, EMPTY_PREFIX, DBValue::from_slice(key)); } Ok(out) } @@ -93,7 +93,7 @@ where // remove if it already exists. if out.is_some() { let aux_hash = L::H::hash(hash.as_ref()); - self.raw.db_mut().remove(&aux_hash, &[]); + self.raw.db_mut().remove(&aux_hash, EMPTY_PREFIX); } Ok(out) @@ -104,7 +104,7 @@ where mod test { use DBValue; use memory_db::{MemoryDB, HashKey}; - use hash_db::Hasher; + use hash_db::{Hasher, EMPTY_PREFIX}; use keccak_hasher::KeccakHasher; use reference_trie::{RefFatDBMut, RefTrieDB, Trie, TrieMut}; @@ -131,8 +131,8 @@ mod test { let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&key, &val).unwrap(); assert_eq!(t.get(&key), Ok(Some(DBValue::from_slice(&val)))); - assert_eq!(t.db().get(&aux_hash, &[]), Some(DBValue::from_slice(&key))); + assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), Some(DBValue::from_slice(&key))); t.remove(&key).unwrap(); - assert_eq!(t.db().get(&aux_hash, &[]), None); + assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), None); } } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index ae15cd4c..e8a3abad 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -14,12 +14,12 @@ //! Alternative tools for working with key value iterator without recursion. -use hash_db::{Hasher, HashDB}; +use hash_db::{Hasher, HashDB, Prefix}; use std::marker::PhantomData; use crate::triedbmut::{ChildReference}; use crate::nibbleslice::NibbleSlice; use crate::nibbleslice::NibbleOps; -use node_codec::NodeCodec; +use node_codec::{NodeCodec, Partial}; // TODO EMCH use L instead of HC (aka TrieLayout) // TODO EMCH move to NibbleOps to use right constants @@ -135,12 +135,11 @@ where let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); // Note: fwiu, having fixed key size, all values are in leaf (no value in // branch). TODO run metrics on a node to count branch with values - let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); + let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes - // in fact or TODO put Vec in the trait? - let encoded_key = NibbleSlice::::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); - let hash = cb_ext.process(&encoded_key[..], encoded, false); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len()); + let hash = cb_ext.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) self.set_node(target_depth, nibble_value as usize, Some(hash)); @@ -228,14 +227,14 @@ where let v = self.0[branch_d].2.take(); let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d, false); - let branch_hash = cb_ext.process(&encoded_key[..], encoded, is_root && nkey.is_none()); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); + let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let nib = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false); - let encoded = C::ext_node(&nib[..], branch_hash); - let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(nkeyix.0, false); - let h = cb_ext.process(&encoded_key[..], encoded, is_root); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let nib = pr.range_iter(nkeyix.1); + let encoded = C::ext_node(nib, nkeyix.1 - nkeyix.0, branch_hash); + let h = cb_ext.process(pr.left(), encoded, is_root); h } else { branch_hash @@ -253,16 +252,17 @@ where ) -> ChildReference<::Out> { // enc branch let v = self.0[branch_d].2.take(); - let enc_nkey = nkey.as_ref() - .map(|nkeyix|NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0).encoded_leftmost(nkeyix.1, false)); + let nkeyix = nkey.unwrap_or((0,0)); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); - enc_nkey.as_ref().map(|v|&v[..]).unwrap_or(&[0]), + pr.range_iter(nkeyix.1), + nkeyix.1 - nkeyix.0, self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let encoded_key = NibbleSlice::::new(&key_branch.as_ref()[..]).encoded_leftmost(branch_d - ext_len, false); - cb_ext.process(&encoded_key[..], encoded, is_root) + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); + cb_ext.process(pr.left(), encoded, is_root) } } @@ -345,9 +345,9 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) // one single element corner case let (k2, v2) = prev_val; let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],prev_depth); - let encoded = C::leaf_node(&nkey.encoded(true).as_ref()[..], &v2.as_ref()[..]); - let encoded_key = NibbleSlice::::new(&k2.as_ref()[..]).encoded_leftmost(k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len(), false); - cb_ext.process(&encoded_key[..], encoded, true); + let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len()); + cb_ext.process(pr.left(), encoded, true); } else { //println!("fbvl {}", prev_depth); depth_queue.flush_val(cb_ext, prev_depth, &prev_val); @@ -357,12 +357,12 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) } } else { // nothing null root corner case TODO warning hardcoded empty nibbleslice - cb_ext.process(N::EMPTY_ENCODED, C::empty_node().to_vec(), true); + cb_ext.process(crate::nibbleslice::EMPTY_ENCODED, C::empty_node().to_vec(), true); } } pub trait ProcessEncodedNode { - fn process(&mut self, encoded_prefix: &[u8], Vec, bool) -> ChildReference; + fn process(&mut self, encoded_prefix: Prefix, Vec, bool) -> ChildReference; } /// Get trie root and insert node in hash db on parsing. @@ -381,7 +381,7 @@ impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { } impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { - fn process(&mut self, encoded_prefix: &[u8], enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + fn process(&mut self, encoded_prefix: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); @@ -411,7 +411,7 @@ impl Default for TrieRoot { } impl ProcessEncodedNode<::Out> for TrieRoot::Out> { - fn process(&mut self, _: &[u8], enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + fn process(&mut self, _: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); @@ -448,7 +448,7 @@ impl Default for TrieRootUnhashed { } impl ProcessEncodedNode<::Out> for TrieRootUnhashed { - fn process(&mut self, _: &[u8], enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + fn process(&mut self, _: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 1dfba8b8..d0563b6a 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -96,8 +96,8 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf, NibblePostHalf}; -pub use node_codec::NodeCodec; +pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf}; +pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub type DBValue = elastic_array::ElasticArray128; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 34629beb..2599f29f 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -47,7 +47,7 @@ where // this loop iterates through non-inline nodes. for depth in 0.. { - let node_data = match self.db.get(&hash, &key.encoded_leftmost(key_nibbles, false)) { + let node_data = match self.db.get(&hash, key.mid(key_nibbles).left()) { Some(value) => value, None => return Err(Box::new(match depth { 0 => TrieError::InvalidStateRoot(hash), diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index a112abb9..0c29fc9e 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -16,10 +16,13 @@ use ::core_::cmp::*; use ::core_::fmt; -use elastic_array::ElasticArray36; use ::core_::marker::PhantomData; use nibblevec::NibbleVec; +use elastic_array::ElasticArray36; +use node::NodeKey; +use node_codec::Partial; +pub const EMPTY_ENCODED: (&'static [u8], Option) = (&[], None); // until const fn for pow const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Nibble specific variants @@ -34,49 +37,19 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; /// Number of nibble per node (must be power of 2 and under 256) const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); - /// Empty nibble encoded - const EMPTY_ENCODED: &'static [u8]; - /// Define wether we should pad eneven byte at start or at end - const PADD_AT_BEGIN: bool; /// padding value to apply (default to 0) const PADDING_VALUE: u8 = 0; /// padding bitmasks (could be calculated with a constant function). const PADDING_BITMASK: &'static [u8]; - /// mask for nibble encoded first byte for leaf - const NIBBLE_ODD_MASK: u8; - /// mask for nibble encoded first byte for extension TODO consider removal (unused) - const NIBBLE_EXT_MASK: u8; - /// mask for nibble encoded first byte for leaf - const NIBBLE_LEAF_MASK: u8; - - // TODO redesign nibble slice to run as cursor over a full key (concat could really benefit from - // it) - /// type for storing padding length (nothing for prefixed impl) - /// should default to no padding - type PADDING_LEN: Clone + Default + Copy + Into + Eq; - - /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). - fn from_encoded(data: &[u8]) -> (NibbleSlice, bool); - - /// Get the nibble at position `i`. - fn at(&NibbleSlice, i: usize) -> u8; - - /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. #[inline] - fn encoded(s: &NibbleSlice, is_leaf: bool) -> ElasticArray36 { - Self::encoded_leftmost_unchecked(s, s.len(), is_leaf) + /// Number of padding needed for a length `i`. + fn nb_padding(i: usize) -> usize { + // TODO bench something faster + (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE } - - /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, - /// noting whether it `is_leaf`. - fn encoded_leftmost(s: &NibbleSlice, n: usize, is_leaf: bool) -> ElasticArray36 { - let l = min(s.len(), n); - Self::encoded_leftmost_unchecked(s, l, is_leaf) - } - - /// encoded leftmost without checking end bound - fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36; + /// Get the nibble at position `i`. + fn at(&NibbleSlice, i: usize) -> u8; /// Try to get the nibble at the given offset. fn vec_at(s: &NibbleVec, idx: usize) -> u8; @@ -87,13 +60,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. fn pop(s: &mut NibbleVec) -> Option; - /// get nb nibble from hpe - fn nb_nibble_hpe(hpe: u8) -> usize; - /// get nb padding in hpe from nibble size - fn nb_padding(len: usize) -> usize; - - /// conveniance convert to avoid new type - fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN; } /// half byte nibble prepend encoding @@ -125,180 +91,18 @@ impl Into for Empty { // TODO EMCH some method can be fuse with post half (see bug solving: eg new_offset and // new_padded_end merged impl NibbleOps for NibblePreHalf { - const PADD_AT_BEGIN: bool = true; - const EMPTY_ENCODED: &'static [u8] = &[0]; const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [u8] = &[0x0f]; - const NIBBLE_ODD_MASK: u8 = 0x10; - const NIBBLE_EXT_MASK: u8 = 0x00; - const NIBBLE_LEAF_MASK: u8 = 0x20; - - - type PADDING_LEN = Empty; - - fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { - if data.is_empty() { - (NibbleSlice::::new(&[]), false) - } else { - (NibbleSlice::::new_offset(data, - if data[0] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK {1} else {2}), - data[0] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK) - } - } - - #[inline(always)] - fn at(s: &NibbleSlice, i: usize) -> u8 { - let l = s.data.len() * Self::NIBBLE_PER_BYTE - s.offset; - if i < l { - if (s.offset + i) & 1 == 1 { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 - } - else { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 - } - } else { - let i = i - l; - if (s.offset_encode_suffix + i) & 1 == 1 { - s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] & 15u8 - } - else { - s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] >> 4 - } - } - } - - fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {Self::NIBBLE_ODD_MASK + Self::at(s, 0)} else {0} - + if is_leaf {Self::NIBBLE_LEAF_MASK} else {Self::NIBBLE_EXT_MASK}); - while i < l { - r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); - i += 2; - } - r - } - - #[inline] - fn vec_at(s: &NibbleVec, idx: usize) -> u8 { - if idx % 2 == 0 { - s.inner[idx / 2] >> 4 - } else { - s.inner[idx / 2] & 0x0F - } - } - - fn push(s: &mut NibbleVec, nibble: u8) { - let nibble = nibble & 0x0F; - - if s.len % 2 == 0 { - s.inner.push(nibble << 4); - } else { - *s.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - } - - s.len += 1; - } - - fn pop(s: &mut NibbleVec) -> Option { - if s.is_empty() { - return None; - } - - let byte = s.inner.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if s.len % 2 == 0 { - s.inner.push(byte & 0xF0); - byte & 0x0F - } else { - byte >> 4 - }; - - s.len -= 1; - Some(nibble) - } - - #[inline] - fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { 1 } else { 0 } - } - #[inline] - fn nb_padding(len: usize) -> usize { - if len % Self::NIBBLE_PER_BYTE == 1 { 1 } else { 0 } - } - #[inline] - fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN { - debug_assert!(nb < 1); - Empty - } -} - -/// half byte nibble padding at end when encoded -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] -pub struct NibblePostHalf; - - -impl NibbleOps for NibblePostHalf { - const PADD_AT_BEGIN: bool = false; - const EMPTY_ENCODED: &'static [u8] = &[0]; - const REPR: ByteLayout = ByteLayout::Half; - const PADDING_BITMASK: &'static [u8] = &[0xf0]; - const NIBBLE_ODD_MASK: u8 = 0x01; - const NIBBLE_EXT_MASK: u8 = 0x00; - const NIBBLE_LEAF_MASK: u8 = 0x02; - type PADDING_LEN = bool; - - fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { - if data.is_empty() { - (NibbleSlice::::new(&[]), false) - } else { - let end_ix = data.len() - 1; - let is_leaf = data[end_ix] & Self::NIBBLE_LEAF_MASK == Self::NIBBLE_LEAF_MASK; - let (padding, data) = if data[end_ix] & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { - (true, &data[..]) - } else { - (false, &data[..end_ix]) - }; - (NibbleSlice::::new_padded_end(data, padding), is_leaf) - } - } #[inline(always)] fn at(s: &NibbleSlice, i: usize) -> u8 { - let l = s.data.len() * Self::NIBBLE_PER_BYTE - s.offset - s.end_padding as usize; - if i < l { - if (s.offset + i) & 1 == 1 { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 - } - else { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 - } + if (s.offset + i) & 1 == 1 { + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 } else { - let i = i - l; - debug_assert!( - i < s.data_encode_suffix.len() * Self::NIBBLE_PER_BYTE - - s.offset_encode_suffix - s.end_padding_suffix as usize); - if (s.offset_encode_suffix + i) & 1 == 1 { - s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] & 15u8 - } - else { - s.data_encode_suffix[(s.offset_encode_suffix + i) / Self::NIBBLE_PER_BYTE] >> 4 - } + s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 } } - fn encoded_leftmost_unchecked(s: &NibbleSlice, l: usize, is_leaf: bool) -> ElasticArray36 { - let mut r = ElasticArray36::new(); - let odd = l % Self::NIBBLE_PER_BYTE; - let mut i = 0; - while i < l - odd { - r.push(Self::at(s, i) * 16 + Self::at(s, i + 1)); - i += 2; - } - r.push(if odd == 1 {Self::NIBBLE_ODD_MASK + Self::at(s, l - 1) * 16} else {0} - + if is_leaf {Self::NIBBLE_LEAF_MASK} else {Self::NIBBLE_EXT_MASK}); - r - } - #[inline] fn vec_at(s: &NibbleVec, idx: usize) -> u8 { if idx % 2 == 0 { @@ -337,23 +141,9 @@ impl NibbleOps for NibblePostHalf { Some(nibble) } - #[inline] - fn nb_nibble_hpe(hpe: u8) -> usize { - if hpe & Self::NIBBLE_ODD_MASK == Self::NIBBLE_ODD_MASK { 1 } else { 0 } - } - #[inline] - fn nb_padding(len: usize) -> usize { - if len % Self::NIBBLE_PER_BYTE == 1 { 1 } else { 0 } - } - #[inline] - fn lossy_into_padding(nb: usize) -> Self::PADDING_LEN { - debug_assert!(nb < 2); - nb == 1 - } } - /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// /// This is an immutable struct. No operations actually change it. @@ -380,10 +170,6 @@ impl NibbleOps for NibblePostHalf { pub struct NibbleSlice<'a, N: NibbleOps> { data: &'a [u8], offset: usize, - end_padding: N::PADDING_LEN, - data_encode_suffix: &'a [u8], - offset_encode_suffix: usize, - end_padding_suffix: N::PADDING_LEN, marker: PhantomData, } @@ -406,51 +192,17 @@ impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Create a new nibble slice with the given byte-slice. - pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0, Default::default()) } + pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0) } /// Create a new nibble slice with the given byte-slice with a nibble offset. pub fn new_offset(data: &'a [u8], offset: usize) -> Self { - Self::new_slice(data, offset, Default::default()) + Self::new_slice(data, offset) } - /// Create a new nibble slice with the given padding - #[inline] - pub fn new_padded(data: &'a [u8], pad: usize) -> Self { - if N::PADD_AT_BEGIN { - Self::new_offset(data, pad) - } else { - Self::new_padded_end(data, N::lossy_into_padding(pad)) - } - } - - fn new_slice(data: &'a [u8], offset: usize, end_padding: N::PADDING_LEN) -> Self { + fn new_slice(data: &'a [u8], offset: usize) -> Self { NibbleSlice { data, offset, - end_padding, - data_encode_suffix: &b""[..], - offset_encode_suffix: 0, - end_padding_suffix: Default::default(), - marker: PhantomData, - } - } - - - /// Create a new nibble slice with the given byte-slice and a padding at the end. - /// This is only use for building encoded slice with end padding - fn new_padded_end(data: &'a [u8], pad: N::PADDING_LEN) -> Self { - Self::new_slice(data, 0, pad) - } - - /// Create a composed nibble slice; one followed by the other. - pub fn new_composed(a: &Self, b: &Self) -> Self { - NibbleSlice { - data: a.data, - offset: a.offset, - end_padding: a.end_padding, - data_encode_suffix: b.data, - offset_encode_suffix: b.offset, - end_padding_suffix: b.end_padding, marker: PhantomData, } } @@ -459,10 +211,39 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { NibbleSliceIterator { p: self, i: 0 } } - - /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). - pub fn from_encoded(data: &[u8]) -> (NibbleSlice, bool) { - N::from_encoded(data) +/* + /// Create a new nibble slice from the given encoded data, applying start padding. + pub fn from_encoded(data: &[u8], nibble_len: usize) -> NibbleSlice { + // TODO EMCH better expression or specialization for len + let start_padding = (N::NIBBLE_PER_BYTE - data.len() % N::NIBBLE_PER_BYTE) % N::NIBBLE_PER_BYTE; + NibbleSlice::::new_offset(data, start_padding) + }*/ + /// helper function for getting slice from `NodeKey` stored in nodes + pub fn from_stored(i: &(usize,ElasticArray36)) -> NibbleSlice { + NibbleSlice::::new_offset(&i.1[..], i.0) + } + /// helper function to get `NodeKey` stored in nodes + pub fn to_stored(&self) -> (usize,ElasticArray36) { + let split = self.offset / N::NIBBLE_PER_BYTE; + let offset = N::nb_padding(self.len()); + (offset, self.data[split..].into()) + } + + /// helper function to get `NodeKey` stored in nodes, warning slow + pub fn to_stored_range(&self, nb: usize) -> (usize,ElasticArray36) { + if nb == self.len() { return self.to_stored() } +/* TODO EMCH (commented thing is shit) if nb % N::NIBBLE_PER_BYTE == 0 { + let split = self.offset / 2; + let end = self.data.len() - (self.len() - (nb / N::NIBBLE_PER_BYTE)); + return (self.len(), self.data[split..end].into()) + }*/ + let mut ea = ElasticArray36::new(); + let iter = self.range_iter(nb); + for i in iter { + ea.push(i); + } + + (N::nb_padding(nb), ea) } /// Is this an empty slice? @@ -470,7 +251,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Get the length (in nibbles, naturally) of this slice. #[inline] - pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * N::NIBBLE_PER_BYTE - self.offset - self.offset_encode_suffix - self.end_padding.into() - self.end_padding_suffix.into() } + pub fn len(&self) -> usize { self.data.len() * N::NIBBLE_PER_BYTE - self.offset } /// Get the nibble at position `i`. #[inline(always)] @@ -483,13 +264,18 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { NibbleSlice { data: self.data, offset: self.offset + i, - end_padding: self.end_padding, - data_encode_suffix: &b""[..], - offset_encode_suffix: 0, - end_padding_suffix: self.end_padding_suffix, marker: PhantomData, } } + /// Return object to an offset position + pub fn back(&self, i: usize) -> NibbleSlice<'a, N> { + NibbleSlice { + data: self.data, + offset: i, + marker: PhantomData, + } + } + /// Do we start with the same nibbles as the whole of `them`? pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } @@ -505,18 +291,98 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { i } - /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. - #[inline] - pub fn encoded(&self, is_leaf: bool) -> ElasticArray36 { - N::encoded(self, is_leaf) + /// return first encoded byte and following slice + pub fn right(&'a self) -> (Option, &'a [u8]) { + let split = self.offset / 2; + if self.data.len() % 2 == 1 { + (Some(self.data[split] & (255 << 4)), &self.data[split + 1 ..]) + } else { + (None, &self.data[split..]) + } + } + + /// return left of key nibble + pub fn left(&'a self) -> (&'a [u8], Option) { + let split = self.offset / 2; + if self.len() % 2 == 1 { + (&self.data[..split], Some(self.data[split] & (255 >> 4))) + } else { + (&self.data[..split], None) + } + } + + /// get iterator over slice, slow + /// TODO switch to padded access as in right padded eg with a move end function that return self + /// and a padding len + pub fn range_iter(&'a self, to: usize) -> impl Iterator + 'a { + let mut first = to % 2; + let aligned_i = (self.offset + to) % 2; + let aligned = aligned_i == 0; + let mut ix = self.offset / 2; + let ix_lim = (self.offset + to) / 2; + ::std::iter::from_fn( move || { + if aligned { + if first > 0 { + first = 0; + ix += 1; + Some(self.data[ix - 1] & (255 >> 4)) + } else if ix < ix_lim { + ix += 1; + Some(self.data[ix - 1]) + } else { + None + } + } else { + // unaligned + if first > 0 { + first = 0; + Some((self.data[ix] & (255 << 4)) >> 4) + } else if ix < ix_lim { + ix += 1; + let b1 = (self.data[ix - 1] & (255 >> 4)) << 4; + let b2 = (self.data[ix] & (255 << 4)) >> 4; + Some(b1 | b2) + } else { + None + } + } + + }) + } +} + +// TODO EMCH not generic (not that partial type does not support nothing else than 4 byte) +impl<'a, N: NibbleOps> Into for NibbleSlice<'a, N> { + fn into(self) -> NodeKey { + (self.offset, self.data.into()) + } +} + +// TODO rename or enhanch +pub fn into_part(inp: &NodeKey) -> Partial { + if inp.0 / 2 == 1 { + (Some(inp.1[0]), + &inp.1[1..]) + } else { + (None, &inp.1[..]) } +} + - /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, - /// noting whether it `is_leaf`. - pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36 { - N::encoded_leftmost(self, n, is_leaf) +/* +// TODO EMCH use in prev into fn +impl<'a, N: NibbleOps> Into<(Option, &'a[u8])> for NibbleSlice<'a, N> { + fn into(self) -> (Option, &'a[u8]) { + if self.len() / 2 == 1 { + (Some(self.data[self.offset/2] & (255 >> 4)), + &self.data[self.offset/2 + 1..]) + } else { + (None, + &self.data[self.offset/2..]) + } } } +*/ impl<'a, N: NibbleOps> PartialEq for NibbleSlice<'a, N> { fn eq(&self, them: &Self) -> bool { @@ -559,17 +425,10 @@ impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { } } -/// Join two encoded nibble slices. -pub fn combine_encoded(prefix: &[u8], extension: &[u8]) -> ElasticArray36 { - let slice = NibbleSlice::::new_composed(&NibbleSlice::from_encoded(&prefix).0, &NibbleSlice::from_encoded(extension).0); - slice.encoded(false) -} - #[cfg(test)] mod tests { use super::NibbleSlice; use super::NibblePreHalf; - use super::NibblePostHalf; use super::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; @@ -577,7 +436,6 @@ mod tests { #[test] fn basics() { basics_inner::(); - basics_inner::(); } fn basics_inner() { let n = NibbleSlice::::new(D); @@ -597,7 +455,6 @@ mod tests { #[test] fn iterator() { iterator_inner::(); - iterator_inner::(); } fn iterator_inner() { let n = NibbleSlice::::new(D); @@ -609,7 +466,6 @@ mod tests { #[test] fn mid() { mid_inner::(); - mid_inner::(); } fn mid_inner() { let n = NibbleSlice::::new(D); @@ -626,12 +482,13 @@ mod tests { #[test] fn encoded_pre() { let n = NibbleSlice::::new(D); - assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); - assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); - assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45])); - assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x31, 0x23, 0x45])); + let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + assert_eq!(n.to_stored(), (0, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); + assert_eq!(n.mid(1).to_stored(), (1, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); + assert_eq!(n.mid(2).to_stored(), (0, ElasticArray36::from_slice(&[0x23, 0x45]))); + assert_eq!(n.mid(3).to_stored(), (1, ElasticArray36::from_slice(&[0x23, 0x45]))); } - +/* #[test] fn encoded_post() { let n = NibbleSlice::::new(D); @@ -645,30 +502,82 @@ mod tests { assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x23, 0x45, 0x00])); assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x23, 0x45, 0x02])); } - +*/ #[test] fn from_encoded_pre() { let n = NibbleSlice::::new(D); - assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45])); - assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45])); - assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45])); - assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45])); + let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + let stored: ElasticArray36 = [0x01, 0x23, 0x45][..].into(); + assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); + assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); } #[test] + fn range_iter() { + let n = NibbleSlice::::new(D); + let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + for i in [ + vec![], + vec![0x00], + vec![0x01], + vec![0x00, 0x12], + vec![0x01, 0x23], + vec![0x00, 0x12, 0x34], + vec![0x01, 0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, None, &i.1[..]); + } + for i in [ + vec![], + vec![0x01], + vec![0x12], + vec![0x01, 0x23], + vec![0x12, 0x34], + vec![0x01, 0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(1), &i.1[..]); + } + for i in [ + vec![], + vec![0x02], + vec![0x23], + vec![0x02, 0x34], + vec![0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(2), &i.1[..]); + } + for i in [ + vec![], + vec![0x03], + vec![0x34], + vec![0x03, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(3), &i.1[..]); + } + + + } + + fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { + let n = if let Some(i) = mid { + n.mid(i) + } else { n }; + assert_eq!(&n.range_iter(nb).collect::>()[..], res); + } + +/* #[test] fn from_encoded_post() { let n = NibbleSlice::::new(D); assert_eq!((n, false), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x00])); assert_eq!((n, true), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x02])); assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x12, 0x34, 0x51])); assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x12, 0x34, 0x53])); - } + }*/ #[test] fn shared() { shared_inner::(); - shared_inner::(); } fn shared_inner() { let n = NibbleSlice::::new(D); @@ -688,7 +597,6 @@ mod tests { #[test] fn compare() { compare_inner::(); - compare_inner::(); } fn compare_inner() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index b03ac5bc..8bcf89dd 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -85,12 +85,11 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { use super::NibbleVec; - use nibbleslice::{NibblePreHalf, NibblePostHalf, NibbleOps}; + use nibbleslice::{NibblePreHalf, NibbleOps}; #[test] fn push_pop() { push_pop_inner::(); - push_pop_inner::(); } fn push_pop_inner() { let mut v = NibbleVec::::new(); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ede04952..9f13eb90 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -21,8 +21,8 @@ use super::DBValue; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -/// Partial node key type. -pub type NodeKey = ElasticArray36; +/// Partial node key type: offset and owned value of a nibbleslice. +pub type NodeKey = (usize, ElasticArray36); /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Debug, Clone)] diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 0cb60227..7742fe9d 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -38,6 +38,7 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} +pub type Partial<'a> = (Option, &'a[u8]); // TODO EMCH change node codec trait to use &mut self as input in order to run on internal buffer. // (not for decode actually!!; code seems fine to do that and new layout trait is ok too /// Trait for trie node encoding/decoding @@ -66,14 +67,14 @@ pub trait NodeCodec: Sized { fn empty_node() -> &'static [u8]; /// Returns an encoded leaf node - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec; + fn leaf_node(partial: Partial, value: &[u8]) -> Vec; /// Returns an encoded extension node - fn ext_node(partial: &[u8], child_ref: ChildReference) -> Vec; + fn ext_node(partial: impl Iterator, nb_nibble: usize, child_ref: ChildReference) -> Vec; /// Returns an encoded branch node. Takes an iterator yielding `ChildReference` and an optional value fn branch_node(children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; /// Returns an encoded branch node with a possible partial path. - fn branch_node_nibbled(partial: &[u8], children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; + fn branch_node_nibbled(partial: impl Iterator, nb_nibble: usize, children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 15135aef..2c59cdd6 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{Hasher, HashDBRef}; -use nibbleslice::{self, NibbleSlice, combine_encoded, NibbleOps}; +use hash_db::{Hasher, HashDBRef, Prefix}; +use nibbleslice::{self, NibbleSlice, NibbleOps}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; @@ -81,7 +81,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { - if !db.contains(root, L::N::EMPTY_ENCODED) { + if !db.contains(root, nibbleslice::EMPTY_ENCODED) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { Ok(TrieDB {db, root, hash_count: 0}) @@ -94,7 +94,7 @@ where /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, L::N::EMPTY_ENCODED) + .get(self.root, nibbleslice::EMPTY_ENCODED) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } @@ -103,8 +103,8 @@ where /// may require a database lookup. If `is_root_data` then this is root-data and /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. - fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: &[u8]) -> Result, TrieHash, CError> { - match (partial_key == L::N::EMPTY_ENCODED, L::C::try_decode_hash(node)) { + fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: Prefix) -> Result, TrieHash, CError> { + match (partial_key.0.is_empty() && partial_key.1.is_none(), L::C::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) @@ -146,7 +146,8 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: &'a ElasticArray36, + // TODO EMCH this field looks useless + partial_key: NibbleSlice<'a, L::N>, index: Option, } @@ -156,7 +157,7 @@ where L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, &self.partial_key) { + if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.left()) { match L::C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { @@ -175,7 +176,7 @@ where .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: &self.partial_key, + partial_key: self.partial_key, index: None, }) .finish(), @@ -247,7 +248,7 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: &Default::default(), + partial_key: NibbleSlice::new(&[]), index: None, }) .finish() @@ -288,6 +289,7 @@ impl Crumb { pub struct TrieDBIterator<'a, L: TrieLayOut> { db: &'a TrieDB<'a, L>, trail: Vec>, + // TODO EMCH replace by niblleVec!!! key_nibbles: Vec, } @@ -333,7 +335,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { self.key_nibbles.extend(slice.iter()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); - let data = self.db.get_raw_or_lookup(&*item, &key.encoded_leftmost(full_key_nibbles, false))?; + let data = self.db.get_raw_or_lookup(&*item, key.back(full_key_nibbles).left())?; data } else { self.descend(&node_data)?; @@ -358,7 +360,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { if let Some(ref child) = nodes[i as usize] { full_key_nibbles += 1; partial = partial.mid(1); - let child = self.db.get_raw_or_lookup(&*child, &key.encoded_leftmost(full_key_nibbles, false))?; + let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; child } else { return Ok(()) @@ -388,7 +390,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { if let Some(ref child) = nodes[i as usize] { full_key_nibbles += slice.len() + 1; partial = partial.mid(slice.len() + 1); - let child = self.db.get_raw_or_lookup(&*child, &key.encoded_leftmost(full_key_nibbles, false))?; + let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; child } else { return Ok(()) @@ -406,7 +408,8 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Descend into a payload. fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { - let node_data = &self.db.get_raw_or_lookup(d, &self.encoded_key())?; + let p_key = self.key(); + let node_data = &self.db.get_raw_or_lookup(d, self.encoded_key(&p_key))?; let node = L::C::decode(&node_data) .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) @@ -426,8 +429,8 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } } - // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead - /// The present key. + // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead + /// The present key. TODO not right it misses last fn key(&self) -> Vec { // collapse the key_nibbles down to bytes. let nibbles = &self.key_nibbles; @@ -441,18 +444,16 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { result } + // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead /// Encoded key for storage lookup - fn encoded_key(&self) -> ElasticArray36 { - let key = self.key(); + fn encoded_key<'b>(&self, key: &'b Vec) -> (&'b [u8], Option) { let slice = NibbleSlice::::new(&key); - let nb_padd = L::N::nb_padding(self.key_nibbles.len()); + let nb_padd = self.key_nibbles.len() % 2; if nb_padd > 0 { - // TODO EMCH costy new_composed when slice build just above??? - NibbleSlice::new_composed(&slice, - &NibbleSlice::new_padded(&self.key_nibbles[(self.key_nibbles.len() - 1)..], nb_padd)) - .encoded(false) + // TODO EMCH costy new_composed when slice build just above?? + (&key[..], Some(self.key_nibbles[self.key_nibbles.len() - 1] & (255 << 4))) } else { - slice.encoded(false) + (&key[..], None) } } } @@ -506,7 +507,8 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { return Some(Ok((self.key(), v.clone()))); }, (Status::At, &OwnedNode::Extension(_, ref d)) => { - IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, &self.encoded_key())) + let p_key = self.key(); + IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, self.encoded_key(&p_key))) }, (Status::At, &OwnedNode::Branch(_)) | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, @@ -518,9 +520,10 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { i => *self.key_nibbles.last_mut() .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, } + let p_key = self.key(); IterStep::Descend::, CError>(self.db.get_raw_or_lookup( &branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), - &self.encoded_key())) + self.encoded_key(&p_key))) }, (Status::AtChild(i), &OwnedNode::Branch(_)) | (Status::AtChild(i), &OwnedNode::NibbledBranch(_,_)) => { @@ -748,7 +751,7 @@ mod tests { } #[test] - fn get_len() { + fn get_len_with_ext() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 0e2ff4a6..a8437c0a 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -20,9 +20,9 @@ use super::node::Node as EncodedNode; use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; -use hash_db::{HashDB, Hasher}; -use nibbleslice::{self, NibbleSlice, combine_encoded, NibbleOps}; - +use hash_db::{HashDB, Hasher, Prefix}; +use nibbleslice::{self, NibbleSlice, NibbleOps}; +use elastic_array::ElasticArray36; use ::core_::marker::PhantomData; use ::core_::mem; use ::core_::ops::Index; @@ -69,33 +69,110 @@ fn empty_children() -> Box<[Option>; 16]> { None, None, None, None, None, None, None, None, ]) } - -struct Partial<'key, N: NibbleOps> { +// TODO rem +#[derive(Clone)] +struct PartialKey<'key, N: NibbleOps> { key: NibbleSlice<'key, N>, - split: usize, } -impl<'key, N: NibbleOps> Partial<'key, N> { - fn new(key: NibbleSlice) -> Partial { - Partial { +impl<'key, N: NibbleOps> PartialKey<'key, N> { + fn new(key: NibbleSlice) -> PartialKey { + PartialKey { key, - split: 0, } } fn advance(&mut self, by: usize) { - self.split += by; + self.key = self.key.mid(by) } fn mid(&self) -> NibbleSlice<'key, N> { - self.key.mid(self.split) + self.key + } + + fn encoded_prefix(&self) -> Prefix { + self.key.left() + } + fn encoded_prefix_owned(&self) -> (ElasticArray36, Option) { + let (a, b) = self.encoded_prefix(); + (a.into(), b) + } + +} + +struct PartialKeyMut<'key, N: NibbleOps> { + key: &'key mut ElasticArray36, + start: usize, + pad: usize, + marker: PhantomData, +} + +impl<'a, N: NibbleOps> PartialKeyMut<'a, N> { + fn new(key: &mut ElasticArray36) -> PartialKeyMut { + Self::new_advance(key, 0) + } + fn new_advance(key: &mut ElasticArray36, start: usize) -> PartialKeyMut { + PartialKeyMut { + key, + start, + pad: 0, + marker: Default::default(), + } + } + + // TODO EMCH unused?? can bybe simplify a bit even up to keyvec (there is quite some + // unused in fact only use into recursive encding). + fn advance(&mut self, by: usize) { + self.start += by + } + + /// ret slice and nb of padding byte + fn mid(&self) -> (NibbleSlice, usize) { + let padd_end = self.pad; + (NibbleSlice::new_offset(&self.key[..], self.start), padd_end) + } + + fn encoded_prefix_owned(&self) -> (ElasticArray36, Option) { + let pr = self.mid().0; + let (a, b) = pr.left(); + (a.into(), b) } + /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. + pub fn push(&mut self, nibble: u8) { + // TODO EMCH move to N + let nibble = nibble & 0x0F; + + if self.pad == 0 { + self.key.push(nibble << 4); + self.pad = N::NIBBLE_PER_BYTE - 1; + } else { + *self.key.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + self.pad -= 1; + } + } + + /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. + pub fn pop(&mut self) -> Option { + if self.key.len() * N::NIBBLE_PER_BYTE <= self.start { + return None; + } - fn encoded_prefix(&self) -> NodeKey { - self.key.encoded_leftmost(self.split, false) + let byte = self.key.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if self.pad == 0 { + // TODO EMCH rem pop / push + self.key.push(byte & 0xF0); + self.pad = 1; + byte & 0x0F + } else { + self.pad += 1; + byte >> 4 + }; + + Some(nibble) } } + /// Node types in the Trie. #[derive(Debug)] enum Node { @@ -160,10 +237,10 @@ where match C::decode(data).unwrap_or(EncodedNode::Empty) { EncodedNode::Empty => Node::Empty, - EncodedNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)), + EncodedNode::Leaf(k, v) => Node::Leaf(k.into(), DBValue::from_slice(&v)), EncodedNode::Extension(key, cb) => { Node::Extension( - key.encoded(false), + key.into(), Self::inline_or_hash::(cb, db, storage)) }, EncodedNode::Branch(encoded_children, val) => { @@ -172,47 +249,67 @@ where }, EncodedNode::NibbledBranch(k, encoded_children, val) => { let children = dec_children(&encoded_children, storage); - Node::NibbledBranch(k.encoded(false), children, val.map(DBValue::from_slice)) + Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) }, } } - // TODO: parallelize + // TODO: parallelize TODO EMCH does not need to consume self: change that fn into_encoded(self, mut child_cb: F) -> Vec where N: NibbleOps, C: NodeCodec, - F: FnMut(NodeHandle, &NodeKey) -> ChildReference, + F: FnMut(NodeHandle, &mut PartialKeyMut) -> ChildReference, H: Hasher, { match self { Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => C::leaf_node(&partial, &value), - Node::Extension(partial, child) => C::ext_node(&partial, child_cb(child, &partial)), + Node::Leaf(partial, value) => C::leaf_node(nibbleslice::into_part(&partial), &value), + Node::Extension(partial, child) => { + // warning we know that partial does not use pop backward from this point, child_cb using pop + // here will break things TODO pop limited version of TrieDBMut?? + let c = child_cb(child, &mut PartialKeyMut::new_advance(&mut partial.1.clone(), partial.0)); + let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + C::ext_node( + pr.iter(), + pr.len(), + c, + ) + }, Node::Branch(mut children, value) => { C::branch_node( // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() .map(Option::take) .enumerate() - .map(|(i, maybe_child)| - maybe_child.map(|child| child_cb(child, &NibbleSlice::::new_offset(&[i as u8], 1).encoded(false))) - ), + .map(|(i, maybe_child)| { + let mut ea = ElasticArray36::new(); + let mut pkm = PartialKeyMut::new(&mut ea); + pkm.push(i as u8); + maybe_child.map(|child|child_cb(child, &mut pkm)) + }), value.as_ref().map(|v|&v[..]) ) }, Node::NibbledBranch(partial, mut children, value) => { + let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); C::branch_node_nibbled( - &partial, + pr.iter(), + pr.len(), // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() .map(Option::take) .enumerate() .map(|(i, maybe_child)|{ - let branch_ix = [i as u8]; - // costy - let nibble = NibbleSlice::::new_composed(&NibbleSlice::from_encoded(&partial).0, &NibbleSlice::new_offset(&branch_ix, 1)); - maybe_child.map(|child| child_cb(child, &nibble.encoded(false))) + //let branch_ix = [i as u8]; + maybe_child.map(|child| { + // TODO EMCH this clone should be avoid by having a lower limit to pkm + // and reseting each time (also good to secure pop!!) + let mut basis = partial.1.clone(); + let mut pkm = PartialKeyMut::new_advance(&mut basis, partial.0); + pkm.push(i as u8); + child_cb(child, &mut pkm) + }) }), value.as_ref().map(|v|&v[..]) ) @@ -356,7 +453,7 @@ where db: &'a mut HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, - death_row: HashSet<(TrieHash, NodeKey)>, + death_row: HashSet<(TrieHash, (ElasticArray36, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, @@ -384,7 +481,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { - if !db.contains(root, L::N::EMPTY_ENCODED) { + if !db.contains(root, nibbleslice::EMPTY_ENCODED) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -409,7 +506,7 @@ where } // cache a node by hash - fn cache(&mut self, hash: TrieHash, key: &[u8]) -> Result, CError> { + fn cache(&mut self, hash: TrieHash, key: Prefix) -> Result, CError> { let node_encoded = self.db.get(&hash, key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; let node = Node::from_encoded::( &node_encoded, @@ -421,8 +518,8 @@ where // inspect a node, choosing either to replace, restore, or delete it. // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored>, key: &mut Partial, inspector: F) -> Result>, bool)>, TrieHash, CError> - where F: FnOnce(&mut Self, Node>, &mut Partial) -> Result>, TrieHash, CError> { + fn inspect(&mut self, stored: Stored>, key: &mut PartialKey, inspector: F) -> Result>, bool)>, TrieHash, CError> + where F: FnOnce(&mut Self, Node>, &mut PartialKey) -> Result>, TrieHash, CError> { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -432,11 +529,11 @@ where Stored::Cached(node, hash) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::Cached(node, hash), false)), Action::Replace(node) => { - self.death_row.insert((hash, key.encoded_prefix())); + self.death_row.insert((hash, key.encoded_prefix_owned())); Some((Stored::New(node), true)) } Action::Delete => { - self.death_row.insert((hash, key.encoded_prefix())); + self.death_row.insert((hash, key.encoded_prefix_owned())); None } }, @@ -458,14 +555,14 @@ where NodeHandle::InMemory(ref handle) => match self.storage[handle] { Node::Empty => return Ok(None), Node::Leaf(ref key, ref value) => { - if NibbleSlice::from_encoded(key).0 == partial { + if NibbleSlice::from_stored(key) == partial { return Ok(Some(DBValue::from_slice(value))); } else { return Ok(None); } }, Node::Extension(ref slice, ref child) => { - let slice = NibbleSlice::from_encoded(slice).0; + let slice = NibbleSlice::from_stored(slice); if partial.starts_with(&slice) { (slice.len(), child) } else { @@ -484,7 +581,7 @@ where } }, Node::NibbledBranch(ref slice, ref children, ref value) => { - let slice = NibbleSlice::from_encoded(slice).0; + let slice = NibbleSlice::from_stored(slice); if partial.is_empty() { return Ok(value.as_ref().map(|v| DBValue::from_slice(v))); } else if partial.starts_with(&slice) { @@ -506,10 +603,10 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { + fn insert_at(&mut self, handle: NodeHandle>, key: &mut PartialKey, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, - NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, + NodeHandle::Hash(h) => self.cache(h, key.encoded_prefix())?, }; let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { @@ -520,14 +617,14 @@ where } /// the insertion inspector. - fn insert_inspector(&mut self, node: Node>, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn insert_inspector(&mut self, node: Node>, key: &mut PartialKey, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); Ok(match node { Node::Empty => { trace!(target: "trie", "empty: COMPOSE"); - InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) + InsertAction::Replace(Node::Leaf(partial.to_stored(), value)) }, Node::Branch(mut children, stored_value) => { debug_assert!(L::USE_EXTENSION); @@ -555,7 +652,7 @@ where } } else { // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().to_stored(), value))); children[idx] = Some(leaf.into()); } @@ -565,12 +662,12 @@ where Node::NibbledBranch(encoded, mut children, stored_value) => { debug_assert!(!L::USE_EXTENSION); trace!(target: "trie", "branch: ROUTE,AUGMENT"); - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let existing_key = NibbleSlice::from_stored(&encoded); let cp = partial.common_prefix(&existing_key); if cp == existing_key.len() && cp == partial.len() { let unchanged = stored_value.as_ref() == Some(&value); - let branch = Node::NibbledBranch(existing_key.encoded(false), children, Some(value)); + let branch = Node::NibbledBranch(existing_key.to_stored(), children, Some(value)); *old_val = stored_value; match unchanged { @@ -580,7 +677,7 @@ where } else if cp < existing_key.len() { // insert a branch value in between trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); - let low = Node::NibbledBranch(existing_key.mid(cp + 1).encoded(false), children, stored_value); + let low = Node::NibbledBranch(existing_key.mid(cp + 1).to_stored(), children, stored_value); let ix = existing_key.at(cp); let mut children = empty_children(); let alloc_storage = self.storage.alloc(Stored::New(low)); @@ -590,18 +687,18 @@ where if partial.len() - cp == 0 { InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded_leftmost(cp,false), + existing_key.to_stored_range(cp), children, Some(value), ) ) } else { let ix = partial.at(cp); - let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).encoded(true), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).to_stored(), value))); children[ix as usize] = Some(leaf.into()); InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded_leftmost(cp,false), + existing_key.to_stored_range(cp), children, None, ) @@ -620,23 +717,22 @@ where children[idx] = Some(new_child.into()); if !changed { // the new node we composed didn't change. that means our branch is untouched too. - return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.encoded(false), children, stored_value))); + return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.to_stored(), children, stored_value))); } } else { // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().to_stored(), value))); children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::NibbledBranch( - existing_key.encoded(false), + existing_key.to_stored(), children, stored_value, )) - } }, Node::Leaf(encoded, stored_value) => { - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let existing_key = NibbleSlice::from_stored(&encoded); let cp = partial.common_prefix(&existing_key); if cp == existing_key.len() && cp == partial.len() { trace!(target: "trie", "equivalent-leaf: REPLACE"); @@ -660,13 +756,13 @@ where Node::Branch(children, Some(stored_value)) } else { let idx = existing_key.at(cp) as usize; - let new_leaf = Node::Leaf(existing_key.mid(cp + 1).encoded(true), stored_value); + let new_leaf = Node::Leaf(existing_key.mid(cp + 1).to_stored(), stored_value); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); if L::USE_EXTENSION { Node::Branch(children, None) } else { - Node::NibbledBranch(partial.encoded_leftmost(cp, false), children, None) + Node::NibbledBranch(partial.to_stored_range(cp), children, None) } }; @@ -678,7 +774,7 @@ where // fully-shared prefix for an extension. // make a stub branch - let branch = Node::NibbledBranch(existing_key.encoded(false), empty_children(), Some(stored_value)); + let branch = Node::NibbledBranch(existing_key.to_stored(), empty_children(), Some(stored_value)); // augment the new branch. let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -697,30 +793,29 @@ where // always replace since we took a leaf and made an extension. let branch_handle = self.storage.alloc(Stored::New(branch)).into(); - InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) + InsertAction::Replace(Node::Extension(existing_key.to_stored(), branch_handle)) } else { debug_assert!(L::USE_EXTENSION); trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared prefix for an extension. // start by making a leaf. - let low = Node::Leaf(existing_key.mid(cp).encoded(true), stored_value); + let low = Node::Leaf(existing_key.mid(cp).to_stored(), stored_value); // augment it. this will result in the Leaf -> cp == 0 routine, // which creates a branch. key.advance(cp); let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); - // make an extension using it. this is a replacement. InsertAction::Replace(Node::Extension( - existing_key.encoded_leftmost(cp, false), + existing_key.to_stored_range(cp), self.storage.alloc(Stored::New(augmented_low)).into() )) } }, Node::Extension(encoded, child_branch) => { debug_assert!(L::USE_EXTENSION); - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let existing_key = NibbleSlice::from_stored(&encoded); let cp = partial.common_prefix(&existing_key); if cp == 0 { trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); @@ -736,7 +831,7 @@ where Some(child_branch) } else { // more work required after branching. - let ext = Node::Extension(existing_key.mid(1).encoded(false), child_branch); + let ext = Node::Extension(existing_key.mid(1).to_stored(), child_branch); Some(self.storage.alloc(Stored::New(ext)).into()) }; @@ -751,7 +846,7 @@ where // insert into the child node. key.advance(cp); let (new_child, changed) = self.insert_at(child_branch, key, value, old_val)?; - let new_ext = Node::Extension(existing_key.encoded(false), new_child.into()); + let new_ext = Node::Extension(existing_key.to_stored(), new_child.into()); // if the child branch wasn't changed, meaning this extension remains the same. match changed { @@ -762,7 +857,7 @@ where trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared. - let low = Node::Extension(existing_key.mid(cp).encoded(false), child_branch); + let low = Node::Extension(existing_key.mid(cp).to_stored(), child_branch); // augment the extension. this will take the cp == 0 path, creating a branch. key.advance(cp); let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); @@ -770,7 +865,7 @@ where // always replace, since this extension is not the one we started with. // this is known because the partial key is only the common prefix. InsertAction::Replace(Node::Extension( - existing_key.encoded_leftmost(cp, false), + existing_key.to_stored_range(cp), self.storage.alloc(Stored::New(augmented_low)).into() )) } @@ -779,11 +874,11 @@ where } /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle>, key: &mut Partial, old_val: &mut Option) -> Result, TrieHash, CError> { + fn remove_at(&mut self, handle: NodeHandle>, key: &mut PartialKey, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &key.encoded_prefix())?; + let handle = self.cache(h, key.encoded_prefix())?; self.storage.destroy(handle) } }; @@ -794,7 +889,7 @@ where } /// the removal inspector - fn remove_inspector(&mut self, node: Node>, key: &mut Partial, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn remove_inspector(&mut self, node: Node>, key: &mut PartialKey, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.mid(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, @@ -803,18 +898,18 @@ where (Node::Branch(children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::Branch(children, None), key.encoded_prefix())?) + Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) }, (Node::NibbledBranch(n, children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.encoded_prefix())?) + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone())?) }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; if let Some(child) = children[idx].take() { trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); - let prefix = key.encoded_prefix(); + let prefix = key.clone(); key.advance(1); match self.remove_at(child, key, old_val)? { Some((new, changed)) => { @@ -841,7 +936,7 @@ where }, (Node::NibbledBranch(encoded, mut children, value), false) => { let (cp, existing_len) = { - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; if cp == existing_len && cp == partial.len() { @@ -850,7 +945,7 @@ where if let Some(val) = value { *old_val = Some(val); - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.encoded_prefix()); + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone()); Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) @@ -864,7 +959,7 @@ where if let Some(child) = children[idx].take() { trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); - let prefix = key.encoded_prefix(); + let prefix = key.clone(); key.advance(cp + 1); match self.remove_at(child, key, old_val)? { Some((new, changed)) => { @@ -891,25 +986,25 @@ where } }, (Node::Leaf(encoded, value), _) => { - if NibbleSlice::from_encoded(&encoded).0 == partial { + if NibbleSlice::from_stored(&encoded) == partial { // this is the node we were looking for. Let's delete it. *old_val = Some(value); Action::Delete } else { // leaf the node alone. - trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::::from_encoded(&encoded).0); + trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::::from_stored(&encoded)); Action::Restore(Node::Leaf(encoded, value)) } }, (Node::Extension(encoded, child_branch), _) => { let (cp, existing_len) = { - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; if cp == existing_len { // try to remove from the child branch. trace!(target: "trie", "removing from extension child, partial={:?}", partial); - let prefix = key.encoded_prefix(); + let prefix = key.clone(); key.advance(cp); match self.remove_at(child_branch, key, old_val)? { Some((new_child, changed)) => { @@ -942,7 +1037,7 @@ where /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node>, key: NodeKey) -> Result>, TrieHash, CError> { + fn fix(&mut self, node: Node>, key: PartialKey) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -968,7 +1063,7 @@ where (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. make an extension. - let new_partial = NibbleSlice::::new_offset(&[a], 1).encoded(false); + let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); self.fix(new_node, key) @@ -976,7 +1071,7 @@ where (UsedIndex::None, Some(value)) => { // make a leaf. trace!(target: "trie", "fixing: branch -> leaf"); - Ok(Node::Leaf(NibbleSlice::::new(&[]).encoded(true), value)) + Ok(Node::Leaf(NibbleSlice::::new(&[]).to_stored(), value)) } (_, value) => { // all is well. @@ -1010,50 +1105,35 @@ where (UsedIndex::One(a), None) => { // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + let mut key = key; let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO EMCH optimize this concat (new_partial_tmp may be calc again afterward) - let new_partial_tmp = NibbleSlice::::new_composed( - &NibbleSlice::from_encoded(&enc_nibble).0, - &NibbleSlice::new_offset(&[a], 1) - ).encoded(false); - - let handle = self.cache(h, &combine_encoded::(&key, &new_partial_tmp))?; + key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0 + 1); + key.advance(1); + let handle = self.cache(h, key.encoded_prefix())?; self.storage.destroy(handle) } }; let child_node = match stored { Stored::New(node) => node, Stored::Cached(node, hash) => { - self.death_row.insert((hash, key.clone())); + self.death_row.insert((hash, key.encoded_prefix_owned())); node }, }; match child_node { Node::Leaf(sub_partial, value) => { - let ix = [a]; - // warning cannot compose more than one time - let new_partial_tmp = NibbleSlice::::new_composed( - &NibbleSlice::from_encoded(&enc_nibble).0, - &NibbleSlice::new_offset(&ix, 1) - ).encoded(false); - let new_partial = NibbleSlice::::new_composed( - &NibbleSlice::from_encoded(&new_partial_tmp).0, - &NibbleSlice::from_encoded(&sub_partial).0); - Ok(Node::Leaf(new_partial.encoded(false), value)) + key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); + key.advance(1); + key.advance((sub_partial.1.len() * L::N::NIBBLE_PER_BYTE) - sub_partial.0); + Ok(Node::Leaf(key.mid().to_stored(), value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { - let ix = [a]; - // warning cannot compose more than one time - let new_partial_tmp = NibbleSlice::::new_composed( - &NibbleSlice::from_encoded(&enc_nibble).0, - &NibbleSlice::new_offset(&ix, 1) - ).encoded(false); - let new_partial = NibbleSlice::::new_composed( - &NibbleSlice::from_encoded(&new_partial_tmp).0, - &NibbleSlice::from_encoded(&sub_partial).0); - Ok(Node::NibbledBranch(new_partial.encoded(false), ch_children, ch_value)) + key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); + key.advance(1); + key.advance((sub_partial.1.len() * L::N::NIBBLE_PER_BYTE) - sub_partial.0); + Ok(Node::NibbledBranch(key.mid().to_stored(), ch_children, ch_value)) }, _ => unreachable!(), } @@ -1061,9 +1141,7 @@ where (UsedIndex::None, Some(value)) => { // make a leaf. trace!(target: "trie", "fixing: branch -> leaf"); - let partial = NibbleSlice::::from_encoded(&enc_nibble).0; - // TODO encoded switch can be almost costless instead of this enc/dec - Ok(Node::Leaf(partial.encoded(true), value)) + Ok(Node::Leaf(enc_nibble, value)) }, (_, value) => { // all is well. @@ -1076,7 +1154,9 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &combine_encoded::(&key, &partial))?; + let mut kc = key.clone(); + kc.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); + let handle = self.cache(h, kc.encoded_prefix())?; self.storage.destroy(handle) } }; @@ -1091,28 +1171,25 @@ where // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key.clone())); + self.death_row.insert((hash, key.encoded_prefix_owned())); } - let partial = NibbleSlice::::from_encoded(&partial).0; - let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; - - let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); - trace!(target: "trie", "fixing: extension combination. new_partial={:?}", new_partial); - let new_partial = new_partial.encoded(false); - self.fix(Node::Extension(new_partial, sub_child), key) + let mut kc = key.clone(); + kc.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); + kc.advance(sub_partial.1.len() * L::N::NIBBLE_PER_BYTE - sub_partial.0); + trace!(target: "trie", "fixing: extension combination. new_partial={:?}", kc.mid()); + self.fix(Node::Extension(kc.mid().to_stored(), sub_child), key) } Node::Leaf(sub_partial, value) => { + let mut key = key; // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key)); + self.death_row.insert((hash, key.encoded_prefix_owned())); } - let partial = NibbleSlice::::from_encoded(&partial).0; - let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; - - let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); - trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", new_partial); - Ok(Node::Leaf(new_partial.encoded(true), value)) + key.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); + key.advance(sub_partial.1.len() * L::N::NIBBLE_PER_BYTE - sub_partial.0); + trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", key.mid()); + Ok(Node::Leaf(key.mid().to_stored(), value)) } child_node => { trace!(target: "trie", "fixing: restoring extension"); @@ -1140,7 +1217,7 @@ where // always kill all the nodes on death row. trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for (hash, prefix) in self.death_row.drain() { - self.db.remove(&hash, &prefix); + self.db.remove(&hash, (&prefix.0[..], prefix.1)); } let handle = match self.root_handle() { @@ -1154,7 +1231,7 @@ where self.commit_child(child, k) }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(L::N::EMPTY_ENCODED, &encoded_root[..]); + *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -1172,7 +1249,7 @@ where /// case where we can fit the actual data in the `Hasher`s output type, we /// store the data inline. This function is used as the callback to the /// `into_encoded` method of `Node`. - fn commit_child(&mut self, handle: NodeHandle>, prefix: &NodeKey) -> ChildReference> { + fn commit_child(&mut self, handle: NodeHandle>, prefix: &mut PartialKeyMut) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), NodeHandle::InMemory(storage_handle) => { @@ -1180,14 +1257,18 @@ where Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { let encoded = { - let commit_child = |node_handle, partial: &NodeKey| { - let combined = combine_encoded::(&prefix, partial); - self.commit_child(node_handle, &combined) + let commit_child = |node_handle, partial: &mut PartialKeyMut| { + let (sl, pad) = partial.mid(); + // TODO EMCH there is an optim for aligned case : directly cat in elasti array + for n in sl.range_iter(sl.len() - pad) { + prefix.push(n); + } + self.commit_child(node_handle, prefix) }; node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; if encoded.len() >= L::H::LENGTH { - let hash = self.db.insert(&prefix, &encoded[..]); + let hash = self.db.insert(prefix.mid().0.left(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { @@ -1247,7 +1328,7 @@ where let root_handle = self.root_handle(); let (new_handle, changed) = self.insert_at( root_handle, - &mut Partial::new(NibbleSlice::new(key)), + &mut PartialKey::new(NibbleSlice::new(key)), DBValue::from_slice(value), &mut old_val, )?; @@ -1262,7 +1343,7 @@ where trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); - let mut key = Partial::new(NibbleSlice::new(key)); + let mut key = PartialKey::new(NibbleSlice::new(key)); let mut old_val = None; match self.remove_at(root_handle, &mut key, &mut old_val)? { From bc1ae2b43a556e3333cceeaae7bb1625e7bfc845 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 18 Apr 2019 23:16:49 +0200 Subject: [PATCH 030/120] fix stack of prefix on commit --- memory-db/src/lib.rs | 2 + trie-db/src/nibbleslice.rs | 2 +- trie-db/src/triedb.rs | 4 +- trie-db/src/triedbmut.rs | 102 ++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index fece535a..eac74ff7 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -410,6 +410,7 @@ where return Some(self.null_node_data.clone()); } + println!("get : {:x?} {:x?}", key, prefix); let key = KF::key(key, prefix); let r = match self.data.get(&key) { Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), @@ -435,6 +436,7 @@ where return; } + println!("emp : {:x?} {:x?}", key, prefix); let key = KF::key(&key, prefix); match self.data.entry(key) { Entry::Occupied(mut entry) => { diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 0c29fc9e..93a33fd3 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -305,7 +305,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn left(&'a self) -> (&'a [u8], Option) { let split = self.offset / 2; if self.len() % 2 == 1 { - (&self.data[..split], Some(self.data[split] & (255 >> 4))) + (&self.data[..split], Some(self.data[split] & (255 << 4))) } else { (&self.data[..split], None) } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 2c59cdd6..d8e3f711 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -757,12 +757,12 @@ mod tests { { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); - t.insert(b"B", b"ABCBA").unwrap(); + t.insert(b"B", b"ABCBAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); - assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5)); + assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(32)); assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index a8437c0a..beca1b04 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -70,7 +70,7 @@ fn empty_children() -> Box<[Option>; 16]> { ]) } // TODO rem -#[derive(Clone)] +#[derive(Clone, Debug)] struct PartialKey<'key, N: NibbleOps> { key: NibbleSlice<'key, N>, } @@ -100,43 +100,34 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { } -struct PartialKeyMut<'key, N: NibbleOps> { - key: &'key mut ElasticArray36, - start: usize, +#[derive(Debug)] +struct PartialKeyMut { + key: ElasticArray36, pad: usize, marker: PhantomData, } - -impl<'a, N: NibbleOps> PartialKeyMut<'a, N> { - fn new(key: &mut ElasticArray36) -> PartialKeyMut { - Self::new_advance(key, 0) - } - fn new_advance(key: &mut ElasticArray36, start: usize) -> PartialKeyMut { +// TODO EMCH almost replaceble with nibblevec +impl PartialKeyMut { + fn new() -> PartialKeyMut { PartialKeyMut { - key, - start, + key: ElasticArray36::new(), pad: 0, marker: Default::default(), } - } - // TODO EMCH unused?? can bybe simplify a bit even up to keyvec (there is quite some - // unused in fact only use into recursive encding). - fn advance(&mut self, by: usize) { - self.start += by } + // TODO EMCH better truncate + fn truncate(&mut self, mov: usize) { + for _ in 0..mov { + self.pop(); + } + } /// ret slice and nb of padding byte - fn mid(&self) -> (NibbleSlice, usize) { - let padd_end = self.pad; - (NibbleSlice::new_offset(&self.key[..], self.start), padd_end) + fn mid(&self) -> NibbleSlice { + (NibbleSlice::new_offset(&self.key[..], self.key.len() * N::NIBBLE_PER_BYTE - self.pad)) } - fn encoded_prefix_owned(&self) -> (ElasticArray36, Option) { - let pr = self.mid().0; - let (a, b) = pr.left(); - (a.into(), b) - } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. pub fn push(&mut self, nibble: u8) { // TODO EMCH move to N @@ -153,10 +144,11 @@ impl<'a, N: NibbleOps> PartialKeyMut<'a, N> { /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { - if self.key.len() * N::NIBBLE_PER_BYTE <= self.start { + let len = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; + if len == 0 { return None; } - +println!("tr {:x?}", self); let byte = self.key.pop().expect("len != 0; inner has last elem; qed"); let nibble = if self.pad == 0 { // TODO EMCH rem pop / push @@ -164,10 +156,11 @@ impl<'a, N: NibbleOps> PartialKeyMut<'a, N> { self.pad = 1; byte & 0x0F } else { - self.pad += 1; + self.pad -= 1; byte >> 4 }; +println!("tr {:x?}", self); Some(nibble) } } @@ -259,7 +252,7 @@ where where N: NibbleOps, C: NodeCodec, - F: FnMut(NodeHandle, &mut PartialKeyMut) -> ChildReference, + F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { @@ -268,8 +261,8 @@ where Node::Extension(partial, child) => { // warning we know that partial does not use pop backward from this point, child_cb using pop // here will break things TODO pop limited version of TrieDBMut?? - let c = child_cb(child, &mut PartialKeyMut::new_advance(&mut partial.1.clone(), partial.0)); let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let c = child_cb(child, Some(&pr), None); C::ext_node( pr.iter(), pr.len(), @@ -283,10 +276,7 @@ where .map(Option::take) .enumerate() .map(|(i, maybe_child)| { - let mut ea = ElasticArray36::new(); - let mut pkm = PartialKeyMut::new(&mut ea); - pkm.push(i as u8); - maybe_child.map(|child|child_cb(child, &mut pkm)) + maybe_child.map(|child|child_cb(child, None, Some(i as u8))) }), value.as_ref().map(|v|&v[..]) ) @@ -305,10 +295,8 @@ where maybe_child.map(|child| { // TODO EMCH this clone should be avoid by having a lower limit to pkm // and reseting each time (also good to secure pop!!) - let mut basis = partial.1.clone(); - let mut pkm = PartialKeyMut::new_advance(&mut basis, partial.0); - pkm.push(i as u8); - child_cb(child, &mut pkm) + let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + child_cb(child, Some(&pr), Some(i as u8)) }) }), value.as_ref().map(|v|&v[..]) @@ -1227,8 +1215,12 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, k| { - self.commit_child(child, k) + let mut k = PartialKeyMut::new(); + let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { + let mov = concat_key(&mut k, o_sl, o_ix); + let cr = self.commit_child(child, &mut k); + k.truncate(mov); + cr }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); @@ -1257,18 +1249,16 @@ where Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { let encoded = { - let commit_child = |node_handle, partial: &mut PartialKeyMut| { - let (sl, pad) = partial.mid(); - // TODO EMCH there is an optim for aligned case : directly cat in elasti array - for n in sl.range_iter(sl.len() - pad) { - prefix.push(n); - } - self.commit_child(node_handle, prefix) + let commit_child = |node_handle, o_sl: Option<&NibbleSlice>, o_ix: Option| { + let mov = concat_key(prefix, o_sl, o_ix); + let cr = self.commit_child(node_handle, prefix); + prefix.truncate(mov); + cr }; node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; if encoded.len() >= L::H::LENGTH { - let hash = self.db.insert(prefix.mid().0.left(), &encoded[..]); + let hash = self.db.insert(prefix.mid().left(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { @@ -1293,6 +1283,22 @@ where } } +fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { + let mut res = 0; + if let Some(sl) = o_sl { + // TODO EMCH align optim + for n in sl.iter() { + prefix.push(n); + } + res += sl.len(); + } + if let Some(ix) = o_ix { + prefix.push(ix); + res += 1; + } + res +} + impl<'a, L> TrieMut for TrieDBMut<'a, L> where L: TrieLayOut, From 4ddb1ebf8d5cb74e47320cb74e3a9ec9d28103e8 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 19 Apr 2019 09:43:13 +0200 Subject: [PATCH 031/120] fix into_part method --- trie-db/src/nibbleslice.rs | 29 +++++++++++++++---- trie-db/src/triedbmut.rs | 58 ++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 93a33fd3..0f2e1b5b 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -358,17 +358,34 @@ impl<'a, N: NibbleOps> Into for NibbleSlice<'a, N> { } } -// TODO rename or enhanch +// TODO EMCH rename or enhanch + generalize (in NibbleOps) + use in a single place (cast from +// leaf) pub fn into_part(inp: &NodeKey) -> Partial { - if inp.0 / 2 == 1 { - (Some(inp.1[0]), - &inp.1[1..]) + let start = inp.0 / 2; + if inp.0 % 2 > 0 { + (Some(inp.1[start]), + &inp.1[start + 1..]) } else { - (None, &inp.1[..]) + (None, &inp.1[start..]) } } - +#[test] +fn into_part_test() { + let v = [ + ((0, [0x12, 0x34][..].into()), + (None, &vec![0x12, 0x34][..])), + ((1, [0x12, 0x34][..].into()), + (Some(0x12), &vec![0x34][..])), + ((2, [0x12, 0x34][..].into()), + (None, &vec![0x34][..])), + ((3, [0x12, 0x34][..].into()), + (Some(0x34), &vec![][..])), + ]; + for nk in v.iter() { + assert_eq!(into_part(&nk.0), nk.1); + } +} /* // TODO EMCH use in prev into fn impl<'a, N: NibbleOps> Into<(Option, &'a[u8])> for NibbleSlice<'a, N> { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index beca1b04..d988028d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -116,12 +116,12 @@ impl PartialKeyMut { } } - // TODO EMCH better truncate - fn truncate(&mut self, mov: usize) { - for _ in 0..mov { - self.pop(); - } - } + // TODO EMCH better truncate + fn truncate(&mut self, mov: usize) { + for _ in 0..mov { + self.pop(); + } + } /// ret slice and nb of padding byte fn mid(&self) -> NibbleSlice { @@ -144,11 +144,10 @@ impl PartialKeyMut { /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { - let len = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; + let len = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; if len == 0 { return None; } -println!("tr {:x?}", self); let byte = self.key.pop().expect("len != 0; inner has last elem; qed"); let nibble = if self.pad == 0 { // TODO EMCH rem pop / push @@ -160,7 +159,6 @@ println!("tr {:x?}", self); byte >> 4 }; -println!("tr {:x?}", self); Some(nibble) } } @@ -295,7 +293,7 @@ where maybe_child.map(|child| { // TODO EMCH this clone should be avoid by having a lower limit to pkm // and reseting each time (also good to secure pop!!) - let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); child_cb(child, Some(&pr), Some(i as u8)) }) }), @@ -1215,12 +1213,12 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let mut k = PartialKeyMut::new(); + let mut k = PartialKeyMut::new(); let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { - let mov = concat_key(&mut k, o_sl, o_ix); + let mov = concat_key(&mut k, o_sl, o_ix); let cr = self.commit_child(child, &mut k); - k.truncate(mov); - cr + k.truncate(mov); + cr }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); @@ -1250,10 +1248,10 @@ where Stored::New(node) => { let encoded = { let commit_child = |node_handle, o_sl: Option<&NibbleSlice>, o_ix: Option| { - let mov = concat_key(prefix, o_sl, o_ix); + let mov = concat_key(prefix, o_sl, o_ix); let cr = self.commit_child(node_handle, prefix); - prefix.truncate(mov); - cr + prefix.truncate(mov); + cr }; node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; @@ -1284,19 +1282,19 @@ where } fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { - let mut res = 0; - if let Some(sl) = o_sl { - // TODO EMCH align optim - for n in sl.iter() { - prefix.push(n); - } - res += sl.len(); - } - if let Some(ix) = o_ix { - prefix.push(ix); - res += 1; - } - res + let mut res = 0; + if let Some(sl) = o_sl { + // TODO EMCH align optim + for n in sl.iter() { + prefix.push(n); + } + res += sl.len(); + } + if let Some(ix) = o_ix { + prefix.push(ix); + res += 1; + } + res } impl<'a, L> TrieMut for TrieDBMut<'a, L> From da335d43ea140043a59834d7ae023bd853cd5f58 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 19 Apr 2019 12:18:02 +0200 Subject: [PATCH 032/120] fix fuzz1 --- test-support/reference-trie/src/lib.rs | 17 +++++++++------ trie-db/src/iter_build.rs | 4 ++-- trie-db/src/nibbleslice.rs | 23 ++++++++++++++++++-- trie-db/src/triedb.rs | 15 ++++++------- trie-db/src/triedbmut.rs | 30 +++++++++++++++++--------- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index f7a8c556..1b017fb7 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -522,7 +522,9 @@ fn partial_to_key_it>(partial: I, nibble_co assert!(nibble_count < over as usize); let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); output.push(offset + nibble_count as u8); + println!("be : {:x?}", output); output.extend(partial); + println!("ae : {:x?}", output); output } @@ -641,6 +643,7 @@ impl NodeCodec for ReferenceNodeCodec { fn ext_node(partial: impl Iterator, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { let mut output = partial_to_key_it::(partial, nb_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + println!("pkitr: {:x?} {}", &output[..], nb_nibble); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -819,21 +822,21 @@ pub fn compare_impl + Eq> ( t.commit(); t.root().clone() }; - if root != root_new { + if root_new != root { { - let db : &dyn hash_db::HashDB<_,_> = &memdb; - let t = RefTrieDB::new(&db, &root).unwrap(); + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDB::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { - println!("a:{:?}", a); + println!("a:{:x?}", a); } } { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; - let t = RefTrieDB::new(&db, &root_new).unwrap(); + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDB::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { - println!("a:{:?}", a); + println!("a:{:x?}", a); } } } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index e8a3abad..202b2f91 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -233,7 +233,7 @@ where if let Some(nkeyix) = nkey { let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.range_iter(nkeyix.1); - let encoded = C::ext_node(nib, nkeyix.1 - nkeyix.0, branch_hash); + let encoded = C::ext_node(nib, nkeyix.1, branch_hash); let h = cb_ext.process(pr.left(), encoded, is_root); h } else { @@ -257,7 +257,7 @@ where let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); pr.range_iter(nkeyix.1), - nkeyix.1 - nkeyix.0, + nkeyix.1, self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 0f2e1b5b..435eae5c 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -294,13 +294,31 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> (Option, &'a [u8]) { let split = self.offset / 2; - if self.data.len() % 2 == 1 { - (Some(self.data[split] & (255 << 4)), &self.data[split + 1 ..]) + if self.len() % 2 == 1 { + (Some(self.data[split] & (255 >> 4)), &self.data[split + 1 ..]) } else { (None, &self.data[split..]) } } + /// return encoded value as an iterator + pub fn right_iter(&'a self) -> impl Iterator + 'a { + let (mut first, sl) = self.right(); + let mut ix = 0; + ::std::iter::from_fn( move || { + if first.is_some() { + first.take() + } else { + if ix < sl.len() { + ix += 1; + Some(sl[ix - 1]) + } else { + None + } + } + }) + } + /// return left of key nibble pub fn left(&'a self) -> (&'a [u8], Option) { let split = self.offset / 2; @@ -314,6 +332,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// get iterator over slice, slow /// TODO switch to padded access as in right padded eg with a move end function that return self /// and a padding len + /// TODO rename to mak it explicit that it works on encoded byte (not returning nibble) pub fn range_iter(&'a self, to: usize) -> impl Iterator + 'a { let mut first = to % 2; let aligned_i = (self.offset + to) % 2; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index d8e3f711..3028dcb7 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -19,7 +19,7 @@ use node_codec::NodeCodec; use super::lookup::Lookup; use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; use ::core_::marker::PhantomData; - +use triedbmut::{PartialKeyMut, concat_key_clone}; #[cfg(feature = "std")] use ::std::fmt; #[cfg(feature = "std")] @@ -146,8 +146,7 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - // TODO EMCH this field looks useless - partial_key: NibbleSlice<'a, L::N>, + partial_key: PartialKeyMut, index: Option, } @@ -157,7 +156,7 @@ where L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.left()) { + if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.mid().left()) { match L::C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { @@ -176,7 +175,7 @@ where .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: self.partial_key, + partial_key: concat_key_clone(&self.partial_key, Some(&slice), None), index: None, }) .finish(), @@ -188,7 +187,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: self.partial_key, + partial_key: concat_key_clone(&self.partial_key, None, Some(i as u8)), }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -207,7 +206,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: self.partial_key, + partial_key: concat_key_clone(&self.partial_key, Some(&slice), Some(i as u8)), }).collect(); match (f.debug_struct("Node::NibbledBranch"), self.index) { (ref mut d, Some(ref i)) => d.field("index", i), @@ -248,7 +247,7 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: NibbleSlice::new(&[]), + partial_key: PartialKeyMut::new(), index: None, }) .finish() diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index d988028d..c05ce335 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -100,15 +100,15 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { } -#[derive(Debug)] -struct PartialKeyMut { +#[derive(Debug, Clone)] +pub(crate) struct PartialKeyMut { key: ElasticArray36, pad: usize, marker: PhantomData, } // TODO EMCH almost replaceble with nibblevec impl PartialKeyMut { - fn new() -> PartialKeyMut { + pub(crate) fn new() -> PartialKeyMut { PartialKeyMut { key: ElasticArray36::new(), pad: 0, @@ -117,19 +117,20 @@ impl PartialKeyMut { } // TODO EMCH better truncate - fn truncate(&mut self, mov: usize) { + pub(crate) fn truncate(&mut self, mov: usize) { for _ in 0..mov { self.pop(); } } /// ret slice and nb of padding byte - fn mid(&self) -> NibbleSlice { + pub(crate) fn mid(&self) -> NibbleSlice { (NibbleSlice::new_offset(&self.key[..], self.key.len() * N::NIBBLE_PER_BYTE - self.pad)) } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - pub fn push(&mut self, nibble: u8) { + /// TODO EMCH make a append nibble slice fn (currently we push iter mostly) -> for aligned + pub(crate) fn push(&mut self, nibble: u8) { // TODO EMCH move to N let nibble = nibble & 0x0F; @@ -143,7 +144,7 @@ impl PartialKeyMut { } /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. - pub fn pop(&mut self) -> Option { + pub(crate) fn pop(&mut self) -> Option { let len = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; if len == 0 { return None; @@ -260,9 +261,10 @@ where // warning we know that partial does not use pop backward from this point, child_cb using pop // here will break things TODO pop limited version of TrieDBMut?? let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); let c = child_cb(child, Some(&pr), None); C::ext_node( - pr.iter(), + it, pr.len(), c, ) @@ -281,8 +283,9 @@ where }, Node::NibbledBranch(partial, mut children, value) => { let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); C::branch_node_nibbled( - pr.iter(), + it, pr.len(), // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() @@ -1281,7 +1284,8 @@ where } } -fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { +// TODO EMCH a with_concat_key function using a closure and truncating correctly +pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { let mut res = 0; if let Some(sl) = o_sl { // TODO EMCH align optim @@ -1297,6 +1301,12 @@ fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleS res } +pub(crate) fn concat_key_clone(prefix: &PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> PartialKeyMut { + let mut p = prefix.clone(); + concat_key(&mut p, o_sl, o_ix); + p +} + impl<'a, L> TrieMut for TrieDBMut<'a, L> where L: TrieLayOut, From f276ace67853e4678785f50140fae30069d8d62b Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 19 Apr 2019 19:15:00 +0200 Subject: [PATCH 033/120] bad work on fixing fix: buf is needed only for terminal and recursive is only inplace so no need to move buffer, just push term to nibble encoded (already buffered) --- trie-db/src/nibbleslice.rs | 6 ++ trie-db/src/triedb.rs | 2 +- trie-db/src/triedbmut.rs | 155 +++++++++++++++++++++++++++---------- 3 files changed, 119 insertions(+), 44 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 435eae5c..92aaf6eb 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -293,6 +293,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> (Option, &'a [u8]) { + println!("rd: {} {}", self.offset, self.data.len()); let split = self.offset / 2; if self.len() % 2 == 1 { (Some(self.data[split] & (255 >> 4)), &self.data[split + 1 ..]) @@ -328,6 +329,11 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { (&self.data[..split], None) } } + pub fn left_owned(&'a self) -> (ElasticArray36, Option) { + let (a, b) = self.left(); + (a.into(), b) + } + /// get iterator over slice, slow /// TODO switch to padded access as in right padded eg with a move end function that return self diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 3028dcb7..5adfa88c 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -156,7 +156,7 @@ where L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.mid().left()) { + if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.end().left()) { match L::C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index c05ce335..9b5edc7e 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -69,9 +69,10 @@ fn empty_children() -> Box<[Option>; 16]> { None, None, None, None, None, None, None, None, ]) } -// TODO rem +// TODO rem? not sure using a different name makes it explicit we are on a full key. +// therefore on left aligned slice #[derive(Clone, Debug)] -struct PartialKey<'key, N: NibbleOps> { +pub(crate) struct PartialKey<'key, N: NibbleOps> { key: NibbleSlice<'key, N>, } @@ -83,9 +84,19 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { } fn advance(&mut self, by: usize) { + + assert!(self.key.len() >= by); self.key = self.key.mid(by) } + /// advance of return none if underlying slice is to short + fn checked_advance(&mut self, by: usize) -> bool { + if self.key.len() >= by { + self.key = self.key.mid(by); + true + } else { false } + } + fn mid(&self) -> NibbleSlice<'key, N> { self.key } @@ -93,6 +104,7 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { fn encoded_prefix(&self) -> Prefix { self.key.left() } + fn encoded_prefix_owned(&self) -> (ElasticArray36, Option) { let (a, b) = self.encoded_prefix(); (a.into(), b) @@ -102,7 +114,7 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { #[derive(Debug, Clone)] pub(crate) struct PartialKeyMut { - key: ElasticArray36, + key: Vec, pad: usize, marker: PhantomData, } @@ -110,12 +122,24 @@ pub(crate) struct PartialKeyMut { impl PartialKeyMut { pub(crate) fn new() -> PartialKeyMut { PartialKeyMut { - key: ElasticArray36::new(), + key: Vec::new(), pad: 0, marker: Default::default(), } - } + /// warning this function expect the partialkey to be left aligned + pub(crate) fn from_partial(p: &PartialKey) -> PartialKeyMut { + let (sl, l) = p.encoded_prefix(); + let key = sl[..].into(); + let mut res = PartialKeyMut { + key, + pad: 0, + marker: Default::default(), + }; + l.map(|l| res.push(l)); + res + } + // TODO EMCH better truncate pub(crate) fn truncate(&mut self, mov: usize) { for _ in 0..mov { @@ -123,8 +147,16 @@ impl PartialKeyMut { } } - /// ret slice and nb of padding byte - pub(crate) fn mid(&self) -> NibbleSlice { + /// clear + pub(crate) fn clear(&mut self) { + self.pad = 0; + self.key.clear(); + } + + + /// ret slice and nb of padding byte TODO rename this is confusing plus it can only be followed + /// by end: also rename this struct to prefix_accum or something like that + pub(crate) fn end(&self) -> NibbleSlice { (NibbleSlice::new_offset(&self.key[..], self.key.len() * N::NIBBLE_PER_BYTE - self.pad)) } @@ -885,14 +917,15 @@ where (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), (Node::Branch(children, Some(val)), true) => { + println!("beffix"); *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) + Action::Replace(self.fix(Node::Branch(children, None), key.clone(), None)?) }, (Node::NibbledBranch(n, children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone())?) + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone(), None)?) }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; @@ -912,10 +945,11 @@ where } } None => { + println!("beffixrigh"); // the child we took was deleted. // the node may need fixing. trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::Branch(children, value), prefix)?) + Action::Replace(self.fix(Node::Branch(children, value), prefix, None)?) } } } else { @@ -934,7 +968,7 @@ where if let Some(val) = value { *old_val = Some(val); - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone()); + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone(), None); Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) @@ -965,7 +999,7 @@ where // the child we took was deleted. // the node may need fixing. trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) + Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix, None)?) }, } } else { @@ -1002,7 +1036,7 @@ where // if the child branch was unchanged, then the extension is too. // otherwise, this extension may need fixing. match changed { - true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix)?), + true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix, None)?), false => Action::Restore(Node::Extension(encoded, new_child)), } } @@ -1020,13 +1054,19 @@ where }) } + fn lazy_get_buff<'b>(buff_key: &'b mut Option>, key: &PartialKey) -> &'b mut PartialKeyMut { + if buff_key.is_none() { + *buff_key = Some(PartialKeyMut::from_partial(key)); + } + buff_key.as_mut().expect("lazy init above;qed") + } /// Given a node which may be in an _invalid state_, fix it such that it is then in a valid /// state. /// /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node>, key: PartialKey) -> Result>, TrieHash, CError> { + fn fix(&mut self, node: Node>, key: PartialKey, buff_key: Option>) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1051,11 +1091,13 @@ where match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { + println!("IN ONE {:x?}", key); // only one onward node. make an extension. let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + println!("part {:x?}", new_partial); let new_node = Node::Extension(new_partial, child); - self.fix(new_node, key) + self.fix(new_node, key, buff_key) } (UsedIndex::None, Some(value)) => { // make a leaf. @@ -1092,37 +1134,49 @@ where match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { + // TODO EMCH can simplify code by transforming to an extension like in branch + // (extension only temp value before being transformed). // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); - let mut key = key; + let mut kc = key.clone(); + let b_slice_len = (enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0; + kc.advance(b_slice_len); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0 + 1); - key.advance(1); - let handle = self.cache(h, key.encoded_prefix())?; + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + buf.push(a); + // TODO NX !!! from this key advenac we are out of key -> can do a + // paded_encoded_prefix for one insert + let handle = self.cache(h, buf.end().left())?; + buf.pop(); self.storage.destroy(handle) } }; let child_node = match stored { Stored::New(node) => node, Stored::Cached(node, hash) => { - self.death_row.insert((hash, key.encoded_prefix_owned())); + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + buf.push(a); + self.death_row.insert((hash, buf.end().left_owned())); + buf.pop(); node }, }; match child_node { Node::Leaf(sub_partial, value) => { - key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); - key.advance(1); - key.advance((sub_partial.1.len() * L::N::NIBBLE_PER_BYTE) - sub_partial.0); - Ok(Node::Leaf(key.mid().to_stored(), value)) + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + // TODO NX!!! + buf.push(a); + let s = buf.push_stored(&sub_partial); + Ok(Node::Leaf(buf.to_stored(s + 1 + b_slice_len), value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { - key.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); - key.advance(1); - key.advance((sub_partial.1.len() * L::N::NIBBLE_PER_BYTE) - sub_partial.0); - Ok(Node::NibbledBranch(key.mid().to_stored(), ch_children, ch_value)) + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + // TODO NX!!! + buf.push(a); + let s = buf.push_stored(&sub_partial); + Ok(Node::NibbledBranch(buf.to_stored(s + 1 + b_slice_len), ch_children, ch_value)) }, _ => unreachable!(), } @@ -1140,11 +1194,12 @@ where } }, Node::Extension(partial, child) => { + let mut kc = key.clone(); + let ex_slice_len = (partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0; + kc.advance(ex_slice_len); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let mut kc = key.clone(); - kc.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); let handle = self.cache(h, kc.encoded_prefix())?; self.storage.destroy(handle) } @@ -1162,23 +1217,25 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, key.encoded_prefix_owned())); } - let mut kc = key.clone(); - kc.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); - kc.advance(sub_partial.1.len() * L::N::NIBBLE_PER_BYTE - sub_partial.0); - trace!(target: "trie", "fixing: extension combination. new_partial={:?}", kc.mid()); - self.fix(Node::Extension(kc.mid().to_stored(), sub_child), key) + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + let s = buf.push_stored(&sub_partial); + // TODO NX!! + trace!(target: "trie", "fixing: extension combination. new_partial={:?}", kc.mid().to_stored()); + self.fix(Node::Extension(buf.to_stored(s + ex_slice_len), sub_child), key, None); // TODO EMCH no need to buff in param... } Node::Leaf(sub_partial, value) => { - let mut key = key; + println!("leaf combine {:x?}", key); // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. self.death_row.insert((hash, key.encoded_prefix_owned())); } - key.advance(partial.1.len() * L::N::NIBBLE_PER_BYTE - partial.0); - key.advance(sub_partial.1.len() * L::N::NIBBLE_PER_BYTE - sub_partial.0); - trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", key.mid()); - Ok(Node::Leaf(key.mid().to_stored(), value)) + let buf = Self::lazy_get_buff(&mut buff_key, &kc); + let s = buf.push_stored(&sub_partial); + // TODO NX!! + trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", kc.mid()); + println!("fixing: extension -> leaf. new_partial={:?}", kc.mid()); + Ok(Node::Leaf(buf.to_stored(s + ex_slice_len), value)) } child_node => { trace!(target: "trie", "fixing: restoring extension"); @@ -1259,7 +1316,7 @@ where node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; if encoded.len() >= L::H::LENGTH { - let hash = self.db.insert(prefix.mid().left(), &encoded[..]); + let hash = self.db.insert(prefix.end().left(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { @@ -1284,6 +1341,8 @@ where } } + +// TODO EMCH change usage here to run on self buffer // TODO EMCH a with_concat_key function using a closure and truncating correctly pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { let mut res = 0; @@ -1789,20 +1848,30 @@ mod tests { min_key: 5, journal_key: 0, value_mode: ValueMode::Index, - count: 4, + count: 2, }.make_with(&mut seed); let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); + { let mut t = RefTrieDBMut::new(&mut db, &mut root); for &(ref key, ref value) in &x { + println!("k{:x?}",key); assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); } + t.remove(&x.iter().next().unwrap().0).unwrap(); + } + let t = RefTrieDB::new(&db, &root); + println!("{:?}", t); + panic!("yo"); +/* let mut i = 0; for (key, value) in x { + println!("rem{}",i); + i+=1; assert_eq!(t.remove(&key).unwrap(), Some(DBValue::from_slice(&value))); assert!(t.remove(&key).unwrap().is_none()); - } + }*/ } } From c5d907d4ca6fb9359280ae8701ad322755e04fdc Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 19 Apr 2019 19:38:56 +0200 Subject: [PATCH 034/120] small cleaning --- trie-db/src/triedbmut.rs | 51 ++++++++++++---------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 9b5edc7e..bdbd6fb5 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -920,12 +920,12 @@ where println!("beffix"); *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::Branch(children, None), key.clone(), None)?) + Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) }, (Node::NibbledBranch(n, children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone(), None)?) + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone())?) }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; @@ -949,7 +949,7 @@ where // the child we took was deleted. // the node may need fixing. trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::Branch(children, value), prefix, None)?) + Action::Replace(self.fix(Node::Branch(children, value), prefix)?) } } } else { @@ -999,7 +999,7 @@ where // the child we took was deleted. // the node may need fixing. trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix, None)?) + Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) }, } } else { @@ -1036,7 +1036,7 @@ where // if the child branch was unchanged, then the extension is too. // otherwise, this extension may need fixing. match changed { - true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix, None)?), + true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix)?), false => Action::Restore(Node::Extension(encoded, new_child)), } } @@ -1054,19 +1054,13 @@ where }) } - fn lazy_get_buff<'b>(buff_key: &'b mut Option>, key: &PartialKey) -> &'b mut PartialKeyMut { - if buff_key.is_none() { - *buff_key = Some(PartialKeyMut::from_partial(key)); - } - buff_key.as_mut().expect("lazy init above;qed") - } /// Given a node which may be in an _invalid state_, fix it such that it is then in a valid /// state. /// /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node>, key: PartialKey, buff_key: Option>) -> Result>, TrieHash, CError> { + fn fix(&mut self, node: Node>, key: PartialKey) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1091,13 +1085,11 @@ where match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { - println!("IN ONE {:x?}", key); // only one onward node. make an extension. let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take().expect("used_index only set if occupied; qed"); - println!("part {:x?}", new_partial); let new_node = Node::Extension(new_partial, child); - self.fix(new_node, key, buff_key) + self.fix(new_node, key) } (UsedIndex::None, Some(value)) => { // make a leaf. @@ -1144,10 +1136,8 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let buf = Self::lazy_get_buff(&mut buff_key, &kc); - buf.push(a); // TODO NX !!! from this key advenac we are out of key -> can do a - // paded_encoded_prefix for one insert + // paded_encoded_prefix for one insert - just a nibble ta add (can reuse buff) let handle = self.cache(h, buf.end().left())?; buf.pop(); self.storage.destroy(handle) @@ -1156,26 +1146,19 @@ where let child_node = match stored { Stored::New(node) => node, Stored::Cached(node, hash) => { - let buf = Self::lazy_get_buff(&mut buff_key, &kc); - buf.push(a); - self.death_row.insert((hash, buf.end().left_owned())); - buf.pop(); + self.death_row.insert((hash, key.encoded_prefix_owned())); node }, }; match child_node { Node::Leaf(sub_partial, value) => { - let buf = Self::lazy_get_buff(&mut buff_key, &kc); // TODO NX!!! - buf.push(a); - let s = buf.push_stored(&sub_partial); + // TODO a + buf + ck Ok(Node::Leaf(buf.to_stored(s + 1 + b_slice_len), value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { - let buf = Self::lazy_get_buff(&mut buff_key, &kc); // TODO NX!!! - buf.push(a); - let s = buf.push_stored(&sub_partial); + // TODO a + buf + ck Ok(Node::NibbledBranch(buf.to_stored(s + 1 + b_slice_len), ch_children, ch_value)) }, _ => unreachable!(), @@ -1194,12 +1177,10 @@ where } }, Node::Extension(partial, child) => { - let mut kc = key.clone(); - let ex_slice_len = (partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0; - kc.advance(ex_slice_len); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { + // TODO NX!! here to let handle = self.cache(h, kc.encoded_prefix())?; self.storage.destroy(handle) } @@ -1217,11 +1198,10 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, key.encoded_prefix_owned())); } - let buf = Self::lazy_get_buff(&mut buff_key, &kc); - let s = buf.push_stored(&sub_partial); // TODO NX!! + // subpartial trace!(target: "trie", "fixing: extension combination. new_partial={:?}", kc.mid().to_stored()); - self.fix(Node::Extension(buf.to_stored(s + ex_slice_len), sub_child), key, None); // TODO EMCH no need to buff in param... + self.fix(Node::Extension(buf.to_stored(s + ex_slice_len), sub_child), key) } Node::Leaf(sub_partial, value) => { println!("leaf combine {:x?}", key); @@ -1230,9 +1210,8 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, key.encoded_prefix_owned())); } - let buf = Self::lazy_get_buff(&mut buff_key, &kc); - let s = buf.push_stored(&sub_partial); // TODO NX!! + // subpartial oly trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", kc.mid()); println!("fixing: extension -> leaf. new_partial={:?}", kc.mid()); Ok(Node::Leaf(buf.to_stored(s + ex_slice_len), value)) From 8ee9798ad695cec13375cb180e2a83584f5b6bf9 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 22 Apr 2019 19:29:50 +0200 Subject: [PATCH 035/120] implement, still 2 failing test (note on remove I did change prefix a bit) --- trie-db/src/nibbleslice.rs | 1 - trie-db/src/triedbmut.rs | 116 ++++++++++++++++++++++++++++++------- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 92aaf6eb..cc6daf1b 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -293,7 +293,6 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> (Option, &'a [u8]) { - println!("rd: {} {}", self.offset, self.data.len()); let split = self.offset / 2; if self.len() % 2 == 1 { (Some(self.data[split] & (255 >> 4)), &self.data[split + 1 ..]) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index bdbd6fb5..405f69b9 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -917,7 +917,6 @@ where (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), (Node::Branch(children, Some(val)), true) => { - println!("beffix"); *old_val = Some(val); // always replace since we took the value out. Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) @@ -945,7 +944,6 @@ where } } None => { - println!("beffixrigh"); // the child we took was deleted. // the node may need fixing. trace!(target: "trie", "branch child deleted, partial={:?}", partial); @@ -968,7 +966,7 @@ where if let Some(val) = value { *old_val = Some(val); - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone(), None); + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone()); Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) @@ -1131,35 +1129,47 @@ where // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let mut kc = key.clone(); - let b_slice_len = (enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0; - kc.advance(b_slice_len); + kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); + + let (st, ost, op) = match kc.encoded_prefix() { + (st, Some(v)) => { + let mut so: ElasticArray36 = st.into(); + so.push((v & (255 << 4)) | (a >> 4)); + (st, Some(so), None) + }, + (st, None) => (st, None, Some(a)), + }; + let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { // TODO NX !!! from this key advenac we are out of key -> can do a - // paded_encoded_prefix for one insert - just a nibble ta add (can reuse buff) - let handle = self.cache(h, buf.end().left())?; - buf.pop(); + let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } }; let child_node = match stored { Stored::New(node) => node, Stored::Cached(node, hash) => { - self.death_row.insert((hash, key.encoded_prefix_owned())); + self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); node }, }; match child_node { Node::Leaf(sub_partial, value) => { + let mut enc_nibble = enc_nibble; // TODO NX!!! - // TODO a + buf + ck - Ok(Node::Leaf(buf.to_stored(s + 1 + b_slice_len), value)) + combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + Ok(Node::Leaf(enc_nibble, value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { + let mut enc_nibble = enc_nibble; // TODO NX!!! // TODO a + buf + ck - Ok(Node::NibbledBranch(buf.to_stored(s + 1 + b_slice_len), ch_children, ch_value)) + combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + Ok(Node::NibbledBranch(enc_nibble, ch_children, ch_value)) }, _ => unreachable!(), } @@ -1177,11 +1187,26 @@ where } }, Node::Extension(partial, child) => { + // we could advance key if there was not the recursion case, there might be prefix from + // branch. + let a = partial.1[partial.1.len() - 1] & (255 >> 4); + let mut kc = key.clone(); + kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); + let (st, ost, op) = match kc.encoded_prefix() { + (st, Some(v)) => { + let mut so: ElasticArray36 = st.into(); + so.push((v & (255 << 4)) | (a >> 4)); + (st, Some(so), None) + }, + (st, None) => (st, None, Some(a)), + }; + let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); + let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { // TODO NX!! here to - let handle = self.cache(h, kc.encoded_prefix())?; + let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } }; @@ -1196,25 +1221,27 @@ where // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key.encoded_prefix_owned())); + self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } // TODO NX!! // subpartial - trace!(target: "trie", "fixing: extension combination. new_partial={:?}", kc.mid().to_stored()); - self.fix(Node::Extension(buf.to_stored(s + ex_slice_len), sub_child), key) + let mut partial = partial; + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + trace!(target: "trie", "fixing: extension combination. new_partial={:?}", partial); + self.fix(Node::Extension(partial, sub_child), key) } Node::Leaf(sub_partial, value) => { - println!("leaf combine {:x?}", key); // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key.encoded_prefix_owned())); + self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } // TODO NX!! // subpartial oly - trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", kc.mid()); - println!("fixing: extension -> leaf. new_partial={:?}", kc.mid()); - Ok(Node::Leaf(buf.to_stored(s + ex_slice_len), value)) + let mut partial = partial; + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", partial); + Ok(Node::Leaf(partial, value)) } child_node => { trace!(target: "trie", "fixing: restoring extension"); @@ -1423,6 +1450,37 @@ where } } +fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { + let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; + let _shifted = shift_key::(start, final_ofset); + let st = if end.0 > 0 { + let sl = start.1.len(); + start.1[sl - 1] |= end.1[0] & (255 >> 4); + 1 + } else { + 0 + }; + (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); +} + +fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { + let old = key.0; + key.0 = ofset; + if old > ofset { + let kl = key.1.len(); + (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << 4 | key.1[i+1]>>4); + key.1[kl - 1] = key.1[kl - 1] << 4; + true + } else if old < ofset { + key.1.push(0); + (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << 4 | key.1[i] >> 4); + key.1[0] = key.1[0] >> 4; + true + } else { + false + } +} + #[cfg(test)] mod tests { use env_logger; @@ -1431,6 +1489,7 @@ mod tests { use memory_db::{MemoryDB, PrefixedKey, HashKey}; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; + use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ref_trie_root, RefTrieDB, RefTrieDBNoExt, LayoutOri}; @@ -1853,4 +1912,19 @@ mod tests { assert!(t.remove(&key).unwrap().is_none()); }*/ } + + #[test] + fn combine_test() { + let a: ElasticArray36 = [0x12, 0x34][..].into(); + let b: &[u8] = [0x56, 0x78][..].into(); + let test_comb = |a: (_,&ElasticArray36<_>), b, c| { + let mut a = (a.0,a.1.clone()); + super::combine_key::(&mut a, b); + assert_eq!((a.0,&a.1[..]), c); + }; + test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((1, &a), (0, &b), (1, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); + test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); + } } From 241eb4a5250b485f2574e94fb7f8ee7be5d420f2 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 20 Apr 2019 18:51:21 +0200 Subject: [PATCH 036/120] nightly debug format --- trie-db/src/triedb.rs | 32 ++++++++++++++++---------------- trie-db/src/triedbmut.rs | 12 +----------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 5adfa88c..564c1ced 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -815,38 +815,38 @@ mod tests { slice: , value: [ 65, - 65 - ] + 65, + ], }, Node::Leaf { index: 2, slice: , value: [ 65, - 66 - ] - } + 66, + ], + }, ], - value: None - } + value: None, + }, ], value: Some( [ - 65 - ] - ) + 65, + ], + ), }, Node::Leaf { index: 2, slice: , value: [ - 66 - ] - } + 66, + ], + }, ], - value: None - } - } + value: None, + }, + }, }"); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 405f69b9..5349c047 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1891,26 +1891,16 @@ mod tests { let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - { let mut t = RefTrieDBMut::new(&mut db, &mut root); for &(ref key, ref value) in &x { println!("k{:x?}",key); assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); } - - t.remove(&x.iter().next().unwrap().0).unwrap(); - } - let t = RefTrieDB::new(&db, &root); - println!("{:?}", t); - panic!("yo"); -/* let mut i = 0; for (key, value) in x { - println!("rem{}",i); - i+=1; assert_eq!(t.remove(&key).unwrap(), Some(DBValue::from_slice(&value))); assert!(t.remove(&key).unwrap().is_none()); - }*/ + } } #[test] From 71e8fab81c43e3c5626151f722414e58bb4480a5 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 20 Apr 2019 19:58:19 +0200 Subject: [PATCH 037/120] Fix remove issue, will need more test (playpen on noext and check nothing in memorydb after full removal). --- memory-db/src/lib.rs | 4 +- trie-db/src/triedbmut.rs | 224 +++++++++++++++++++-------------------- 2 files changed, 113 insertions(+), 115 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index eac74ff7..c1e511ea 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -256,7 +256,7 @@ where /// extern crate keccak_hasher; /// extern crate memory_db; /// - /// use hash_db::{Hasher, HashDB}; + /// use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; /// use keccak_hasher::KeccakHasher; /// use memory_db::{MemoryDB, HashKey}; /// @@ -410,7 +410,6 @@ where return Some(self.null_node_data.clone()); } - println!("get : {:x?} {:x?}", key, prefix); let key = KF::key(key, prefix); let r = match self.data.get(&key) { Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), @@ -436,7 +435,6 @@ where return; } - println!("emp : {:x?} {:x?}", key, prefix); let key = KF::key(&key, prefix); match self.data.entry(key) { Entry::Occupied(mut entry) => { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 5349c047..ec908954 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -85,16 +85,16 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { fn advance(&mut self, by: usize) { - assert!(self.key.len() >= by); + assert!(self.key.len() >= by); self.key = self.key.mid(by) } - /// advance of return none if underlying slice is to short - fn checked_advance(&mut self, by: usize) -> bool { - if self.key.len() >= by { - self.key = self.key.mid(by); - true - } else { false } + /// advance of return none if underlying slice is to short + fn checked_advance(&mut self, by: usize) -> bool { + if self.key.len() >= by { + self.key = self.key.mid(by); + true + } else { false } } fn mid(&self) -> NibbleSlice<'key, N> { @@ -127,17 +127,17 @@ impl PartialKeyMut { marker: Default::default(), } } - /// warning this function expect the partialkey to be left aligned + /// warning this function expect the partialkey to be left aligned pub(crate) fn from_partial(p: &PartialKey) -> PartialKeyMut { - let (sl, l) = p.encoded_prefix(); - let key = sl[..].into(); - let mut res = PartialKeyMut { - key, - pad: 0, + let (sl, l) = p.encoded_prefix(); + let key = sl[..].into(); + let mut res = PartialKeyMut { + key, + pad: 0, marker: Default::default(), - }; - l.map(|l| res.push(l)); - res + }; + l.map(|l| res.push(l)); + res } // TODO EMCH better truncate @@ -147,21 +147,21 @@ impl PartialKeyMut { } } - /// clear + /// clear pub(crate) fn clear(&mut self) { - self.pad = 0; - self.key.clear(); + self.pad = 0; + self.key.clear(); } /// ret slice and nb of padding byte TODO rename this is confusing plus it can only be followed - /// by end: also rename this struct to prefix_accum or something like that + /// by end: also rename this struct to prefix_accum or something like that pub(crate) fn end(&self) -> NibbleSlice { (NibbleSlice::new_offset(&self.key[..], self.key.len() * N::NIBBLE_PER_BYTE - self.pad)) } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - /// TODO EMCH make a append nibble slice fn (currently we push iter mostly) -> for aligned + /// TODO EMCH make a append nibble slice fn (currently we push iter mostly) -> for aligned pub(crate) fn push(&mut self, nibble: u8) { // TODO EMCH move to N let nibble = nibble & 0x0F; @@ -317,7 +317,7 @@ where let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); C::branch_node_nibbled( - it, + it, pr.len(), // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() @@ -1084,6 +1084,7 @@ where (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. make an extension. + let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); @@ -1124,26 +1125,26 @@ where match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { - // TODO EMCH can simplify code by transforming to an extension like in branch - // (extension only temp value before being transformed). + // TODO EMCH can simplify code by transforming to an extension like in branch + // (extension only temp value before being transformed). // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); - let mut kc = key.clone(); - kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); - - let (st, ost, op) = match kc.encoded_prefix() { - (st, Some(v)) => { - let mut so: ElasticArray36 = st.into(); - so.push((v & (255 << 4)) | (a >> 4)); - (st, Some(so), None) - }, - (st, None) => (st, None, Some(a)), - }; - let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); + let mut kc = key.clone(); + kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); + + let (st, ost, op) = match kc.encoded_prefix() { + (st, Some(v)) => { + let mut so: ElasticArray36 = st.into(); + so.push((v & (255 << 4)) | a); + (st, Some(so), None) + }, + (st, None) => (st, None, Some(a << 4)), + }; + let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO NX !!! from this key advenac we are out of key -> can do a + // TODO NX !!! from this key advenac we are out of key -> can do a let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } @@ -1157,18 +1158,18 @@ where }; match child_node { Node::Leaf(sub_partial, value) => { - let mut enc_nibble = enc_nibble; - // TODO NX!!! - combine_key::(&mut enc_nibble, (1, &[a][..])); - combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + let mut enc_nibble = enc_nibble; + // TODO NX!!! + combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); Ok(Node::Leaf(enc_nibble, value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { - let mut enc_nibble = enc_nibble; - // TODO NX!!! - // TODO a + buf + ck - combine_key::(&mut enc_nibble, (1, &[a][..])); - combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + let mut enc_nibble = enc_nibble; + // TODO NX!!! + // TODO a + buf + ck + combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); Ok(Node::NibbledBranch(enc_nibble, ch_children, ch_value)) }, _ => unreachable!(), @@ -1187,25 +1188,25 @@ where } }, Node::Extension(partial, child) => { - // we could advance key if there was not the recursion case, there might be prefix from - // branch. - let a = partial.1[partial.1.len() - 1] & (255 >> 4); - let mut kc = key.clone(); - kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); - let (st, ost, op) = match kc.encoded_prefix() { - (st, Some(v)) => { - let mut so: ElasticArray36 = st.into(); - so.push((v & (255 << 4)) | (a >> 4)); - (st, Some(so), None) - }, - (st, None) => (st, None, Some(a)), - }; - let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); + // we could advance key if there was not the recursion case, there might be prefix from + // branch. + let a = partial.1[partial.1.len() - 1] & (255 >> 4); + let mut kc = key.clone(); + kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); + let (st, ost, op) = match kc.encoded_prefix() { + (st, Some(v)) => { + let mut so: ElasticArray36 = st.into(); + so.push((v & (255 << 4)) | a); + (st, Some(so), None) + }, + (st, None) => (st, None, Some(a << 4)), + }; + let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO NX!! here to + // TODO NX!! here to let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } @@ -1223,10 +1224,10 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } - // TODO NX!! - // subpartial - let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + // TODO NX!! + // subpartial + let mut partial = partial; + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); trace!(target: "trie", "fixing: extension combination. new_partial={:?}", partial); self.fix(Node::Extension(partial, sub_child), key) } @@ -1236,10 +1237,10 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } - // TODO NX!! - // subpartial oly - let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + // TODO NX!! + // subpartial oly + let mut partial = partial; + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", partial); Ok(Node::Leaf(partial, value)) } @@ -1367,9 +1368,9 @@ pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Opti } pub(crate) fn concat_key_clone(prefix: &PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> PartialKeyMut { - let mut p = prefix.clone(); - concat_key(&mut p, o_sl, o_ix); - p + let mut p = prefix.clone(); + concat_key(&mut p, o_sl, o_ix); + p } impl<'a, L> TrieMut for TrieDBMut<'a, L> @@ -1451,34 +1452,34 @@ where } fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { - let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; - let _shifted = shift_key::(start, final_ofset); - let st = if end.0 > 0 { - let sl = start.1.len(); - start.1[sl - 1] |= end.1[0] & (255 >> 4); - 1 - } else { - 0 - }; - (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); + let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; + let _shifted = shift_key::(start, final_ofset); + let st = if end.0 > 0 { + let sl = start.1.len(); + start.1[sl - 1] |= end.1[0] & (255 >> 4); + 1 + } else { + 0 + }; + (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); } fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { - let old = key.0; - key.0 = ofset; - if old > ofset { - let kl = key.1.len(); - (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << 4 | key.1[i+1]>>4); - key.1[kl - 1] = key.1[kl - 1] << 4; - true - } else if old < ofset { - key.1.push(0); - (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << 4 | key.1[i] >> 4); - key.1[0] = key.1[0] >> 4; - true - } else { - false - } + let old = key.0; + key.0 = ofset; + if old > ofset { + let kl = key.1.len(); + (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << 4 | key.1[i+1]>>4); + key.1[kl - 1] = key.1[kl - 1] << 4; + true + } else if old < ofset { + key.1.push(0); + (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << 4 | key.1[i] >> 4); + key.1[0] = key.1[0] >> 4; + true + } else { + false + } } #[cfg(test)] @@ -1893,7 +1894,6 @@ mod tests { let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut db, &mut root); for &(ref key, ref value) in &x { - println!("k{:x?}",key); assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); } @@ -1903,18 +1903,18 @@ mod tests { } } - #[test] - fn combine_test() { - let a: ElasticArray36 = [0x12, 0x34][..].into(); - let b: &[u8] = [0x56, 0x78][..].into(); - let test_comb = |a: (_,&ElasticArray36<_>), b, c| { - let mut a = (a.0,a.1.clone()); - super::combine_key::(&mut a, b); - assert_eq!((a.0,&a.1[..]), c); - }; - test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); - test_comb((1, &a), (0, &b), (1, &[0x12, 0x34, 0x56, 0x78][..])); - test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); - test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); - } + #[test] + fn combine_test() { + let a: ElasticArray36 = [0x12, 0x34][..].into(); + let b: &[u8] = [0x56, 0x78][..].into(); + let test_comb = |a: (_,&ElasticArray36<_>), b, c| { + let mut a = (a.0,a.1.clone()); + super::combine_key::(&mut a, b); + assert_eq!((a.0,&a.1[..]), c); + }; + test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((1, &a), (0, &b), (1, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); + test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); + } } From 0c7efd12610a37930f8fdaa256fb332564885476 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Apr 2019 17:12:29 +0200 Subject: [PATCH 038/120] clean up prefixkeymut before merge with keyvec --- trie-db/src/nibbleslice.rs | 2 +- trie-db/src/nibblevec.rs | 8 +++ trie-db/src/triedb.rs | 2 +- trie-db/src/triedbmut.rs | 122 +++++++++++++++++++++++-------------- 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index cc6daf1b..b673d439 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -292,7 +292,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// return first encoded byte and following slice - pub fn right(&'a self) -> (Option, &'a [u8]) { + pub fn right(&'a self) -> Partial { let split = self.offset / 2; if self.len() % 2 == 1 { (Some(self.data[split] & (255 >> 4)), &self.data[split + 1 ..]) diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index 8bcf89dd..06b0fdd3 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -70,6 +70,14 @@ impl NibbleVec { pub fn inner(&self) -> &[u8] { &self.inner[..] } + + /// clear + pub fn clear(&mut self) { + self.inner.clear(); + self.len = 0; + } + + } impl<'a, N: NibbleOps> From> for NibbleVec { diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 564c1ced..10710246 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -156,7 +156,7 @@ where L: TrieLayOut, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.end().left()) { + if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { match L::C::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index ec908954..28410f6d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -17,7 +17,7 @@ use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; use super::lookup::Lookup; use super::node::Node as EncodedNode; -use node_codec::NodeCodec; +use node_codec::{NodeCodec, Partial}; use super::{DBValue, node::NodeKey}; use hash_db::{HashDB, Hasher, Prefix}; @@ -114,7 +114,7 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { #[derive(Debug, Clone)] pub(crate) struct PartialKeyMut { - key: Vec, + key: ElasticArray36, pad: usize, marker: PhantomData, } @@ -122,46 +122,55 @@ pub(crate) struct PartialKeyMut { impl PartialKeyMut { pub(crate) fn new() -> PartialKeyMut { PartialKeyMut { - key: Vec::new(), + key: Default::default(), pad: 0, marker: Default::default(), } } - /// warning this function expect the partialkey to be left aligned - pub(crate) fn from_partial(p: &PartialKey) -> PartialKeyMut { - let (sl, l) = p.encoded_prefix(); - let key = sl[..].into(); - let mut res = PartialKeyMut { - key, - pad: 0, - marker: Default::default(), - }; - l.map(|l| res.push(l)); - res - } - - // TODO EMCH better truncate + + /// truncate n last nibbles. pub(crate) fn truncate(&mut self, mov: usize) { - for _ in 0..mov { - self.pop(); + if mov == 0 { return; } + if mov >= self.key.len() * N::NIBBLE_PER_BYTE - self.pad { + self.clear(); + return; + } + let nb_rem = if (self.pad + mov) % N::NIBBLE_PER_BYTE > 0 { + self.pad = 1; + mov / N::NIBBLE_PER_BYTE + + } else { + self.pad = 0; + (mov + 1) / N::NIBBLE_PER_BYTE + + }; + (0..nb_rem).for_each(|_|{ self.key.pop(); }); + if self.pad == 1 { + let kl = self.key.len() - 1; + self.key[kl] &= 255 << 4; } } /// clear - pub(crate) fn clear(&mut self) { - self.pad = 0; + pub fn clear(&mut self) { self.key.clear(); + self.pad = 0; } - /// ret slice and nb of padding byte TODO rename this is confusing plus it can only be followed - /// by end: also rename this struct to prefix_accum or something like that - pub(crate) fn end(&self) -> NibbleSlice { - (NibbleSlice::new_offset(&self.key[..], self.key.len() * N::NIBBLE_PER_BYTE - self.pad)) + /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). + pub(crate) fn as_prefix(&self) -> Prefix { + // TODO EMCH very similar to left fn -> put as much as possible in nibbleops + let offset = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; + let split = offset / 2; + if offset % 2 == 1 { + (&self.key[..split], Some(self.key[split] & (255 << 4))) + } else { + (&self.key[..split], None) + } } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - /// TODO EMCH make a append nibble slice fn (currently we push iter mostly) -> for aligned pub(crate) fn push(&mut self, nibble: u8) { // TODO EMCH move to N let nibble = nibble & 0x0F; @@ -175,24 +184,22 @@ impl PartialKeyMut { } } - /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. - pub(crate) fn pop(&mut self) -> Option { - let len = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; - if len == 0 { - return None; + /// push a full partial. + pub(crate) fn append_partial(&mut self, (o_n, sl): Partial) { + if let Some(nibble) = o_n { + self.push(nibble) } - let byte = self.key.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if self.pad == 0 { - // TODO EMCH rem pop / push - self.key.push(byte & 0xF0); - self.pad = 1; - byte & 0x0F + if self.pad == 0 { + self.key.append_slice(&sl[..]); } else { - self.pad -= 1; - byte >> 4 - }; - - Some(nibble) + let kend = self.key.len() - 1; + if sl.len() > 0 { + self.key[kend] &= 255 << 4; + self.key[kend] |= sl[0] >> 4; + (0..sl.len() - 1).for_each(|i|self.key.push(sl[i] << 4 | sl[i+1]>>4)); + self.key.push(sl[sl.len() - 1] << 4); + } + } } } @@ -1323,7 +1330,7 @@ where node.into_encoded::<_, L::C, L::H, L::N>(commit_child) }; if encoded.len() >= L::H::LENGTH { - let hash = self.db.insert(prefix.end().left(), &encoded[..]); + let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { @@ -1354,10 +1361,7 @@ where pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { let mut res = 0; if let Some(sl) = o_sl { - // TODO EMCH align optim - for n in sl.iter() { - prefix.push(n); - } + prefix.append_partial(sl.right()); res += sl.len(); } if let Some(ix) = o_ix { @@ -1917,4 +1921,28 @@ mod tests { test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); } + + #[test] + fn truncate_test() { + let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { + let mut k = super::PartialKeyMut::::new(); + for v in a { + k.push(*v); + } + k.truncate(b); + assert_eq!((&k.key[..], k.pad), c); + }; + test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 0)); + test_trun(&[1,2,3,4], 1, (&[0x12, 0x30], 1)); + test_trun(&[1,2,3,4], 2, (&[0x12], 0)); + test_trun(&[1,2,3,4], 3, (&[0x10], 1)); + test_trun(&[1,2,3,4], 4, (&[], 0)); + test_trun(&[1,2,3,4], 5, (&[], 0)); + test_trun(&[1,2,3], 0, (&[0x12, 0x30], 1)); + test_trun(&[1,2,3], 1, (&[0x12], 0)); + test_trun(&[1,2,3], 2, (&[0x10], 1)); + test_trun(&[1,2,3], 3, (&[], 0)); + test_trun(&[1,2,3], 4, (&[], 0)); + } + } From 107027b8667adefde209791d3730939718dde7ba Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Apr 2019 19:39:15 +0200 Subject: [PATCH 039/120] Use keyvec instead of keymut. --- trie-db/src/nibbleslice.rs | 116 ++++++++++++--------------------- trie-db/src/nibblevec.rs | 104 +++++++++++++++++++++++++++++- trie-db/src/triedb.rs | 7 +- trie-db/src/triedbmut.rs | 128 +++---------------------------------- 4 files changed, 156 insertions(+), 199 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index b673d439..84b3d222 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -54,12 +54,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Try to get the nibble at the given offset. fn vec_at(s: &NibbleVec, idx: usize) -> u8; - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - fn push(s: &mut NibbleVec, nibble: u8); - - /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. - fn pop(s: &mut NibbleVec) -> Option; - } /// half byte nibble prepend encoding @@ -112,35 +106,6 @@ impl NibbleOps for NibblePreHalf { } } - fn push(s: &mut NibbleVec, nibble: u8) { - let nibble = nibble & 0x0F; - - if s.len % 2 == 0 { - s.inner.push(nibble << 4); - } else { - *s.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - } - - s.len += 1; - } - - fn pop(s: &mut NibbleVec) -> Option { - if s.is_empty() { - return None; - } - - let byte = s.inner.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if s.len % 2 == 0 { - s.inner.push(byte & 0xF0); - byte & 0x0F - } else { - byte >> 4 - }; - - s.len -= 1; - Some(nibble) - } - } @@ -211,39 +176,42 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { NibbleSliceIterator { p: self, i: 0 } } -/* - /// Create a new nibble slice from the given encoded data, applying start padding. - pub fn from_encoded(data: &[u8], nibble_len: usize) -> NibbleSlice { - // TODO EMCH better expression or specialization for len - let start_padding = (N::NIBBLE_PER_BYTE - data.len() % N::NIBBLE_PER_BYTE) % N::NIBBLE_PER_BYTE; - NibbleSlice::::new_offset(data, start_padding) - }*/ /// helper function for getting slice from `NodeKey` stored in nodes pub fn from_stored(i: &(usize,ElasticArray36)) -> NibbleSlice { NibbleSlice::::new_offset(&i.1[..], i.0) } /// helper function to get `NodeKey` stored in nodes - pub fn to_stored(&self) -> (usize,ElasticArray36) { + pub fn to_stored(&self) -> NodeKey { let split = self.offset / N::NIBBLE_PER_BYTE; let offset = N::nb_padding(self.len()); (offset, self.data[split..].into()) } /// helper function to get `NodeKey` stored in nodes, warning slow - pub fn to_stored_range(&self, nb: usize) -> (usize,ElasticArray36) { - if nb == self.len() { return self.to_stored() } -/* TODO EMCH (commented thing is shit) if nb % N::NIBBLE_PER_BYTE == 0 { - let split = self.offset / 2; - let end = self.data.len() - (self.len() - (nb / N::NIBBLE_PER_BYTE)); - return (self.len(), self.data[split..end].into()) - }*/ - let mut ea = ElasticArray36::new(); - let iter = self.range_iter(nb); - for i in iter { - ea.push(i); + pub fn to_stored_range(&self, nb: usize) -> NodeKey { + if nb >= self.len() { return self.to_stored() } + if (self.offset + nb) % N::NIBBLE_PER_BYTE == 0 { + // aligned + let start = self.offset / N::NIBBLE_PER_BYTE; + let end = self.offset + nb / N::NIBBLE_PER_BYTE; + (N::nb_padding(nb), ElasticArray36::from_slice(&self.data[start..end])) + } else { + let start = self.offset / N::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; + let ea = ElasticArray36::from_slice(&self.data[start..=end]); + let n_offset = N::nb_padding(nb); + if n_offset == 1 { + let mut result = (0, ea); + super::triedbmut::shift_key::(&mut result, 1); + result.1.pop(); + result + } else { + let mut result = (1, ea); + super::triedbmut::shift_key::(&mut result, 0); + result.1.pop(); + result + } } - - (N::nb_padding(nb), ea) } /// Is this an empty slice? @@ -301,23 +269,23 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } - /// return encoded value as an iterator + /// return encoded value as an iterator pub fn right_iter(&'a self) -> impl Iterator + 'a { - let (mut first, sl) = self.right(); - let mut ix = 0; + let (mut first, sl) = self.right(); + let mut ix = 0; ::std::iter::from_fn( move || { - if first.is_some() { - first.take() - } else { - if ix < sl.len() { - ix += 1; - Some(sl[ix - 1]) - } else { - None - } - } - }) - } + if first.is_some() { + first.take() + } else { + if ix < sl.len() { + ix += 1; + Some(sl[ix - 1]) + } else { + None + } + } + }) + } /// return left of key nibble pub fn left(&'a self) -> (&'a [u8], Option) { @@ -329,15 +297,15 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } pub fn left_owned(&'a self) -> (ElasticArray36, Option) { - let (a, b) = self.left(); - (a.into(), b) + let (a, b) = self.left(); + (a.into(), b) } /// get iterator over slice, slow /// TODO switch to padded access as in right padded eg with a move end function that return self /// and a padding len - /// TODO rename to mak it explicit that it works on encoded byte (not returning nibble) + /// TODO rename to mak it explicit that it works on encoded byte (not returning nibble) pub fn range_iter(&'a self, to: usize) -> impl Iterator + 'a { let mut first = to % 2; let aligned_i = (self.offset + to) % 2; diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index 06b0fdd3..c5ac9a14 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -16,6 +16,8 @@ use elastic_array::ElasticArray36; use nibbleslice::NibbleSlice; use nibbleslice::NibbleOps; +use hash_db::Prefix; +use node_codec::Partial; use ::core_::marker::PhantomData; // TODO EMCH change crate layout to give access to nibble vec field to nibble ops and avoid pub(crate) @@ -58,12 +60,85 @@ impl NibbleVec { /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. pub fn push(&mut self, nibble: u8) { - N::push(self, nibble) + let nibble = nibble & 0x0F; + + if self.len % 2 == 0 { + self.inner.push(nibble << 4); + } else { + *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + + self.len += 1; } /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { - N::pop(self) + if self.is_empty() { + return None; + } + + let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if self.len % 2 == 0 { + self.inner.push(byte & 0xF0); + byte & 0x0F + } else { + byte >> 4 + }; + + self.len -= 1; + Some(nibble) + } + + /// truncate n last nibbles. + pub fn truncate(&mut self, mov: usize) { + if mov == 0 { return; } + if mov >= self.len { + self.clear(); + return; + } + let nb_rem = if (self.len - mov) % N::NIBBLE_PER_BYTE > 0 { + mov / N::NIBBLE_PER_BYTE + + } else { + (mov + 1) / N::NIBBLE_PER_BYTE + + }; + (0..nb_rem).for_each(|_|{ self.inner.pop(); }); + self.len -= mov; + if self.len % 2 == 1 { + let kl = self.inner.len() - 1; + self.inner[kl] &= 255 << 4; + } + } + + /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). + pub fn as_prefix(&self) -> Prefix { + let split = self.len / 2; + if self.len % 2 == 1 { + (&self.inner[..split], Some(self.inner[split] & (255 << 4))) + } else { + (&self.inner[..split], None) + } + } + + /// push a full partial. + pub fn append_partial(&mut self, (o_n, sl): Partial) { + if let Some(nibble) = o_n { + self.push(nibble) + } + let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; + if pad == 0 { + self.inner.append_slice(&sl[..]); + } else { + let kend = self.inner.len() - 1; + if sl.len() > 0 { + self.inner[kend] &= 255 << 4; + self.inner[kend] |= sl[0] >> 4; + (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << 4 | sl[i+1]>>4)); + self.inner.push(sl[sl.len() - 1] << 4); + } + } + self.len += sl.len() * N::NIBBLE_PER_BYTE; } /// Get the underlying byte slice. @@ -113,4 +188,29 @@ mod tests { assert_eq!(v.len(), i as usize); } } + + #[test] + fn truncate_test() { + let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { + let mut k = NibbleVec::::new(); + for v in a { + k.push(*v); + } + k.truncate(b); + assert_eq!((&k.inner[..], k.len), c); + }; + test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 4)); + test_trun(&[1,2,3,4], 1, (&[0x12, 0x30], 3)); + test_trun(&[1,2,3,4], 2, (&[0x12], 2)); + test_trun(&[1,2,3,4], 3, (&[0x10], 1)); + test_trun(&[1,2,3,4], 4, (&[], 0)); + test_trun(&[1,2,3,4], 5, (&[], 0)); + test_trun(&[1,2,3], 0, (&[0x12, 0x30], 3)); + test_trun(&[1,2,3], 1, (&[0x12], 2)); + test_trun(&[1,2,3], 2, (&[0x10], 1)); + test_trun(&[1,2,3], 3, (&[], 0)); + test_trun(&[1,2,3], 4, (&[], 0)); + } + + } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 10710246..2b033680 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -19,7 +19,8 @@ use node_codec::NodeCodec; use super::lookup::Lookup; use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; use ::core_::marker::PhantomData; -use triedbmut::{PartialKeyMut, concat_key_clone}; +use triedbmut::{concat_key_clone}; +use super::nibblevec::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; #[cfg(feature = "std")] @@ -146,7 +147,7 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: PartialKeyMut, + partial_key: NibbleVec, index: Option, } @@ -247,7 +248,7 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: PartialKeyMut::new(), + partial_key: NibbleVec::new(), index: None, }) .finish() diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 28410f6d..b66c4d15 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -16,6 +16,7 @@ use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; use super::lookup::Lookup; +use super::nibblevec::NibbleVec; use super::node::Node as EncodedNode; use node_codec::{NodeCodec, Partial}; use super::{DBValue, node::NodeKey}; @@ -112,98 +113,6 @@ impl<'key, N: NibbleOps> PartialKey<'key, N> { } -#[derive(Debug, Clone)] -pub(crate) struct PartialKeyMut { - key: ElasticArray36, - pad: usize, - marker: PhantomData, -} -// TODO EMCH almost replaceble with nibblevec -impl PartialKeyMut { - pub(crate) fn new() -> PartialKeyMut { - PartialKeyMut { - key: Default::default(), - pad: 0, - marker: Default::default(), - } - } - - /// truncate n last nibbles. - pub(crate) fn truncate(&mut self, mov: usize) { - if mov == 0 { return; } - if mov >= self.key.len() * N::NIBBLE_PER_BYTE - self.pad { - self.clear(); - return; - } - let nb_rem = if (self.pad + mov) % N::NIBBLE_PER_BYTE > 0 { - self.pad = 1; - mov / N::NIBBLE_PER_BYTE - - } else { - self.pad = 0; - (mov + 1) / N::NIBBLE_PER_BYTE - - }; - (0..nb_rem).for_each(|_|{ self.key.pop(); }); - if self.pad == 1 { - let kl = self.key.len() - 1; - self.key[kl] &= 255 << 4; - } - } - - /// clear - pub fn clear(&mut self) { - self.key.clear(); - self.pad = 0; - } - - - /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). - pub(crate) fn as_prefix(&self) -> Prefix { - // TODO EMCH very similar to left fn -> put as much as possible in nibbleops - let offset = self.key.len() * N::NIBBLE_PER_BYTE - self.pad; - let split = offset / 2; - if offset % 2 == 1 { - (&self.key[..split], Some(self.key[split] & (255 << 4))) - } else { - (&self.key[..split], None) - } - } - - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - pub(crate) fn push(&mut self, nibble: u8) { - // TODO EMCH move to N - let nibble = nibble & 0x0F; - - if self.pad == 0 { - self.key.push(nibble << 4); - self.pad = N::NIBBLE_PER_BYTE - 1; - } else { - *self.key.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - self.pad -= 1; - } - } - - /// push a full partial. - pub(crate) fn append_partial(&mut self, (o_n, sl): Partial) { - if let Some(nibble) = o_n { - self.push(nibble) - } - if self.pad == 0 { - self.key.append_slice(&sl[..]); - } else { - let kend = self.key.len() - 1; - if sl.len() > 0 { - self.key[kend] &= 255 << 4; - self.key[kend] |= sl[0] >> 4; - (0..sl.len() - 1).for_each(|i|self.key.push(sl[i] << 4 | sl[i+1]>>4)); - self.key.push(sl[sl.len() - 1] << 4); - } - } - } -} - - /// Node types in the Trie. #[derive(Debug)] enum Node { @@ -1287,7 +1196,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let mut k = PartialKeyMut::new(); + let mut k = NibbleVec::new(); let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { let mov = concat_key(&mut k, o_sl, o_ix); let cr = self.commit_child(child, &mut k); @@ -1313,7 +1222,7 @@ where /// case where we can fit the actual data in the `Hasher`s output type, we /// store the data inline. This function is used as the callback to the /// `into_encoded` method of `Node`. - fn commit_child(&mut self, handle: NodeHandle>, prefix: &mut PartialKeyMut) -> ChildReference> { + fn commit_child(&mut self, handle: NodeHandle>, prefix: &mut NibbleVec) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), NodeHandle::InMemory(storage_handle) => { @@ -1358,7 +1267,7 @@ where // TODO EMCH change usage here to run on self buffer // TODO EMCH a with_concat_key function using a closure and truncating correctly -pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { +pub(crate) fn concat_key(prefix: &mut NibbleVec, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { let mut res = 0; if let Some(sl) = o_sl { prefix.append_partial(sl.right()); @@ -1371,7 +1280,7 @@ pub(crate) fn concat_key(prefix: &mut PartialKeyMut, o_sl: Opti res } -pub(crate) fn concat_key_clone(prefix: &PartialKeyMut, o_sl: Option<&NibbleSlice>, o_ix: Option) -> PartialKeyMut { +pub(crate) fn concat_key_clone(prefix: &NibbleVec, o_sl: Option<&NibbleSlice>, o_ix: Option) -> NibbleVec { let mut p = prefix.clone(); concat_key(&mut p, o_sl, o_ix); p @@ -1455,6 +1364,7 @@ where } } +/// TODO EMCH Consider moving to NibbleOps fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; let _shifted = shift_key::(start, final_ofset); @@ -1468,7 +1378,8 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); } -fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { +/// TODO EMCH Consider moving to NibbleOps +pub(crate) fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { let old = key.0; key.0 = ofset; if old > ofset { @@ -1922,27 +1833,4 @@ mod tests { test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); } - #[test] - fn truncate_test() { - let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { - let mut k = super::PartialKeyMut::::new(); - for v in a { - k.push(*v); - } - k.truncate(b); - assert_eq!((&k.key[..], k.pad), c); - }; - test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 0)); - test_trun(&[1,2,3,4], 1, (&[0x12, 0x30], 1)); - test_trun(&[1,2,3,4], 2, (&[0x12], 0)); - test_trun(&[1,2,3,4], 3, (&[0x10], 1)); - test_trun(&[1,2,3,4], 4, (&[], 0)); - test_trun(&[1,2,3,4], 5, (&[], 0)); - test_trun(&[1,2,3], 0, (&[0x12, 0x30], 1)); - test_trun(&[1,2,3], 1, (&[0x12], 0)); - test_trun(&[1,2,3], 2, (&[0x10], 1)); - test_trun(&[1,2,3], 3, (&[], 0)); - test_trun(&[1,2,3], 4, (&[], 0)); - } - } From ca950b9209c447fa93d1dfc0e1904d15fc0b7ada Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Tue, 23 Apr 2019 11:50:24 +0200 Subject: [PATCH 040/120] ref padding constant --- test-support/reference-trie/src/lib.rs | 45 ++++--- trie-db/src/iter_build.rs | 4 +- trie-db/src/lib.rs | 2 +- trie-db/src/nibbleslice.rs | 156 ++++++++++++------------- trie-db/src/nibblevec.rs | 6 +- trie-db/src/triedbmut.rs | 5 +- 6 files changed, 105 insertions(+), 113 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 1b017fb7..46933569 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -39,7 +39,7 @@ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; -pub use trie_db::{Record, TrieLayOut, NibblePreHalf, NibbleOps}; +pub use trie_db::{Record, TrieLayOut, NibbleHalf, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum @@ -49,7 +49,7 @@ impl TrieLayOut for LayoutOri { const USE_EXTENSION: bool = true; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodec; - type N = NibblePreHalf; + type N = NibbleHalf; } /// trie layout similar to substrate one @@ -59,7 +59,7 @@ impl TrieLayOut for LayoutNew { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; - type N = NibblePreHalf; + type N = NibbleHalf; } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; @@ -511,7 +511,7 @@ fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if let Some(v) = partial.0 { - output.push((v & N::PADDING_BITMASK[nb_nibble_hpe - 1]) + N::PADDING_VALUE); + output.push(v & N::PADDING_BITMASK[nb_nibble_hpe].0); } output.extend_from_slice(&partial.1[..]); output @@ -602,7 +602,7 @@ impl NodeCodec for ReferenceNodeCodec { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! + N::nb_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -610,7 +610,7 @@ impl NodeCodec for ReferenceNodeCodec { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! + N::nb_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -700,13 +700,13 @@ impl NodeCodec for ReferenceNodeCodecNoExt { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { + if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe].0 != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! + N::nb_padding(nibble_count)); let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -727,13 +727,13 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } NodeHeaderNoExt::Leaf(nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe - 1] != 0 { + if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe].0 != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_count % N::NIBBLE_PER_BYTE); // TODO EMCH incorrect for non half padding! + N::nb_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -810,7 +810,7 @@ pub fn compare_impl + Eq> ( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -852,7 +852,7 @@ pub fn compare_root( ) { let root_new = { let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -872,7 +872,7 @@ pub fn compare_unhashed( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed(data); @@ -885,8 +885,7 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - // TODO EMCH siwtch this to post and implement post on ref_trie_root_unhashed_no_ext!! - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -905,7 +904,7 @@ pub fn calc_root( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -918,7 +917,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -929,7 +928,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -985,7 +984,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -1052,13 +1051,13 @@ pub fn compare_no_ext_insert_remove( assert_eq!(*t.root(), calc_root_no_ext(data2)); } -// TODO to big is currently truncate, keep it that way?? +// TODO EMCH to big is currently truncate, keep it that way?? wait for final spec #[test] fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node((None,&input), (&[1])); - let dec = >::decode(&enc).unwrap(); + let enc = >::leaf_node((None,&input), (&[1])); + let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) } else { None }; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 202b2f91..b2745af8 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -232,7 +232,7 @@ where if let Some(nkeyix) = nkey { let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); - let nib = pr.range_iter(nkeyix.1); + let nib = pr.right_range_iter(nkeyix.1); let encoded = C::ext_node(nib, nkeyix.1, branch_hash); let h = cb_ext.process(pr.left(), encoded, is_root); h @@ -256,7 +256,7 @@ where let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); let encoded = C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); - pr.range_iter(nkeyix.1), + pr.right_range_iter(nkeyix.1), nkeyix.1, self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index d0563b6a..c0e39e9c 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -96,7 +96,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibblePreHalf}; +pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibbleHalf}; pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 84b3d222..0d923734 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -29,49 +29,62 @@ const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Note that some function are defined here but ideally it should just be a set of /// constant (with function handling all constant case). pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { - /// variant repr - const REPR : ByteLayout; - /// Number of bit per nibble - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); - /// Number of nibble per byte - const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; - /// Number of nibble per node (must be power of 2 and under 256) - const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); - /// padding value to apply (default to 0) - const PADDING_VALUE: u8 = 0; - /// padding bitmasks (could be calculated with a constant function). - const PADDING_BITMASK: &'static [u8]; - + /// variant repr + const REPR : ByteLayout; + /// Number of bit per nibble + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); + /// Number of nibble per byte + const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; + /// Number of nibble per node (must be power of 2 and under 256) + const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); + /// padding bitmasks (could be calculated with a constant function). + /// First is bit mask to apply, second is right shift needed. + /// TODO EMCH check that array act as constant + const PADDING_BITMASK: &'static [(u8, usize)]; + + /// Try to get the nibble at the given offset. #[inline] - /// Number of padding needed for a length `i`. - fn nb_padding(i: usize) -> usize { - // TODO bench something faster - (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE + fn vec_at(s: &NibbleVec, idx: usize) -> u8 { + let ix = idx / Self::NIBBLE_PER_BYTE; + let pad = idx % Self::NIBBLE_PER_BYTE; + (s.inner[ix] & Self::PADDING_BITMASK[pad].0) + >> Self::PADDING_BITMASK[pad].1 + } + + /// Get the nibble at position `i`. + #[inline(always)] + fn at(s: &NibbleSlice, i: usize) -> u8 { + let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; + let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; + (s.data[ix] & Self::PADDING_BITMASK[pad].0) + >> Self::PADDING_BITMASK[pad].1 } - /// Get the nibble at position `i`. - fn at(&NibbleSlice, i: usize) -> u8; - /// Try to get the nibble at the given offset. - fn vec_at(s: &NibbleVec, idx: usize) -> u8; + #[inline] + /// Number of padding needed for a length `i`. + fn nb_padding(i: usize) -> usize { + // TODO bench something faster + (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE + } } /// half byte nibble prepend encoding #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] -pub struct NibblePreHalf; +pub struct NibbleHalf; /// Type of nibble in term of byte size #[repr(usize)] pub enum ByteLayout { - /// nibble of one bit length - Bit = 0, // 1, 8, 2 - /// nibble of a quarter byte length - Quarter = 1, // 2, 4, 4 - /// nibble of a half byte length - Half = 2, // 4, 2, 16 - /// nibble of one byte length - Full = 3, // 8, 1, 256 + /// nibble of one bit length + Bit = 0, // 1, 8, 2 + /// nibble of a quarter byte length + Quarter = 1, // 2, 4, 4 + /// nibble of a half byte length + Half = 2, // 4, 2, 16 + /// nibble of one byte length + Full = 3, // 8, 1, 256 } /// `()` with a conversion to 0 @@ -79,36 +92,30 @@ pub enum ByteLayout { pub struct Empty; impl Into for Empty { - fn into(self) -> usize { 0 } + fn into(self) -> usize { 0 } } -// TODO EMCH some method can be fuse with post half (see bug solving: eg new_offset and -// new_padded_end merged -impl NibbleOps for NibblePreHalf { - const REPR: ByteLayout = ByteLayout::Half; - const PADDING_BITMASK: &'static [u8] = &[0x0f]; - - #[inline(always)] - fn at(s: &NibbleSlice, i: usize) -> u8 { - if (s.offset + i) & 1 == 1 { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] & 15u8 - } else { - s.data[(s.offset + i) / Self::NIBBLE_PER_BYTE] >> 4 - } - } +impl NibbleOps for NibbleHalf { + const REPR: ByteLayout = ByteLayout::Half; + const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; +} - #[inline] - fn vec_at(s: &NibbleVec, idx: usize) -> u8 { - if idx % 2 == 0 { - s.inner[idx / 2] >> 4 - } else { - s.inner[idx / 2] & 0x0F - } - } +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +pub struct NibbleQuarter; +// new_padded_end merged +impl NibbleOps for NibbleQuarter { + const REPR: ByteLayout = ByteLayout::Quarter; + const PADDING_BITMASK: &'static [(u8, usize)] = &[ + (0b1111_1111, 6), + (0b0011_1111, 4), + (0b0000_1111, 2), + (0b0000_0011, 0), + ]; } + /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// /// This is an immutable struct. No operations actually change it. @@ -303,10 +310,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// get iterator over slice, slow - /// TODO switch to padded access as in right padded eg with a move end function that return self - /// and a padding len - /// TODO rename to mak it explicit that it works on encoded byte (not returning nibble) - pub fn range_iter(&'a self, to: usize) -> impl Iterator + 'a { + pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { let mut first = to % 2; let aligned_i = (self.offset + to) % 2; let aligned = aligned_i == 0; @@ -437,14 +441,14 @@ impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { #[cfg(test)] mod tests { use super::NibbleSlice; - use super::NibblePreHalf; + use super::NibbleHalf; use super::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; #[test] fn basics() { - basics_inner::(); + basics_inner::(); } fn basics_inner() { let n = NibbleSlice::::new(D); @@ -463,7 +467,7 @@ mod tests { #[test] fn iterator() { - iterator_inner::(); + iterator_inner::(); } fn iterator_inner() { let n = NibbleSlice::::new(D); @@ -474,7 +478,7 @@ mod tests { #[test] fn mid() { - mid_inner::(); + mid_inner::(); } fn mid_inner() { let n = NibbleSlice::::new(D); @@ -490,8 +494,8 @@ mod tests { #[test] fn encoded_pre() { - let n = NibbleSlice::::new(D); - let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + let n = NibbleSlice::::new(D); + let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; assert_eq!(n.to_stored(), (0, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(1).to_stored(), (1, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(2).to_stored(), (0, ElasticArray36::from_slice(&[0x23, 0x45]))); @@ -515,16 +519,16 @@ mod tests { #[test] fn from_encoded_pre() { - let n = NibbleSlice::::new(D); - let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + let n = NibbleSlice::::new(D); + let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; let stored: ElasticArray36 = [0x01, 0x23, 0x45][..].into(); assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); } #[test] fn range_iter() { - let n = NibbleSlice::::new(D); - let len = D.len() * NibblePreHalf::NIBBLE_PER_BYTE; + let n = NibbleSlice::::new(D); + let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; for i in [ vec![], vec![0x00], @@ -567,26 +571,16 @@ mod tests { } - fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { + fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { let n = if let Some(i) = mid { n.mid(i) } else { n }; - assert_eq!(&n.range_iter(nb).collect::>()[..], res); + assert_eq!(&n.right_range_iter(nb).collect::>()[..], res); } -/* #[test] - fn from_encoded_post() { - let n = NibbleSlice::::new(D); - assert_eq!((n, false), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x00])); - assert_eq!((n, true), NibbleSlice::from_encoded(&[0x01, 0x23, 0x45, 0x02])); - assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x12, 0x34, 0x51])); - assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x12, 0x34, 0x53])); - }*/ - - #[test] fn shared() { - shared_inner::(); + shared_inner::(); } fn shared_inner() { let n = NibbleSlice::::new(D); @@ -605,7 +599,7 @@ mod tests { #[test] fn compare() { - compare_inner::(); + compare_inner::(); } fn compare_inner() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index c5ac9a14..c5c6beb3 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -168,11 +168,11 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { use super::NibbleVec; - use nibbleslice::{NibblePreHalf, NibbleOps}; + use nibbleslice::{NibbleHalf, NibbleOps}; #[test] fn push_pop() { - push_pop_inner::(); + push_pop_inner::(); } fn push_pop_inner() { let mut v = NibbleVec::::new(); @@ -192,7 +192,7 @@ mod tests { #[test] fn truncate_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { - let mut k = NibbleVec::::new(); + let mut k = NibbleVec::::new(); for v in a { k.push(*v); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index b66c4d15..f86e213f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -70,8 +70,7 @@ fn empty_children() -> Box<[Option>; 16]> { None, None, None, None, None, None, None, None, ]) } -// TODO rem? not sure using a different name makes it explicit we are on a full key. -// therefore on left aligned slice + #[derive(Clone, Debug)] pub(crate) struct PartialKey<'key, N: NibbleOps> { key: NibbleSlice<'key, N>, @@ -1824,7 +1823,7 @@ mod tests { let b: &[u8] = [0x56, 0x78][..].into(); let test_comb = |a: (_,&ElasticArray36<_>), b, c| { let mut a = (a.0,a.1.clone()); - super::combine_key::(&mut a, b); + super::combine_key::(&mut a, b); assert_eq!((a.0,&a.1[..]), c); }; test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); From ba7de315b2c12d99f4189d629e5a467ec36d3c63 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Tue, 23 Apr 2019 12:24:22 +0200 Subject: [PATCH 041/120] remove PartialKey (same as NibbleSlice in this state). --- trie-db/src/nibbleslice.rs | 8 +++- trie-db/src/triedb.rs | 2 +- trie-db/src/triedbmut.rs | 81 +++++++++++--------------------------- 3 files changed, 30 insertions(+), 61 deletions(-) diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 0d923734..d9a7079a 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -21,6 +21,7 @@ use nibblevec::NibbleVec; use elastic_array::ElasticArray36; use node::NodeKey; use node_codec::Partial; +use hash_db::Prefix; pub const EMPTY_ENCODED: (&'static [u8], Option) = (&[], None); // until const fn for pow @@ -242,6 +243,11 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { marker: PhantomData, } } + /// Advance the view on the slice by `i` nibbles + pub fn advance(&mut self, i: usize) { + debug_assert!(self.len() >= i); + self.offset += i; + } /// Return object to an offset position pub fn back(&self, i: usize) -> NibbleSlice<'a, N> { NibbleSlice { @@ -295,7 +301,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// return left of key nibble - pub fn left(&'a self) -> (&'a [u8], Option) { + pub fn left(&'a self) -> Prefix { let split = self.offset / 2; if self.len() % 2 == 1 { (&self.data[..split], Some(self.data[split] & (255 << 4))) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 2b033680..a2f57c07 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{Hasher, HashDBRef, Prefix}; +use hash_db::{HashDBRef, Prefix}; use nibbleslice::{self, NibbleSlice, NibbleOps}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index f86e213f..aaa1cc90 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -71,46 +71,9 @@ fn empty_children() -> Box<[Option>; 16]> { ]) } -#[derive(Clone, Debug)] -pub(crate) struct PartialKey<'key, N: NibbleOps> { - key: NibbleSlice<'key, N>, -} - -impl<'key, N: NibbleOps> PartialKey<'key, N> { - fn new(key: NibbleSlice) -> PartialKey { - PartialKey { - key, - } - } - - fn advance(&mut self, by: usize) { - - assert!(self.key.len() >= by); - self.key = self.key.mid(by) - } - - /// advance of return none if underlying slice is to short - fn checked_advance(&mut self, by: usize) -> bool { - if self.key.len() >= by { - self.key = self.key.mid(by); - true - } else { false } - } - - fn mid(&self) -> NibbleSlice<'key, N> { - self.key - } - - fn encoded_prefix(&self) -> Prefix { - self.key.left() - } - - fn encoded_prefix_owned(&self) -> (ElasticArray36, Option) { - let (a, b) = self.encoded_prefix(); - (a.into(), b) - } - -} +/// type alias to indicate the nible cover a full key, +/// and left side therefore is a full prefix. +type NibbleFullKey<'key, N> = NibbleSlice<'key, N>; /// Node types in the Trie. #[derive(Debug)] @@ -454,8 +417,8 @@ where // inspect a node, choosing either to replace, restore, or delete it. // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored>, key: &mut PartialKey, inspector: F) -> Result>, bool)>, TrieHash, CError> - where F: FnOnce(&mut Self, Node>, &mut PartialKey) -> Result>, TrieHash, CError> { + fn inspect(&mut self, stored: Stored>, key: &mut NibbleFullKey, inspector: F) -> Result>, bool)>, TrieHash, CError> + where F: FnOnce(&mut Self, Node>, &mut NibbleFullKey) -> Result>, TrieHash, CError> { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -465,11 +428,11 @@ where Stored::Cached(node, hash) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::Cached(node, hash), false)), Action::Replace(node) => { - self.death_row.insert((hash, key.encoded_prefix_owned())); + self.death_row.insert((hash, key.left_owned())); Some((Stored::New(node), true)) } Action::Delete => { - self.death_row.insert((hash, key.encoded_prefix_owned())); + self.death_row.insert((hash, key.left_owned())); None } }, @@ -539,10 +502,10 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle>, key: &mut PartialKey, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { + fn insert_at(&mut self, handle: NodeHandle>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, - NodeHandle::Hash(h) => self.cache(h, key.encoded_prefix())?, + NodeHandle::Hash(h) => self.cache(h, key.left())?, }; let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { @@ -553,8 +516,8 @@ where } /// the insertion inspector. - fn insert_inspector(&mut self, node: Node>, key: &mut PartialKey, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { - let partial = key.mid(); + fn insert_inspector(&mut self, node: Node>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { + let partial = key.clone(); trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); Ok(match node { @@ -588,7 +551,7 @@ where } } else { // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().to_stored(), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.to_stored(), value))); children[idx] = Some(leaf.into()); } @@ -657,7 +620,7 @@ where } } else { // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().to_stored(), value))); + let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.to_stored(), value))); children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::NibbledBranch( @@ -810,11 +773,11 @@ where } /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle>, key: &mut PartialKey, old_val: &mut Option) -> Result, TrieHash, CError> { + fn remove_at(&mut self, handle: NodeHandle>, key: &mut NibbleFullKey, old_val: &mut Option) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, key.encoded_prefix())?; + let handle = self.cache(h, key.left())?; self.storage.destroy(handle) } }; @@ -825,8 +788,8 @@ where } /// the removal inspector - fn remove_inspector(&mut self, node: Node>, key: &mut PartialKey, old_val: &mut Option) -> Result>, TrieHash, CError> { - let partial = key.mid(); + fn remove_inspector(&mut self, node: Node>, key: &mut NibbleFullKey, old_val: &mut Option) -> Result>, TrieHash, CError> { + let partial = key.clone(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), @@ -973,7 +936,7 @@ where /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node>, key: PartialKey) -> Result>, TrieHash, CError> { + fn fix(&mut self, node: Node>, key: NibbleSlice) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1047,7 +1010,7 @@ where let mut kc = key.clone(); kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); - let (st, ost, op) = match kc.encoded_prefix() { + let (st, ost, op) = match kc.left() { (st, Some(v)) => { let mut so: ElasticArray36 = st.into(); so.push((v & (255 << 4)) | a); @@ -1108,7 +1071,7 @@ where let a = partial.1[partial.1.len() - 1] & (255 >> 4); let mut kc = key.clone(); kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); - let (st, ost, op) = match kc.encoded_prefix() { + let (st, ost, op) = match kc.left() { (st, Some(v)) => { let mut so: ElasticArray36 = st.into(); so.push((v & (255 << 4)) | a); @@ -1320,7 +1283,7 @@ where let root_handle = self.root_handle(); let (new_handle, changed) = self.insert_at( root_handle, - &mut PartialKey::new(NibbleSlice::new(key)), + &mut NibbleSlice::new(key), DBValue::from_slice(value), &mut old_val, )?; @@ -1335,7 +1298,7 @@ where trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); - let mut key = PartialKey::new(NibbleSlice::new(key)); + let mut key = NibbleSlice::new(key); let mut old_val = None; match self.remove_at(root_handle, &mut key, &mut old_val)? { From 5de3fbe74d1b1e5ac22b95cefaf9c902cc938d12 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Tue, 23 Apr 2019 13:01:04 +0200 Subject: [PATCH 042/120] no ext error from playpen --- test-support/reference-trie/src/lib.rs | 4 +- trie-db/src/iter_build.rs | 80 +++++++++++++++++++++++++- trie-db/src/lookup.rs | 2 +- trie-db/src/nibbleslice.rs | 4 +- trie-db/src/triedb.rs | 4 -- trie-db/src/triedbmut.rs | 50 +++++++++++++++- 6 files changed, 130 insertions(+), 14 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 46933569..8facffcd 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -115,7 +115,7 @@ const EXTENSION_NODE_OVER: u8 = BRANCH_NODE_NO_VALUE - EXTENSION_NODE_OFFSET; const LEAF_NODE_LAST: u8 = EXTENSION_NODE_OFFSET - 1; const EXTENSION_NODE_LAST: u8 = BRANCH_NODE_NO_VALUE - 1; -mod noext_cst { +/*mod noext_cst { pub const EMPTY_TRIE: u8 = 0; pub const LEAF_NODE_OFFSET: u8 = 1; pub const LEAF_NODE_BIG: u8 = 85; @@ -129,7 +129,7 @@ mod noext_cst { pub const LEAF_NODE_LAST: u8 = LEAF_NODE_BIG - 1; pub const BRANCH_NODE_WITH_VALUE_LAST: u8 = BRANCH_NODE_WITH_VALUE_BIG - 1; pub const BRANCH_NODE_NO_VALUE_LAST: u8 = BRANCH_NODE_NO_VALUE_BIG - 1; -} +}*/ /// constant use with trie simplification codec mod s_cst { diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index b2745af8..ece4cdf1 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -19,7 +19,7 @@ use std::marker::PhantomData; use crate::triedbmut::{ChildReference}; use crate::nibbleslice::NibbleSlice; use crate::nibbleslice::NibbleOps; -use node_codec::{NodeCodec, Partial}; +use node_codec::NodeCodec; // TODO EMCH use L instead of HC (aka TrieLayout) // TODO EMCH move to NibbleOps to use right constants @@ -672,7 +672,83 @@ mod test { (vec![0],vec![0, 0]), ]); } - + #[test] + fn fuzz_noext4 () { + compare_impl_no_ext_unordered(vec![ +(vec![ + 0x50, + 0x4d, + 0x59, + 0x43, + 0x4c, +], vec![ + 0x0, + 0x0, + 0x0, + 0x0, +]), +(vec![ + 0x4a, + 0x56, + 0x4b, + 0x45, + 0x4f, +], vec![ + 0x1, + 0x0, + 0x0, + 0x0, +]), +(vec![ + 0x4e, + 0x45, + 0x49, + 0x4a, + 0x45, +], vec![ + 0x2, + 0x0, + 0x0, + 0x0, +]), +(vec![ + 0x45, + 0x42, + 0x2f, + 0x55, + 0x51, +], vec![ + 0x3, + 0x0, + 0x0, + 0x0, +]), +(vec![ + 0x56, + 0x48, + 0x5d, + 0x51, + 0x47, +], vec![ + 0x4, + 0x0, + 0x0, + 0x0, +]), +(vec![ + 0x45, + 0x50, + 0x47, + 0x5a, + 0x4a, +], vec![ + 0x5, + 0x0, + 0x0, + 0x0, +]), +]); + } #[test] fn fuzz_noext_ins_rem_pref () { let data = vec![ diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 2599f29f..aa231c9e 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -14,7 +14,7 @@ //! Trie lookup via HashDB. -use hash_db::{HashDBRef, Hasher}; +use hash_db::HashDBRef; use nibbleslice::NibbleSlice; use node::Node; use node_codec::NodeCodec; diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index d9a7079a..689793b7 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -286,7 +286,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn right_iter(&'a self) -> impl Iterator + 'a { let (mut first, sl) = self.right(); let mut ix = 0; - ::std::iter::from_fn( move || { + ::core_::iter::from_fn( move || { if first.is_some() { first.take() } else { @@ -322,7 +322,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let aligned = aligned_i == 0; let mut ix = self.offset / 2; let ix_lim = (self.offset + to) / 2; - ::std::iter::from_fn( move || { + ::core_::iter::from_fn( move || { if aligned { if first > 0 { first = 0; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index a2f57c07..4b8b35ae 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -18,7 +18,6 @@ use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; -use ::core_::marker::PhantomData; use triedbmut::{concat_key_clone}; use super::nibblevec::NibbleVec; #[cfg(feature = "std")] @@ -33,7 +32,6 @@ use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use elastic_array::ElasticArray36; /// A `Trie` implementation using a generic `HashDB` backing database, a `Hasher` /// implementation to generate keys and a `NodeCodec` implementation to encode/decode @@ -447,10 +445,8 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead /// Encoded key for storage lookup fn encoded_key<'b>(&self, key: &'b Vec) -> (&'b [u8], Option) { - let slice = NibbleSlice::::new(&key); let nb_padd = self.key_nibbles.len() % 2; if nb_padd > 0 { - // TODO EMCH costy new_composed when slice build just above?? (&key[..], Some(self.key_nibbles[self.key_nibbles.len() - 1] & (255 << 4))) } else { (&key[..], None) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index aaa1cc90..204bb786 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -18,13 +18,12 @@ use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; use super::lookup::Lookup; use super::nibblevec::NibbleVec; use super::node::Node as EncodedNode; -use node_codec::{NodeCodec, Partial}; +use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; use hash_db::{HashDB, Hasher, Prefix}; use nibbleslice::{self, NibbleSlice, NibbleOps}; use elastic_array::ElasticArray36; -use ::core_::marker::PhantomData; use ::core_::mem; use ::core_::ops::Index; use ::core_::hash::Hash; @@ -1369,7 +1368,8 @@ mod tests { use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, - ReferenceNodeCodec, ref_trie_root, RefTrieDB, RefTrieDBNoExt, LayoutOri}; + ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, + RefTrieDB, RefTrieDBNoExt, LayoutOri, LayoutNew}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1458,8 +1458,52 @@ mod tests { } assert_eq!(*memtrie.root(), hashed_null_node); } + + // no_ext + let mut seed = Default::default(); + for test_i in 0..10 { + if test_i % 50 == 0 { + debug!("{:?} of 10000 stress tests done", test_i); + } + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: 5, + journal_key: 0, + value_mode: ValueMode::Index, + count: 5, + }.make_with(&mut seed); + + let real = ref_trie_root_no_ext(x.clone()); + let mut memdb = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + let mut memtrie = populate_trie_no_ext(&mut memdb, &mut root, &x); + + memtrie.commit(); + if *memtrie.root() != real { + println!("TRIE MISMATCH"); + println!(""); + println!("{:?} vs {:?}", memtrie.root(), real); + for i in &x { + println!("{:#x?} -> {:#x?}", i.0, i.1); + } + } + assert_eq!(*memtrie.root(), real); + unpopulate_trie_no_ext(&mut memtrie, &x); + memtrie.commit(); + let hashed_null_node = ::N>>::hashed_null_node(); + if *memtrie.root() != hashed_null_node { + println!("- TRIE MISMATCH"); + println!(""); + println!("{:#x?} vs {:#x?}", memtrie.root(), hashed_null_node); + for i in &x { + println!("{:#x?} -> {:#x?}", i.0, i.1); + } + } + assert_eq!(*memtrie.root(), hashed_null_node); + } } + #[test] fn init() { let mut memdb = MemoryDB::, DBValue>::default(); From cf2b5e37615d8905be1d015b05976827206f7cff Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Tue, 23 Apr 2019 19:11:09 +0200 Subject: [PATCH 043/120] Fix playpen error. --- trie-db/src/iter_build.rs | 81 +++----------------------------------- trie-db/src/nibbleslice.rs | 2 +- trie-db/src/triedbmut.rs | 2 +- 3 files changed, 8 insertions(+), 77 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index ece4cdf1..16cfdce3 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -674,81 +674,12 @@ mod test { } #[test] fn fuzz_noext4 () { - compare_impl_no_ext_unordered(vec![ -(vec![ - 0x50, - 0x4d, - 0x59, - 0x43, - 0x4c, -], vec![ - 0x0, - 0x0, - 0x0, - 0x0, -]), -(vec![ - 0x4a, - 0x56, - 0x4b, - 0x45, - 0x4f, -], vec![ - 0x1, - 0x0, - 0x0, - 0x0, -]), -(vec![ - 0x4e, - 0x45, - 0x49, - 0x4a, - 0x45, -], vec![ - 0x2, - 0x0, - 0x0, - 0x0, -]), -(vec![ - 0x45, - 0x42, - 0x2f, - 0x55, - 0x51, -], vec![ - 0x3, - 0x0, - 0x0, - 0x0, -]), -(vec![ - 0x56, - 0x48, - 0x5d, - 0x51, - 0x47, -], vec![ - 0x4, - 0x0, - 0x0, - 0x0, -]), -(vec![ - 0x45, - 0x50, - 0x47, - 0x5a, - 0x4a, -], vec![ - 0x5, - 0x0, - 0x0, - 0x0, -]), -]); - } + compare_impl_no_ext(vec![ + (vec![0x01, 0x56], vec![0x1]), + (vec![0x02, 0x42], vec![0x2]), + (vec![0x02, 0x50], vec![0x3]), + ]); + } #[test] fn fuzz_noext_ins_rem_pref () { let data = vec![ diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 689793b7..1cf7b759 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -201,7 +201,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { if (self.offset + nb) % N::NIBBLE_PER_BYTE == 0 { // aligned let start = self.offset / N::NIBBLE_PER_BYTE; - let end = self.offset + nb / N::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; (N::nb_padding(nb), ElasticArray36::from_slice(&self.data[start..end])) } else { let start = self.offset / N::NIBBLE_PER_BYTE; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 204bb786..22934a9a 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1470,7 +1470,7 @@ mod tests { min_key: 5, journal_key: 0, value_mode: ValueMode::Index, - count: 5, + count: 100, }.make_with(&mut seed); let real = ref_trie_root_no_ext(x.clone()); From 2e8cd6acf85a02a52a902a83d35718f98e3cd620 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Wed, 24 Apr 2019 14:44:00 +0200 Subject: [PATCH 044/120] fix some warnings --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/iter_build.rs | 6 --- trie-db/src/nibbleslice.rs | 62 -------------------------- trie-db/src/triedb.rs | 5 +-- trie-db/src/triedbmut.rs | 58 ++++++++++-------------- 5 files changed, 27 insertions(+), 106 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 8facffcd..af167d1e 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1056,7 +1056,7 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node((None,&input), (&[1])); + let enc = >::leaf_node((None,&input), &[1]); let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 16cfdce3..bbee2318 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -468,15 +468,9 @@ impl ProcessEncodedNode<::Out> for TrieRootUnhashed { #[cfg(test)] mod test { - use super::*; - use env_logger; - use standardmap::*; use DBValue; use memory_db::{MemoryDB, HashKey, PrefixedKey}; - use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; - use reference_trie::{RefTrieDBMut, RefTrieDB, Trie, TrieMut, - ReferenceNodeCodec, ref_trie_root}; #[test] fn trie_root_empty () { diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs index 1cf7b759..f2fea5c4 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibbleslice.rs @@ -353,56 +353,12 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } -// TODO EMCH not generic (not that partial type does not support nothing else than 4 byte) impl<'a, N: NibbleOps> Into for NibbleSlice<'a, N> { fn into(self) -> NodeKey { (self.offset, self.data.into()) } } -// TODO EMCH rename or enhanch + generalize (in NibbleOps) + use in a single place (cast from -// leaf) -pub fn into_part(inp: &NodeKey) -> Partial { - let start = inp.0 / 2; - if inp.0 % 2 > 0 { - (Some(inp.1[start]), - &inp.1[start + 1..]) - } else { - (None, &inp.1[start..]) - } -} - -#[test] -fn into_part_test() { - let v = [ - ((0, [0x12, 0x34][..].into()), - (None, &vec![0x12, 0x34][..])), - ((1, [0x12, 0x34][..].into()), - (Some(0x12), &vec![0x34][..])), - ((2, [0x12, 0x34][..].into()), - (None, &vec![0x34][..])), - ((3, [0x12, 0x34][..].into()), - (Some(0x34), &vec![][..])), - ]; - for nk in v.iter() { - assert_eq!(into_part(&nk.0), nk.1); - } -} -/* -// TODO EMCH use in prev into fn -impl<'a, N: NibbleOps> Into<(Option, &'a[u8])> for NibbleSlice<'a, N> { - fn into(self) -> (Option, &'a[u8]) { - if self.len() / 2 == 1 { - (Some(self.data[self.offset/2] & (255 >> 4)), - &self.data[self.offset/2 + 1..]) - } else { - (None, - &self.data[self.offset/2..]) - } - } -} -*/ - impl<'a, N: NibbleOps> PartialEq for NibbleSlice<'a, N> { fn eq(&self, them: &Self) -> bool { self.len() == them.len() && self.starts_with(them) @@ -501,32 +457,15 @@ mod tests { #[test] fn encoded_pre() { let n = NibbleSlice::::new(D); - let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; assert_eq!(n.to_stored(), (0, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(1).to_stored(), (1, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(2).to_stored(), (0, ElasticArray36::from_slice(&[0x23, 0x45]))); assert_eq!(n.mid(3).to_stored(), (1, ElasticArray36::from_slice(&[0x23, 0x45]))); } -/* - #[test] - fn encoded_post() { - let n = NibbleSlice::::new(D); - assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x01, 0x23, 0x45, 0x00])); - assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x01, 0x23, 0x45, 0x02])); - assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); - assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); - let n = NibbleSlice::::from_encoded(&[0x12, 0x34, 0x51]).0; // unaligned end - assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x12, 0x34, 0x51])); - assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x12, 0x34, 0x53])); - assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x23, 0x45, 0x00])); - assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x23, 0x45, 0x02])); - } -*/ #[test] fn from_encoded_pre() { let n = NibbleSlice::::new(D); - let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; let stored: ElasticArray36 = [0x01, 0x23, 0x45][..].into(); assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); @@ -534,7 +473,6 @@ mod tests { #[test] fn range_iter() { let n = NibbleSlice::::new(D); - let len = D.len() * NibbleHalf::NIBBLE_PER_BYTE; for i in [ vec![], vec![0x00], diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 4b8b35ae..b7673d7c 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -124,7 +124,7 @@ where fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key, { - Lookup:: { // TODO EMCH rem type + Lookup:: { db: self.db, query: query, hash: self.root.clone(), @@ -551,7 +551,7 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { #[cfg(test)] mod tests { - use memory_db::{MemoryDB, PrefixedKey, HashKey}; + use memory_db::{MemoryDB, PrefixedKey}; use keccak_hasher::KeccakHasher; use DBValue; use reference_trie::{RefTrieDB, RefTrieDBMut, RefLookup, Trie, TrieMut, NibbleSlice}; @@ -849,7 +849,6 @@ mod tests { #[test] fn test_lookup_with_corrupt_data_returns_decoder_error() { - use std::marker::PhantomData; let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 22934a9a..b95613e0 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -155,7 +155,7 @@ where } } - // TODO: parallelize TODO EMCH does not need to consume self: change that + // TODO: parallelize fn into_encoded(self, mut child_cb: F) -> Vec where N: NibbleOps, @@ -165,10 +165,11 @@ where { match self { Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => C::leaf_node(nibbleslice::into_part(&partial), &value), + Node::Leaf(partial, value) => { + let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + C::leaf_node(pr.right(), &value) + }, Node::Extension(partial, child) => { - // warning we know that partial does not use pop backward from this point, child_cb using pop - // here will break things TODO pop limited version of TrieDBMut?? let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); let c = child_cb(child, Some(&pr), None); @@ -1231,7 +1232,7 @@ where pub(crate) fn concat_key(prefix: &mut NibbleVec, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { let mut res = 0; if let Some(sl) = o_sl { - prefix.append_partial(sl.right()); + prefix.append_partial(sl.right()); res += sl.len(); } if let Some(ix) = o_ix { @@ -1363,13 +1364,13 @@ mod tests { use env_logger; use standardmap::*; use DBValue; - use memory_db::{MemoryDB, PrefixedKey, HashKey}; + use memory_db::{MemoryDB, PrefixedKey}; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - RefTrieDB, RefTrieDBNoExt, LayoutOri, LayoutNew}; + RefTrieDBNoExt, LayoutOri, LayoutNew}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1459,7 +1460,7 @@ mod tests { assert_eq!(*memtrie.root(), hashed_null_node); } - // no_ext + // no_ext let mut seed = Default::default(); for test_i in 0..10 { if test_i % 50 == 0 { @@ -1528,17 +1529,12 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root); - t1.insert(&[0x01, 0x23], big_value).unwrap(); - t1.insert(&[0x01, 0x34], big_value).unwrap(); - let mut memdb2 = MemoryDB::, DBValue>::default(); - let mut root2 = Default::default(); - let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2); - - t2.insert(&[0x01], big_value).unwrap(); - t2.insert(&[0x01, 0x23], big_value).unwrap(); - t2.insert(&[0x01, 0x34], big_value).unwrap(); - t2.remove(&[0x01]).unwrap(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + + t.insert(&[0x01], big_value).unwrap(); + t.insert(&[0x01, 0x23], big_value).unwrap(); + t.insert(&[0x01, 0x34], big_value).unwrap(); + t.remove(&[0x01]).unwrap(); } #[test] @@ -1547,24 +1543,18 @@ mod tests { let big_value2 = b"00000000000000000000000000000002"; let big_value3 = b"00000000000000000000000000000004"; - let mut memdb2 = MemoryDB::<_,PrefixedKey<_>,_>::default(); - let mut root2 = Default::default(); + let mut memdb = MemoryDB::<_,PrefixedKey<_>,_>::default(); + let mut root = Default::default(); { - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let mut root = Default::default(); - let mut t1 = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - //t1.insert(&[0x01, 0x23], big_value).unwrap(); - //t1.insert(&[0x01, 0x34], big_value).unwrap(); - let mut t2 = RefTrieDBMutNoExt::new(&mut memdb2, &mut root2); - - t2.insert(&[0x01, 0x23], big_value3).unwrap(); - t2.insert(&[0x01], big_value2).unwrap(); - t2.insert(&[0x01, 0x34], big_value).unwrap(); - t2.remove(&[0x01]).unwrap(); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + + t.insert(&[0x01, 0x23], big_value3).unwrap(); + t.insert(&[0x01], big_value2).unwrap(); + t.insert(&[0x01, 0x34], big_value).unwrap(); + t.remove(&[0x01]).unwrap(); // commit on drop } - let t2 = RefTrieDBNoExt::new(& memdb2, &root2); - assert_eq!(&root2[..], &reference_trie::calc_root_no_ext(vec![ + assert_eq!(&root[..], &reference_trie::calc_root_no_ext(vec![ (vec![0x01u8, 0x23], big_value3.to_vec()), (vec![0x01u8, 0x34], big_value.to_vec()), ])[..]); From 6c69177d8fa4cdda727b6c569f8ec00dfe6781e1 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Wed, 24 Apr 2019 15:39:23 +0200 Subject: [PATCH 045/120] use nibblevec instead of vec in iter, TODO still need remove a useless recurse calls (encoded_) --- trie-db/src/nibblevec.rs | 8 ++++---- trie-db/src/triedb.rs | 38 ++++++++++++++++++++++---------------- trie-db/src/triedbmut.rs | 4 ++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index c5c6beb3..8959339b 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -89,8 +89,8 @@ impl NibbleVec { Some(nibble) } - /// truncate n last nibbles. - pub fn truncate(&mut self, mov: usize) { + /// remove n last nibbles. + pub fn drop_lasts(&mut self, mov: usize) { if mov == 0 { return; } if mov >= self.len { self.clear(); @@ -190,13 +190,13 @@ mod tests { } #[test] - fn truncate_test() { + fn drop_lasts_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { let mut k = NibbleVec::::new(); for v in a { k.push(*v); } - k.truncate(b); + k.drop_lasts(b); assert_eq!((&k.inner[..], k.len), c); }; test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 4)); diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index b7673d7c..5ef99961 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -288,13 +288,13 @@ pub struct TrieDBIterator<'a, L: TrieLayOut> { db: &'a TrieDB<'a, L>, trail: Vec>, // TODO EMCH replace by niblleVec!!! - key_nibbles: Vec, + key_nibbles: NibbleVec, } impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { - let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: Vec::with_capacity(64) }; + let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: NibbleVec::new() }; db.root_data().and_then(|root_data| r.descend(&root_data))?; Ok(r) } @@ -321,7 +321,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { }); } - self.key_nibbles.extend(slice.iter()); + self.key_nibbles.append_partial(slice.right()); return Ok(()) }, Node::Extension(ref slice, ref item) => { @@ -330,7 +330,8 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { status: Status::At, node: node.clone().into(), }); - self.key_nibbles.extend(slice.iter()); + slice.iter().for_each(|i|self.key_nibbles.push(i)); + // TODO EMCH self.key_nibbles.append_partial(slice.right()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); let data = self.db.get_raw_or_lookup(&*item, key.back(full_key_nibbles).left())?; @@ -383,7 +384,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { status: Status::AtChild(i as usize), node: node.clone().into(), }); - self.key_nibbles.extend(slice.iter()); + self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push(i); if let Some(ref child) = nodes[i as usize] { full_key_nibbles += slice.len() + 1; @@ -415,13 +416,16 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Descend into a payload. fn descend_into_node(&mut self, node: OwnedNode) { - self.trail.push(Crumb { status: Status::Entering, node }); - match &self.trail.last().expect("just pushed item; qed").node { + let trail = &mut self.trail; + let key_nibbles = &mut self.key_nibbles; + trail.push(Crumb { status: Status::Entering, node }); + match &trail.last().expect("just pushed item; qed").node { &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) | &OwnedNode::NibbledBranch(ref n, _) => { - self.key_nibbles.extend((0..n.len()).map(|i| n.at(i))); + // TODO EMCH optimize this + (0..n.len()).for_each(|i| key_nibbles.push(n.at(i))); }, _ => {} } @@ -436,7 +440,9 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { let mut result = >::with_capacity(nibbles.len() / 2); let len = nibbles.len(); while i < len { - result.push(nibbles[i - 1] * 16 + nibbles[i]); + // TODO EMCH damn stupid code replace when nibblevec got already + // packed thing: neend to return nibble vec (same for encoded_key) + result.push(nibbles.at(i - 1) * 16 + nibbles.at(i)); i += 2; } result @@ -447,7 +453,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { fn encoded_key<'b>(&self, key: &'b Vec) -> (&'b [u8], Option) { let nb_padd = self.key_nibbles.len() % 2; if nb_padd > 0 { - (&key[..], Some(self.key_nibbles[self.key_nibbles.len() - 1] & (255 << 4))) + (&key[..], Some(self.key_nibbles.at(self.key_nibbles.len() - 1) & (255 << 4))) } else { (&key[..], None) } @@ -482,13 +488,11 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { (Status::Exiting, n) => { match *n { OwnedNode::Leaf(ref n, _) | OwnedNode::Extension(ref n, _) => { - let l = self.key_nibbles.len(); - self.key_nibbles.truncate(l - n.len()); + self.key_nibbles.drop_lasts(n.len()); }, OwnedNode::Branch(_) => { self.key_nibbles.pop(); }, OwnedNode::NibbledBranch(ref n,_) => { - let l = self.key_nibbles.len(); - self.key_nibbles.truncate(l - n.len() - 1); + self.key_nibbles.drop_lasts(n.len() + 1); }, OwnedNode::Empty => {}, } @@ -513,8 +517,10 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { if branch.index(i).is_some() => { match i { 0 => self.key_nibbles.push(0), - i => *self.key_nibbles.last_mut() - .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, + i => { + self.key_nibbles.pop(); + self.key_nibbles.push(i as u8); + }, } let p_key = self.key(); IterStep::Descend::, CError>(self.db.get_raw_or_lookup( diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index b95613e0..bc81d623 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1162,7 +1162,7 @@ where let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { let mov = concat_key(&mut k, o_sl, o_ix); let cr = self.commit_child(child, &mut k); - k.truncate(mov); + k.drop_lasts(mov); cr }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); @@ -1195,7 +1195,7 @@ where let commit_child = |node_handle, o_sl: Option<&NibbleSlice>, o_ix: Option| { let mov = concat_key(prefix, o_sl, o_ix); let cr = self.commit_child(node_handle, prefix); - prefix.truncate(mov); + prefix.drop_lasts(mov); cr }; node.into_encoded::<_, L::C, L::H, L::N>(commit_child) From fbe84fb2c45857207eaba93fbf694a3652c59332 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Wed, 24 Apr 2019 17:37:07 +0200 Subject: [PATCH 046/120] end of switch Vec to NibbleVec, this will need a bench. --- trie-db/src/nibblevec.rs | 9 +++++++ trie-db/src/triedb.rs | 54 ++++++++++++---------------------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs index 8959339b..0a47fc16 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibblevec.rs @@ -152,6 +152,15 @@ impl NibbleVec { self.len = 0; } + /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. + pub fn as_nibbleslice(&self) -> Option> { + // TODO is_aligned from NibbleVec + if self.len % 2 == 0 { + Some(NibbleSlice::new(self.inner())) + } else { + None + } + } } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 5ef99961..8fd8efaa 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -287,7 +287,6 @@ impl Crumb { pub struct TrieDBIterator<'a, L: TrieLayOut> { db: &'a TrieDB<'a, L>, trail: Vec>, - // TODO EMCH replace by niblleVec!!! key_nibbles: NibbleVec, } @@ -330,8 +329,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { status: Status::At, node: node.clone().into(), }); - slice.iter().for_each(|i|self.key_nibbles.push(i)); - // TODO EMCH self.key_nibbles.append_partial(slice.right()); + self.key_nibbles.append_partial(slice.right()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); let data = self.db.get_raw_or_lookup(&*item, key.back(full_key_nibbles).left())?; @@ -407,8 +405,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Descend into a payload. fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { - let p_key = self.key(); - let node_data = &self.db.get_raw_or_lookup(d, self.encoded_key(&p_key))?; + let node_data = &self.db.get_raw_or_lookup(d, self.key_nibbles.as_prefix())?; let node = L::C::decode(&node_data) .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) @@ -424,40 +421,23 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { | &OwnedNode::Extension(ref n, _) | &OwnedNode::NibbledBranch(ref n, _) => { - // TODO EMCH optimize this - (0..n.len()).for_each(|i| key_nibbles.push(n.at(i))); + if let Some(part) = n.as_nibbleslice() { + key_nibbles.append_partial(part.right()); + } else { + // TODO EMCH range_iter on nibble vec or simply cat of two nibblevec + (0..n.len()).for_each(|i|key_nibbles.push(n.at(i))); + } }, _ => {} } } - // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead - /// The present key. TODO not right it misses last - fn key(&self) -> Vec { - // collapse the key_nibbles down to bytes. - let nibbles = &self.key_nibbles; - let mut i = 1; - let mut result = >::with_capacity(nibbles.len() / 2); - let len = nibbles.len(); - while i < len { - // TODO EMCH damn stupid code replace when nibblevec got already - // packed thing: neend to return nibble vec (same for encoded_key) - result.push(nibbles.at(i - 1) * 16 + nibbles.at(i)); - i += 2; - } - result + /// The present key. This can only be called on valued node (key is therefore + /// aligned to byte). + fn key(&self) -> NibbleSlice { + self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") } - // TODO EMCH : do note generalize -> try remove (unexpose), encoded_key is use insstead - /// Encoded key for storage lookup - fn encoded_key<'b>(&self, key: &'b Vec) -> (&'b [u8], Option) { - let nb_padd = self.key_nibbles.len() % 2; - if nb_padd > 0 { - (&key[..], Some(self.key_nibbles.at(self.key_nibbles.len() - 1) & (255 << 4))) - } else { - (&key[..], None) - } - } } impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { @@ -501,14 +481,13 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { (Status::At, &OwnedNode::Branch(ref branch)) | (Status::At, &OwnedNode::NibbledBranch(_, ref branch)) if branch.has_value() => { let value = branch.get_value().expect("already checked `has_value`"); - return Some(Ok((self.key(), DBValue::from_slice(value)))); + return Some(Ok((self.key().right().1.into(), DBValue::from_slice(value)))); }, (Status::At, &OwnedNode::Leaf(_, ref v)) => { - return Some(Ok((self.key(), v.clone()))); + return Some(Ok((self.key().right().1.into(), v.clone()))); }, (Status::At, &OwnedNode::Extension(_, ref d)) => { - let p_key = self.key(); - IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, self.encoded_key(&p_key))) + IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, self.key_nibbles.as_prefix())) }, (Status::At, &OwnedNode::Branch(_)) | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, @@ -522,10 +501,9 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { self.key_nibbles.push(i as u8); }, } - let p_key = self.key(); IterStep::Descend::, CError>(self.db.get_raw_or_lookup( &branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), - self.encoded_key(&p_key))) + self.key_nibbles.as_prefix())) }, (Status::AtChild(i), &OwnedNode::Branch(_)) | (Status::AtChild(i), &OwnedNode::NibbledBranch(_,_)) => { From 50fec8507cd15142d94b13e5065021f524fbbe6f Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Thu, 25 Apr 2019 14:00:39 +0200 Subject: [PATCH 047/120] make a nibble module to share field between vec and slice --- trie-db/src/iter_build.rs | 6 +- trie-db/src/lib.rs | 6 +- trie-db/src/lookup.rs | 2 +- trie-db/src/nibble/mod.rs | 150 ++++++++++++++++++++++++ trie-db/src/{ => nibble}/nibbleslice.rs | 141 +--------------------- trie-db/src/{ => nibble}/nibblevec.rs | 20 +--- trie-db/src/node.rs | 6 +- trie-db/src/node_codec.rs | 2 +- trie-db/src/triedb.rs | 9 +- trie-db/src/triedbmut.rs | 24 ++-- 10 files changed, 189 insertions(+), 177 deletions(-) create mode 100644 trie-db/src/nibble/mod.rs rename trie-db/src/{ => nibble}/nibbleslice.rs (71%) rename trie-db/src/{ => nibble}/nibblevec.rs (90%) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index bbee2318..9f2d9054 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -17,8 +17,8 @@ use hash_db::{Hasher, HashDB, Prefix}; use std::marker::PhantomData; use crate::triedbmut::{ChildReference}; -use crate::nibbleslice::NibbleSlice; -use crate::nibbleslice::NibbleOps; +use crate::nibble::NibbleSlice; +use crate::nibble::NibbleOps; use node_codec::NodeCodec; // TODO EMCH use L instead of HC (aka TrieLayout) @@ -357,7 +357,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) } } else { // nothing null root corner case TODO warning hardcoded empty nibbleslice - cb_ext.process(crate::nibbleslice::EMPTY_ENCODED, C::empty_node().to_vec(), true); + cb_ext.process(crate::nibble::EMPTY_ENCODED, C::empty_node().to_vec(), true); } } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index c0e39e9c..ccd9d0d6 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -82,8 +82,7 @@ pub mod recorder; mod fatdb; mod fatdbmut; mod lookup; -mod nibblevec; -mod nibbleslice; +mod nibble; mod node_codec; mod iter_build; @@ -96,7 +95,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::{NibbleSlice, NibbleOps, NibbleHalf}; +pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf}; pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; @@ -215,7 +214,6 @@ pub trait Trie { } /// A key-value datastore implemented as a database-backed modified Merkle tree. -/// TODO EMCH switch to use only H::Out and C::Error as trait params?? pub trait TrieMut { /// Return the root of the trie. fn root(&mut self) -> &TrieHash; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index aa231c9e..372a5118 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -15,7 +15,7 @@ //! Trie lookup via HashDB. use hash_db::HashDBRef; -use nibbleslice::NibbleSlice; +use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; use super::{DBValue, Result, TrieError, Query, TrieLayOut, CError, TrieHash}; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs new file mode 100644 index 00000000..9b67128b --- /dev/null +++ b/trie-db/src/nibble/mod.rs @@ -0,0 +1,150 @@ +// Copyright 2019 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. + +//! Nibble oriented methods + +mod nibblevec; +mod nibbleslice; +use ::core_::cmp::*; +use ::core_::marker::PhantomData; +use elastic_array::ElasticArray36; + +pub const EMPTY_ENCODED: (&'static [u8], Option) = (&[], None); +// until const fn for pow +const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; +/// Nibble specific variants +/// Note that some function are defined here but ideally it should just be a set of +/// constant (with function handling all constant case). +pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { + /// variant repr + const REPR : ByteLayout; + /// Number of bit per nibble + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); + /// Number of nibble per byte + const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; + /// Number of nibble per node (must be power of 2 and under 256) + const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); + /// padding bitmasks (could be calculated with a constant function). + /// First is bit mask to apply, second is right shift needed. + /// TODO EMCH check that array act as constant + const PADDING_BITMASK: &'static [(u8, usize)]; + + /// Try to get the nibble at the given offset. + #[inline] + fn vec_at(s: &NibbleVec, idx: usize) -> u8 { + let ix = idx / Self::NIBBLE_PER_BYTE; + let pad = idx % Self::NIBBLE_PER_BYTE; + (s.inner[ix] & Self::PADDING_BITMASK[pad].0) + >> Self::PADDING_BITMASK[pad].1 + } + + /// Get the nibble at position `i`. + #[inline(always)] + fn at(s: &NibbleSlice, i: usize) -> u8 { + let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; + let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; + (s.data[ix] & Self::PADDING_BITMASK[pad].0) + >> Self::PADDING_BITMASK[pad].1 + } + + #[inline] + /// Number of padding needed for a length `i`. + fn nb_padding(i: usize) -> usize { + (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE + } + +} + +/// half byte nibble prepend encoding +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +pub struct NibbleHalf; + + +/// Type of nibble in term of byte size +#[repr(usize)] +pub enum ByteLayout { + /// nibble of one bit length + Bit = 0, // 1, 8, 2 + /// nibble of a quarter byte length + Quarter = 1, // 2, 4, 4 + /// nibble of a half byte length + Half = 2, // 4, 2, 16 + /// nibble of one byte length + Full = 3, // 8, 1, 256 +} + +impl NibbleOps for NibbleHalf { + const REPR: ByteLayout = ByteLayout::Half; + const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; +} + +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +pub struct NibbleQuarter; + +// new_padded_end merged +impl NibbleOps for NibbleQuarter { + const REPR: ByteLayout = ByteLayout::Quarter; + const PADDING_BITMASK: &'static [(u8, usize)] = &[ + (0b1111_1111, 6), + (0b0011_1111, 4), + (0b0000_1111, 2), + (0b0000_0011, 0), + ]; +} + + +/// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct NibbleVec { + inner: ElasticArray36, + len: usize, + marker: PhantomData, +} + +/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. +/// +/// This is an immutable struct. No operations actually change it. +/// +/// # Example +/// ```snippet +/// use patricia_trie::nibbleslice::NibbleSlice; +/// fn main() { +/// let d1 = &[0x01u8, 0x23, 0x45]; +/// let d2 = &[0x34u8, 0x50, 0x12]; +/// let d3 = &[0x00u8, 0x12]; +/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5 +/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2 +/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2 +/// assert!(n1 > n3); // 0,1,2,... > 0,1,2 +/// assert!(n1 < n2); // 0,... < 3,... +/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2 +/// assert!(n1.starts_with(&n3)); +/// assert_eq!(n1.common_prefix(&n3), 3); +/// assert_eq!(n2.mid(3).common_prefix(&n1), 3); +/// } +/// ``` +#[derive(Copy, Clone)] +pub struct NibbleSlice<'a, N: NibbleOps> { + data: &'a [u8], + offset: usize, + marker: PhantomData, +} + +/// Iterator type for a nibble slice. +pub struct NibbleSliceIterator<'a, N: NibbleOps> { + p: &'a NibbleSlice<'a, N>, + i: usize, +} + + diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs similarity index 71% rename from trie-db/src/nibbleslice.rs rename to trie-db/src/nibble/nibbleslice.rs index f2fea5c4..ccc89f0b 100644 --- a/trie-db/src/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -17,141 +17,12 @@ use ::core_::cmp::*; use ::core_::fmt; use ::core_::marker::PhantomData; -use nibblevec::NibbleVec; +use super::{NibbleOps, NibbleSlice, NibbleSliceIterator}; use elastic_array::ElasticArray36; use node::NodeKey; use node_codec::Partial; use hash_db::Prefix; -pub const EMPTY_ENCODED: (&'static [u8], Option) = (&[], None); -// until const fn for pow -const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; -/// Nibble specific variants -/// Note that some function are defined here but ideally it should just be a set of -/// constant (with function handling all constant case). -pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { - /// variant repr - const REPR : ByteLayout; - /// Number of bit per nibble - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); - /// Number of nibble per byte - const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; - /// Number of nibble per node (must be power of 2 and under 256) - const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); - /// padding bitmasks (could be calculated with a constant function). - /// First is bit mask to apply, second is right shift needed. - /// TODO EMCH check that array act as constant - const PADDING_BITMASK: &'static [(u8, usize)]; - - /// Try to get the nibble at the given offset. - #[inline] - fn vec_at(s: &NibbleVec, idx: usize) -> u8 { - let ix = idx / Self::NIBBLE_PER_BYTE; - let pad = idx % Self::NIBBLE_PER_BYTE; - (s.inner[ix] & Self::PADDING_BITMASK[pad].0) - >> Self::PADDING_BITMASK[pad].1 - } - - /// Get the nibble at position `i`. - #[inline(always)] - fn at(s: &NibbleSlice, i: usize) -> u8 { - let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; - let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; - (s.data[ix] & Self::PADDING_BITMASK[pad].0) - >> Self::PADDING_BITMASK[pad].1 - } - - #[inline] - /// Number of padding needed for a length `i`. - fn nb_padding(i: usize) -> usize { - // TODO bench something faster - (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE - } - -} - -/// half byte nibble prepend encoding -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] -pub struct NibbleHalf; - - -/// Type of nibble in term of byte size -#[repr(usize)] -pub enum ByteLayout { - /// nibble of one bit length - Bit = 0, // 1, 8, 2 - /// nibble of a quarter byte length - Quarter = 1, // 2, 4, 4 - /// nibble of a half byte length - Half = 2, // 4, 2, 16 - /// nibble of one byte length - Full = 3, // 8, 1, 256 -} - -/// `()` with a conversion to 0 -#[derive(Clone, Default, Copy, PartialEq, Eq, Debug)] -pub struct Empty; - -impl Into for Empty { - fn into(self) -> usize { 0 } -} - -impl NibbleOps for NibbleHalf { - const REPR: ByteLayout = ByteLayout::Half; - const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; -} - -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] -pub struct NibbleQuarter; - -// new_padded_end merged -impl NibbleOps for NibbleQuarter { - const REPR: ByteLayout = ByteLayout::Quarter; - const PADDING_BITMASK: &'static [(u8, usize)] = &[ - (0b1111_1111, 6), - (0b0011_1111, 4), - (0b0000_1111, 2), - (0b0000_0011, 0), - ]; -} - - - -/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. -/// -/// This is an immutable struct. No operations actually change it. -/// -/// # Example -/// ```snippet -/// use patricia_trie::nibbleslice::NibbleSlice; -/// fn main() { -/// let d1 = &[0x01u8, 0x23, 0x45]; -/// let d2 = &[0x34u8, 0x50, 0x12]; -/// let d3 = &[0x00u8, 0x12]; -/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5 -/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2 -/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2 -/// assert!(n1 > n3); // 0,1,2,... > 0,1,2 -/// assert!(n1 < n2); // 0,... < 3,... -/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2 -/// assert!(n1.starts_with(&n3)); -/// assert_eq!(n1.common_prefix(&n3), 3); -/// assert_eq!(n2.mid(3).common_prefix(&n1), 3); -/// } -/// ``` -#[derive(Copy, Clone)] -pub struct NibbleSlice<'a, N: NibbleOps> { - data: &'a [u8], - offset: usize, - marker: PhantomData, -} - -/// Iterator type for a nibble slice. -pub struct NibbleSliceIterator<'a, N: NibbleOps> { - p: &'a NibbleSlice<'a, N>, - i: usize, -} - impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { type Item = u8; fn next(&mut self) -> Option { @@ -210,12 +81,12 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let n_offset = N::nb_padding(nb); if n_offset == 1 { let mut result = (0, ea); - super::triedbmut::shift_key::(&mut result, 1); + crate::triedbmut::shift_key::(&mut result, 1); result.1.pop(); result } else { let mut result = (1, ea); - super::triedbmut::shift_key::(&mut result, 0); + crate::triedbmut::shift_key::(&mut result, 0); result.1.pop(); result } @@ -402,9 +273,9 @@ impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { #[cfg(test)] mod tests { - use super::NibbleSlice; - use super::NibbleHalf; - use super::NibbleOps; + use crate::nibble::NibbleSlice; + use crate::nibble::NibbleHalf; + use crate::nibble::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs similarity index 90% rename from trie-db/src/nibblevec.rs rename to trie-db/src/nibble/nibblevec.rs index 0a47fc16..4f15b882 100644 --- a/trie-db/src/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -14,20 +14,12 @@ //! An owning, nibble-oriented byte vector. use elastic_array::ElasticArray36; -use nibbleslice::NibbleSlice; -use nibbleslice::NibbleOps; +use nibble::NibbleSlice; +use nibble::NibbleOps; use hash_db::Prefix; use node_codec::Partial; use ::core_::marker::PhantomData; - -// TODO EMCH change crate layout to give access to nibble vec field to nibble ops and avoid pub(crate) -/// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct NibbleVec { - pub(crate) inner: ElasticArray36, - pub(crate) len: usize, - marker: PhantomData, -} +use super::NibbleVec; impl Default for NibbleVec { fn default() -> Self { @@ -176,8 +168,8 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { - use super::NibbleVec; - use nibbleslice::{NibbleHalf, NibbleOps}; + use crate::nibble::NibbleVec; + use crate::nibble::{NibbleHalf, NibbleOps}; #[test] fn push_pop() { @@ -201,7 +193,7 @@ mod tests { #[test] fn drop_lasts_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { - let mut k = NibbleVec::::new(); + let mut k = NibbleVec::::new(); for v in a { k.push(*v); } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 9f13eb90..5c729a6b 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -13,9 +13,9 @@ // limitations under the License. use elastic_array::ElasticArray36; -use nibbleslice::NibbleSlice; -use nibbleslice::NibbleOps; -use nibblevec::NibbleVec; +use nibble::NibbleSlice; +use nibble::NibbleOps; +use nibble::NibbleVec; use super::DBValue; #[cfg(not(feature = "std"))] diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 7742fe9d..c757ff49 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -17,7 +17,7 @@ use hash_db::Hasher; use node::Node; -use nibbleslice::NibbleOps; +use nibble::NibbleOps; use ChildReference; #[cfg(feature = "std")] use std::borrow::Borrow; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 8fd8efaa..1427f763 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,13 +13,14 @@ // limitations under the License. use hash_db::{HashDBRef, Prefix}; -use nibbleslice::{self, NibbleSlice, NibbleOps}; +use nibble::{NibbleSlice, NibbleOps}; +use nibble::EMPTY_ENCODED as NIBBLE_EMPTY_ENCODED; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; use triedbmut::{concat_key_clone}; -use super::nibblevec::NibbleVec; +use super::nibble::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; #[cfg(feature = "std")] @@ -80,7 +81,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + if !db.contains(root, NIBBLE_EMPTY_ENCODED) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { Ok(TrieDB {db, root, hash_count: 0}) @@ -93,7 +94,7 @@ where /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, nibbleslice::EMPTY_ENCODED) + .get(self.root, NIBBLE_EMPTY_ENCODED) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index bc81d623..bef3a650 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -16,13 +16,13 @@ use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; use super::lookup::Lookup; -use super::nibblevec::NibbleVec; use super::node::Node as EncodedNode; use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; +use nibble::EMPTY_ENCODED as NIBBLE_EMPTY_ENCODED; use hash_db::{HashDB, Hasher, Prefix}; -use nibbleslice::{self, NibbleSlice, NibbleOps}; +use nibble::{NibbleVec, NibbleSlice, NibbleOps}; use elastic_array::ElasticArray36; use ::core_::mem; use ::core_::ops::Index; @@ -126,14 +126,14 @@ where encoded_children[i].map(|data| Self::inline_or_hash::(data, db, storage) ) - }; + }; - Box::new([ - child(0), child(1), child(2), child(3), - child(4), child(5), child(6), child(7), - child(8), child(9), child(10), child(11), - child(12), child(13), child(14), child(15), - ]) + Box::new([ + child(0), child(1), child(2), child(3), + child(4), child(5), child(6), child(7), + child(8), child(9), child(10), child(11), + child(12), child(13), child(14), child(15), + ]) }; match C::decode(data).unwrap_or(EncodedNode::Empty) { @@ -380,7 +380,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + if !db.contains(root, NIBBLE_EMPTY_ENCODED) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -1166,7 +1166,7 @@ where cr }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); + *self.root = self.db.insert(NIBBLE_EMPTY_ENCODED, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -1820,7 +1820,7 @@ mod tests { let b: &[u8] = [0x56, 0x78][..].into(); let test_comb = |a: (_,&ElasticArray36<_>), b, c| { let mut a = (a.0,a.1.clone()); - super::combine_key::(&mut a, b); + super::combine_key::(&mut a, b); assert_eq!((a.0,&a.1[..]), c); }; test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); From 275f5b8ef7113f8f05db6f970301152fce71918d Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Thu, 25 Apr 2019 15:47:22 +0200 Subject: [PATCH 048/120] push_pop on quarter --- trie-db/src/nibble/mod.rs | 12 ++--- trie-db/src/nibble/nibblevec.rs | 77 +++++++++++++++++++++++---------- trie-db/src/triedbmut.rs | 2 +- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 9b67128b..5e59af55 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -39,15 +39,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// First is bit mask to apply, second is right shift needed. /// TODO EMCH check that array act as constant const PADDING_BITMASK: &'static [(u8, usize)]; - - /// Try to get the nibble at the given offset. - #[inline] - fn vec_at(s: &NibbleVec, idx: usize) -> u8 { - let ix = idx / Self::NIBBLE_PER_BYTE; - let pad = idx % Self::NIBBLE_PER_BYTE; - (s.inner[ix] & Self::PADDING_BITMASK[pad].0) - >> Self::PADDING_BITMASK[pad].1 - } + const SINGLE_BITMASK: u8; /// Get the nibble at position `i`. #[inline(always)] @@ -87,6 +79,7 @@ pub enum ByteLayout { impl NibbleOps for NibbleHalf { const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; + const SINGLE_BITMASK: u8 = 0x0F; } #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] @@ -101,6 +94,7 @@ impl NibbleOps for NibbleQuarter { (0b0000_1111, 2), (0b0000_0011, 0), ]; + const SINGLE_BITMASK: u8 = 0b0000_0011; } diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 4f15b882..84a68eff 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -47,11 +47,13 @@ impl NibbleVec { /// Try to get the nibble at the given offset. #[inline] pub fn at(&self, idx: usize) -> u8 { - N::vec_at(self, idx) + let ix = idx / N::NIBBLE_PER_BYTE; + let pad = idx % N::NIBBLE_PER_BYTE; + (self.inner[ix] & N::PADDING_BITMASK[pad].0) + >> N::PADDING_BITMASK[pad].1 } - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - pub fn push(&mut self, nibble: u8) { + pub fn push_o(&mut self, nibble: u8) { let nibble = nibble & 0x0F; if self.len % 2 == 0 { @@ -63,8 +65,20 @@ impl NibbleVec { self.len += 1; } - /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. - pub fn pop(&mut self) -> Option { + + /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. + pub fn push(&mut self, nibble: u8) { + let i = self.len % N::NIBBLE_PER_BYTE; + let nibble = (nibble & N::SINGLE_BITMASK) << N::PADDING_BITMASK[i].1; + + if i == 0 { + self.inner.push(nibble); + } else { + *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + self.len += 1; + } + pub fn pop_o(&mut self) -> Option { if self.is_empty() { return None; } @@ -80,6 +94,20 @@ impl NibbleVec { self.len -= 1; Some(nibble) } + + /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None; + } + let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); + self.len -= 1; + let i_new = self.len % N::NIBBLE_PER_BYTE; + if i_new != 0 { + self.inner.push(byte & !N::PADDING_BITMASK[i_new].0); + } + Some((byte & N::PADDING_BITMASK[i_new].0) >> N::PADDING_BITMASK[i_new].1) + } /// remove n last nibbles. pub fn drop_lasts(&mut self, mov: usize) { @@ -96,7 +124,7 @@ impl NibbleVec { }; (0..nb_rem).for_each(|_|{ self.inner.pop(); }); - self.len -= mov; + self.len -= mov; if self.len % 2 == 1 { let kl = self.inner.len() - 1; self.inner[kl] &= 255 << 4; @@ -118,7 +146,7 @@ impl NibbleVec { if let Some(nibble) = o_n { self.push(nibble) } - let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; + let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; if pad == 0 { self.inner.append_slice(&sl[..]); } else { @@ -130,7 +158,7 @@ impl NibbleVec { self.inner.push(sl[sl.len() - 1] << 4); } } - self.len += sl.len() * N::NIBBLE_PER_BYTE; + self.len += sl.len() * N::NIBBLE_PER_BYTE; } /// Get the underlying byte slice. @@ -146,8 +174,7 @@ impl NibbleVec { /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. pub fn as_nibbleslice(&self) -> Option> { - // TODO is_aligned from NibbleVec - if self.len % 2 == 0 { + if self.len % N::NIBBLE_PER_BYTE == 0 { Some(NibbleSlice::new(self.inner())) } else { None @@ -169,24 +196,28 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { use crate::nibble::NibbleVec; - use crate::nibble::{NibbleHalf, NibbleOps}; + use crate::nibble::{NibbleHalf, NibbleOps, NibbleQuarter}; #[test] fn push_pop() { push_pop_inner::(); + push_pop_inner::(); } fn push_pop_inner() { let mut v = NibbleVec::::new(); - for i in 0..16 { - v.push(i); - assert_eq!(v.len() - 1, i as usize); - assert_eq!(v.at(i as usize), i); + for i in 0..(N::NIBBLE_PER_BYTE * 3) { + let iu8 = (i % N::NIBBLE_PER_BYTE) as u8; + v.push(iu8); + assert_eq!(v.len() - 1, i); + assert_eq!(v.at(i), iu8); } - for i in (0..16).rev() { - assert_eq!(v.pop(), Some(i)); - assert_eq!(v.len(), i as usize); + for i in (0..(N::NIBBLE_PER_BYTE * 3)).rev() { + let iu8 = (i % N::NIBBLE_PER_BYTE) as u8; + let a = v.pop(); + assert_eq!(a, Some(iu8)); + assert_eq!(v.len(), i); } } @@ -194,11 +225,11 @@ mod tests { fn drop_lasts_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { let mut k = NibbleVec::::new(); - for v in a { - k.push(*v); - } - k.drop_lasts(b); - assert_eq!((&k.inner[..], k.len), c); + for v in a { + k.push(*v); + } + k.drop_lasts(b); + assert_eq!((&k.inner[..], k.len), c); }; test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 4)); test_trun(&[1,2,3,4], 1, (&[0x12, 0x30], 3)); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index bef3a650..d0b169a4 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1370,7 +1370,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - RefTrieDBNoExt, LayoutOri, LayoutNew}; + LayoutOri, LayoutNew}; fn populate_trie<'db>( db: &'db mut HashDB, From 6fd7531e46741adf758028b3e62cb252f8a6ce15 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sun, 28 Apr 2019 22:31:36 +0200 Subject: [PATCH 049/120] in progress - append_partial --- trie-db/src/nibble/mod.rs | 2 +- trie-db/src/nibble/nibblevec.rs | 49 ++++++++------------------------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 5e59af55..c4aa4949 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -37,7 +37,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); /// padding bitmasks (could be calculated with a constant function). /// First is bit mask to apply, second is right shift needed. - /// TODO EMCH check that array act as constant + /// TODO EMCH check that array acts as constant const PADDING_BITMASK: &'static [(u8, usize)]; const SINGLE_BITMASK: u8; diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 84a68eff..f2ca4956 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -52,18 +52,6 @@ impl NibbleVec { (self.inner[ix] & N::PADDING_BITMASK[pad].0) >> N::PADDING_BITMASK[pad].1 } - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - pub fn push_o(&mut self, nibble: u8) { - let nibble = nibble & 0x0F; - - if self.len % 2 == 0 { - self.inner.push(nibble << 4); - } else { - *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - } - - self.len += 1; - } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. @@ -78,22 +66,6 @@ impl NibbleVec { } self.len += 1; } - pub fn pop_o(&mut self) -> Option { - if self.is_empty() { - return None; - } - - let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if self.len % 2 == 0 { - self.inner.push(byte & 0xF0); - byte & 0x0F - } else { - byte >> 4 - }; - - self.len -= 1; - Some(nibble) - } /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { @@ -118,26 +90,26 @@ impl NibbleVec { } let nb_rem = if (self.len - mov) % N::NIBBLE_PER_BYTE > 0 { mov / N::NIBBLE_PER_BYTE - } else { - (mov + 1) / N::NIBBLE_PER_BYTE - + (mov + N::NIBBLE_PER_BYTE - 1) / N::NIBBLE_PER_BYTE }; (0..nb_rem).for_each(|_|{ self.inner.pop(); }); self.len -= mov; - if self.len % 2 == 1 { + let pos = self.len % N::NIBBLE_PER_BYTE; + if pos != 0 { let kl = self.inner.len() - 1; - self.inner[kl] &= 255 << 4; + self.inner[kl] &= !N::PADDING_BITMASK[pos].0; } } /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). pub fn as_prefix(&self) -> Prefix { - let split = self.len / 2; - if self.len % 2 == 1 { - (&self.inner[..split], Some(self.inner[split] & (255 << 4))) - } else { + let split = self.len / N::NIBBLE_PER_BYTE; + let pos = self.len % N::NIBBLE_PER_BYTE; + if pos == 0 { (&self.inner[..split], None) + } else { + (&self.inner[..split], Some(self.inner[split] & !N::PADDING_BITMASK[pos].0)) } } @@ -152,7 +124,8 @@ impl NibbleVec { } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - self.inner[kend] &= 255 << 4; + let pos = self.len % N::NIBBLE_PER_BYTE; + self.inner[kend] &= !N::PADDING_BITMASK[pos].0; self.inner[kend] |= sl[0] >> 4; (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << 4 | sl[i+1]>>4)); self.inner.push(sl[sl.len() - 1] << 4); From 959da6c5c565ba6e127f24329c29ef948d323ad1 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 30 Apr 2019 19:23:34 +0200 Subject: [PATCH 050/120] partial & prefix cannot be simply option but (nb_nibble, aligned_val) as (u8, u8) seems more proper --- trie-db/src/nibble/nibblevec.rs | 37 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index f2ca4956..5f0c6d94 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -113,22 +113,23 @@ impl NibbleVec { } } - /// push a full partial. + /// push a full partial. TODO option for partial does not contain enough info pub fn append_partial(&mut self, (o_n, sl): Partial) { if let Some(nibble) = o_n { self.push(nibble) } - let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; + let pad = N::NIBBLE_PER_BYTE - self.len; if pad == 0 { self.inner.append_slice(&sl[..]); } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - let pos = self.len % N::NIBBLE_PER_BYTE; - self.inner[kend] &= !N::PADDING_BITMASK[pos].0; - self.inner[kend] |= sl[0] >> 4; - (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << 4 | sl[i+1]>>4)); - self.inner.push(sl[sl.len() - 1] << 4); + self.inner[kend] &= !N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - pad].0; + let s1 = N::PADDING_BITMASK[pad - 1].1; + let s2 = 8 - s1; + self.inner[kend] |= sl[0] >> s1; + (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); + self.inner.push(sl[sl.len() - 1] << s2); } } self.len += sl.len() * N::NIBBLE_PER_BYTE; @@ -193,6 +194,28 @@ mod tests { assert_eq!(v.len(), i); } } + #[test] + fn append_partial() { + append_partial_inner::(&[1,2,3], &[], (Some(1), &[0x23])); + append_partial_inner::(&[1,2,3], &[1], (None, &[0x23])); + append_partial_inner::(&[0,1,2,3], &[0], (Some(1), &[0x23])); + append_partial_inner::(&[1, 0, 2, 0, 3], &[], (Some(1), &[0x23])); + append_partial_inner::(&[1, 0, 2, 0, 3, 0, 1, 0, 2], &[], (Some(1), &[0x23, 0x12])); + append_partial_inner::(&[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3], (Some(1), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], (Some(1), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], (Some(1), &[0x23, 0x12])); + + // append_partial_inner::(); + } + fn append_partial_inner(res: &[u8], init: &[u8], partial: (Option, &[u8])) { + let mut resv = NibbleVec::::new(); + res.iter().for_each(|r|resv.push(*r)); + let mut initv = NibbleVec::::new(); + init.iter().for_each(|r|initv.push(*r)); + initv.append_partial(partial); + assert_eq!(resv, initv); + } + #[test] fn drop_lasts_test() { From dd93050075416aa2498d87057fdae9f7cd049baa Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 30 Apr 2019 20:06:28 +0200 Subject: [PATCH 051/120] switch partial to (u8, u8) --- test-support/reference-trie/src/lib.rs | 16 ++++++------ trie-db/src/nibble/nibbleslice.rs | 15 ++++++----- trie-db/src/nibble/nibblevec.rs | 35 ++++++++++++++------------ trie-db/src/node_codec.rs | 2 +- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index af167d1e..1505cd28 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -506,12 +506,12 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { } fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { - let nb_nibble_hpe = if partial.0.is_some() { 1 } else { 0 }; + let nb_nibble_hpe = (partial.0).0 as usize; let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if let Some(v) = partial.0 { - output.push(v & N::PADDING_BITMASK[nb_nibble_hpe].0); + if nb_nibble_hpe > 0 { + output.push((partial.0).1 & N::PADDING_BITMASK[nb_nibble_hpe].0); } output.extend_from_slice(&partial.1[..]); output @@ -522,9 +522,7 @@ fn partial_to_key_it>(partial: I, nibble_co assert!(nibble_count < over as usize); let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); output.push(offset + nibble_count as u8); - println!("be : {:x?}", output); output.extend(partial); - println!("ae : {:x?}", output); output } @@ -544,7 +542,7 @@ fn partial_enc_it>(partial: I, nibble_count fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec { - let nb_nibble_hpe = if partial.0.is_some() { 1 } else { 0 }; + let nb_nibble_hpe = (partial.0).0 as usize; let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); @@ -555,8 +553,8 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; - if let Some(v) = partial.0 { - output.push(v & N::PADDING_BITMASK[nb_nibble_hpe].0); + if nb_nibble_hpe > 0 { + output.push((partial.0).1 & N::PADDING_BITMASK[nb_nibble_hpe].0); } output.extend_from_slice(&partial.1[..]); output @@ -1056,7 +1054,7 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node((None,&input), &[1]); + let enc = >::leaf_node(((0,0),&input), &[1]); let dec = >::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index ccc89f0b..75f6044c 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -145,11 +145,12 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> Partial { - let split = self.offset / 2; - if self.len() % 2 == 1 { - (Some(self.data[split] & (255 >> 4)), &self.data[split + 1 ..]) + let split = self.offset / N::NIBBLE_PER_BYTE; + let nb = self.len() % N::NIBBLE_PER_BYTE; + if nb > 0 { + ((nb as u8, self.data[split] & N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - nb].0), &self.data[split + 1 ..]) } else { - (None, &self.data[split..]) + ((0,0), &self.data[split..]) } } @@ -158,8 +159,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let (mut first, sl) = self.right(); let mut ix = 0; ::core_::iter::from_fn( move || { - if first.is_some() { - first.take() + if first.0 > 0 { + let ix = N::NIBBLE_PER_BYTE - first.0 as usize; + first.0 -= 1; + Some((first.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) } else { if ix < sl.len() { ix += 1; diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 5f0c6d94..82dbaff7 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -115,10 +115,11 @@ impl NibbleVec { /// push a full partial. TODO option for partial does not contain enough info pub fn append_partial(&mut self, (o_n, sl): Partial) { - if let Some(nibble) = o_n { - self.push(nibble) - } - let pad = N::NIBBLE_PER_BYTE - self.len; + for i in (1..=o_n.0).rev() { + let ix = N::NIBBLE_PER_BYTE - i as usize; + self.push((o_n.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) + } + let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; if pad == 0 { self.inner.append_slice(&sl[..]); } else { @@ -196,18 +197,20 @@ mod tests { } #[test] fn append_partial() { - append_partial_inner::(&[1,2,3], &[], (Some(1), &[0x23])); - append_partial_inner::(&[1,2,3], &[1], (None, &[0x23])); - append_partial_inner::(&[0,1,2,3], &[0], (Some(1), &[0x23])); - append_partial_inner::(&[1, 0, 2, 0, 3], &[], (Some(1), &[0x23])); - append_partial_inner::(&[1, 0, 2, 0, 3, 0, 1, 0, 2], &[], (Some(1), &[0x23, 0x12])); - append_partial_inner::(&[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3], (Some(1), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], (Some(1), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], (Some(1), &[0x23, 0x12])); - - // append_partial_inner::(); - } - fn append_partial_inner(res: &[u8], init: &[u8], partial: (Option, &[u8])) { + append_partial_inner::(&[1,2,3], &[], ((1,1), &[0x23])); + append_partial_inner::(&[1,2,3], &[1], ((0,0), &[0x23])); + append_partial_inner::(&[0,1,2,3], &[0], ((1,1), &[0x23])); + append_partial_inner::(&[1, 0, 2, 0, 3], &[], ((1,1), &[0x23])); + append_partial_inner::(&[1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((1,1), &[0x23, 0x12])); + append_partial_inner::(&[2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((2,0b1001), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((3,0b111001), &[0x23, 0x12])); + append_partial_inner::(&[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3], ((1,1), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], ((1,1), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], ((2,0b1001), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((1,1), &[0x23, 0x12])); + append_partial_inner::(&[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((3,0b111001), &[0x23, 0x12])); + } + fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8,u8), &[u8])) { let mut resv = NibbleVec::::new(); res.iter().for_each(|r|resv.push(*r)); let mut initv = NibbleVec::::new(); diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index c757ff49..35fcc774 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -38,7 +38,7 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} -pub type Partial<'a> = (Option, &'a[u8]); +pub type Partial<'a> = ((u8,u8), &'a[u8]); // TODO EMCH change node codec trait to use &mut self as input in order to run on internal buffer. // (not for decode actually!!; code seems fine to do that and new layout trait is ok too /// Trait for trie node encoding/decoding From b75624805a9dd0a082669608c700795a7d4cd7a1 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 1 May 2019 19:57:04 +0200 Subject: [PATCH 052/120] Replace prefix option by index bytes --- hash-db/src/lib.rs | 8 ++- memory-db/src/lib.rs | 5 +- trie-db/src/fatdb.rs | 2 +- trie-db/src/nibble/mod.rs | 102 +++++++++++++++++------------- trie-db/src/nibble/nibbleslice.rs | 45 ++++++------- trie-db/src/nibble/nibblevec.rs | 22 +++---- trie-db/src/triedb.rs | 2 +- trie-db/src/triedbmut.rs | 24 +++---- 8 files changed, 117 insertions(+), 93 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index fe65c834..bb734d9f 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -34,11 +34,13 @@ impl MaybeDebug for T {} /// Empty prefix constant -pub static EMPTY_PREFIX: Prefix<'static> = (&[], None); +pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0,0)); /// prefix for trie value, encoded as reference and a last padded byte to -/// match `left` function of nibbleslice -pub type Prefix<'a> = (&'a[u8], Option); +/// match `left` function of nibbleslice. +/// Padded byte is represented as a pair with number of nibble first and +/// padded value second. +pub type Prefix<'a> = (&'a[u8], (u8, u8)); /// Trait describing an object that can hash a slice of bytes. Used to abstract /// other types over the hashing algorithm. Defines a single `hash` method and an diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index c1e511ea..c9b55f79 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -155,7 +155,10 @@ pub trait KeyFunction { pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); prefixed_key.extend_from_slice(prefix.0); - prefix.1.map(|v|prefixed_key.push(v)); + if (prefix.1).0 > 0 { + prefixed_key.push((prefix.1).1); + } + prefixed_key.push((prefix.1).0); // put size to avoid any possible collision prefixed_key.extend_from_slice(key.as_ref()); prefixed_key } diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 361179ab..d85374fa 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -108,7 +108,7 @@ where .map(|res| { res.map(|(hash, value)| { let aux_hash = L::H::hash(&hash); - (self.trie.db().get(&aux_hash, (&[], None)).expect("Missing fatdb hash").into_vec(), value) + (self.trie.db().get(&aux_hash, Default::default()).expect("Missing fatdb hash").into_vec(), value) }) }) } diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index c4aa4949..e7620cd8 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -20,41 +20,57 @@ use ::core_::cmp::*; use ::core_::marker::PhantomData; use elastic_array::ElasticArray36; -pub const EMPTY_ENCODED: (&'static [u8], Option) = (&[], None); +pub const EMPTY_ENCODED: (&'static [u8], (u8, u8)) = (&[], (0, 0)); // until const fn for pow const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Nibble specific variants /// Note that some function are defined here but ideally it should just be a set of /// constant (with function handling all constant case). pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { - /// variant repr - const REPR : ByteLayout; - /// Number of bit per nibble - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); - /// Number of nibble per byte - const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; - /// Number of nibble per node (must be power of 2 and under 256) - const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); - /// padding bitmasks (could be calculated with a constant function). - /// First is bit mask to apply, second is right shift needed. - /// TODO EMCH check that array acts as constant - const PADDING_BITMASK: &'static [(u8, usize)]; - const SINGLE_BITMASK: u8; - - /// Get the nibble at position `i`. + /// variant repr + const REPR : ByteLayout; + /// Number of bit per nibble + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); + /// Number of nibble per byte + const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; + /// Number of nibble per node (must be power of 2 and under 256) + const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); + /// padding bitmasks (could be calculated with a constant function). + /// First is bit mask to apply, second is right shift needed. + /// TODO EMCH check that array acts as constant + const PADDING_BITMASK: &'static [(u8, usize)]; + /// las ix for nible + const LAST_N_IX: usize = Self::NIBBLE_LEN - 1; + /// las ix for nible as a u8 (for pattern matching) + const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; + const SINGLE_BITMASK: u8; + + /// mask a byte from a ix > 0 + #[inline(always)] + fn masked_left(ix: u8, b: u8) -> u8 { + debug_assert!(ix > 0); + b & !Self::PADDING_BITMASK[ix as usize].0 + } + /// push u8 nib value at ix into a existing byte + #[inline(always)] + fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { + into | (v << Self::PADDING_BITMASK[ix as usize].1) + } + + /// Get the nibble at position `i`. #[inline(always)] fn at(s: &NibbleSlice, i: usize) -> u8 { - let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; - let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; + let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; + let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; (s.data[ix] & Self::PADDING_BITMASK[pad].0) - >> Self::PADDING_BITMASK[pad].1 + >> Self::PADDING_BITMASK[pad].1 } - #[inline] - /// Number of padding needed for a length `i`. - fn nb_padding(i: usize) -> usize { - (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE - } + #[inline] + /// Number of padding needed for a length `i`. + fn nb_padding(i: usize) -> usize { + (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE + } } @@ -66,20 +82,20 @@ pub struct NibbleHalf; /// Type of nibble in term of byte size #[repr(usize)] pub enum ByteLayout { - /// nibble of one bit length - Bit = 0, // 1, 8, 2 - /// nibble of a quarter byte length - Quarter = 1, // 2, 4, 4 - /// nibble of a half byte length - Half = 2, // 4, 2, 16 - /// nibble of one byte length - Full = 3, // 8, 1, 256 + /// nibble of one bit length + Bit = 0, // 1, 8, 2 + /// nibble of a quarter byte length + Quarter = 1, // 2, 4, 4 + /// nibble of a half byte length + Half = 2, // 4, 2, 16 + /// nibble of one byte length + Full = 3, // 8, 1, 256 } impl NibbleOps for NibbleHalf { - const REPR: ByteLayout = ByteLayout::Half; - const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; - const SINGLE_BITMASK: u8 = 0x0F; + const REPR: ByteLayout = ByteLayout::Half; + const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; + const SINGLE_BITMASK: u8 = 0x0F; } #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] @@ -87,14 +103,14 @@ pub struct NibbleQuarter; // new_padded_end merged impl NibbleOps for NibbleQuarter { - const REPR: ByteLayout = ByteLayout::Quarter; - const PADDING_BITMASK: &'static [(u8, usize)] = &[ - (0b1111_1111, 6), - (0b0011_1111, 4), - (0b0000_1111, 2), - (0b0000_0011, 0), - ]; - const SINGLE_BITMASK: u8 = 0b0000_0011; + const REPR: ByteLayout = ByteLayout::Quarter; + const PADDING_BITMASK: &'static [(u8, usize)] = &[ + (0b1111_1111, 6), + (0b0011_1111, 4), + (0b0000_1111, 2), + (0b0000_0011, 0), + ]; + const SINGLE_BITMASK: u8 = 0b0000_0011; } diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 75f6044c..6a361f9e 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -78,18 +78,18 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); - let n_offset = N::nb_padding(nb); - if n_offset == 1 { - let mut result = (0, ea); - crate::triedbmut::shift_key::(&mut result, 1); - result.1.pop(); - result - } else { - let mut result = (1, ea); - crate::triedbmut::shift_key::(&mut result, 0); - result.1.pop(); - result - } + let n_offset = N::nb_padding(nb); + if n_offset == 1 { + let mut result = (0, ea); + crate::triedbmut::shift_key::(&mut result, 1); + result.1.pop(); + result + } else { + let mut result = (1, ea); + crate::triedbmut::shift_key::(&mut result, 0); + result.1.pop(); + result + } } } @@ -114,10 +114,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { marker: PhantomData, } } - /// Advance the view on the slice by `i` nibbles + /// Advance the view on the slice by `i` nibbles pub fn advance(&mut self, i: usize) { debug_assert!(self.len() >= i); - self.offset += i; + self.offset += i; } /// Return object to an offset position pub fn back(&self, i: usize) -> NibbleSlice<'a, N> { @@ -146,7 +146,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> Partial { let split = self.offset / N::NIBBLE_PER_BYTE; - let nb = self.len() % N::NIBBLE_PER_BYTE; + let nb = self.len() % N::NIBBLE_PER_BYTE; if nb > 0 { ((nb as u8, self.data[split] & N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - nb].0), &self.data[split + 1 ..]) } else { @@ -160,8 +160,8 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let mut ix = 0; ::core_::iter::from_fn( move || { if first.0 > 0 { - let ix = N::NIBBLE_PER_BYTE - first.0 as usize; - first.0 -= 1; + let ix = N::NIBBLE_PER_BYTE - first.0 as usize; + first.0 -= 1; Some((first.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) } else { if ix < sl.len() { @@ -176,14 +176,15 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return left of key nibble pub fn left(&'a self) -> Prefix { - let split = self.offset / 2; - if self.len() % 2 == 1 { - (&self.data[..split], Some(self.data[split] & (255 << 4))) + let split = self.offset / N::NIBBLE_PER_BYTE; + let ix = (self.offset % N::NIBBLE_PER_BYTE) as u8; + if ix == 0 { + (&self.data[..split], (0,0)) } else { - (&self.data[..split], None) + (&self.data[..split], (ix, N::masked_left(ix, self.data[split]))) } } - pub fn left_owned(&'a self) -> (ElasticArray36, Option) { + pub fn left_owned(&'a self) -> (ElasticArray36, (u8,u8)) { let (a, b) = self.left(); (a.into(), b) } diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 82dbaff7..43e650e0 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -105,20 +105,20 @@ impl NibbleVec { /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). pub fn as_prefix(&self) -> Prefix { let split = self.len / N::NIBBLE_PER_BYTE; - let pos = self.len % N::NIBBLE_PER_BYTE; + let pos = (self.len % N::NIBBLE_PER_BYTE) as u8; if pos == 0 { - (&self.inner[..split], None) + (&self.inner[..split], (0,0)) } else { - (&self.inner[..split], Some(self.inner[split] & !N::PADDING_BITMASK[pos].0)) + (&self.inner[..split], (pos, N::masked_left(pos, self.inner[split]))) } } /// push a full partial. TODO option for partial does not contain enough info pub fn append_partial(&mut self, (o_n, sl): Partial) { - for i in (1..=o_n.0).rev() { - let ix = N::NIBBLE_PER_BYTE - i as usize; + for i in (1..=o_n.0).rev() { + let ix = N::NIBBLE_PER_BYTE - i as usize; self.push((o_n.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) - } + } let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; if pad == 0 { self.inner.append_slice(&sl[..]); @@ -126,8 +126,8 @@ impl NibbleVec { let kend = self.inner.len() - 1; if sl.len() > 0 { self.inner[kend] &= !N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - pad].0; - let s1 = N::PADDING_BITMASK[pad - 1].1; - let s2 = 8 - s1; + let s1 = N::PADDING_BITMASK[pad - 1].1; + let s2 = 8 - s1; self.inner[kend] |= sl[0] >> s1; (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); self.inner.push(sl[sl.len() - 1] << s2); @@ -212,10 +212,10 @@ mod tests { } fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8,u8), &[u8])) { let mut resv = NibbleVec::::new(); - res.iter().for_each(|r|resv.push(*r)); + res.iter().for_each(|r|resv.push(*r)); let mut initv = NibbleVec::::new(); - init.iter().for_each(|r|initv.push(*r)); - initv.append_partial(partial); + init.iter().for_each(|r|initv.push(*r)); + initv.append_partial(partial); assert_eq!(resv, initv); } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 1427f763..e3c9ca9b 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -104,7 +104,7 @@ where /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: Prefix) -> Result, TrieHash, CError> { - match (partial_key.0.is_empty() && partial_key.1.is_none(), L::C::try_decode_hash(node)) { + match (partial_key.0.is_empty() && (partial_key.1).0 == 0, L::C::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index d0b169a4..50068683 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -167,8 +167,8 @@ where Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => { let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); - C::leaf_node(pr.right(), &value) - }, + C::leaf_node(pr.right(), &value) + }, Node::Extension(partial, child) => { let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); @@ -352,7 +352,7 @@ where db: &'a mut HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, - death_row: HashSet<(TrieHash, (ElasticArray36, Option))>, + death_row: HashSet<(TrieHash, (ElasticArray36, (u8,u8)))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, @@ -1011,12 +1011,13 @@ where kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); let (st, ost, op) = match kc.left() { - (st, Some(v)) => { + (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), + (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { let mut so: ElasticArray36 = st.into(); - so.push((v & (255 << 4)) | a); - (st, Some(so), None) + so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); // TODO replace by push_at left?? + (st, Some(so), (0,0)) }, - (st, None) => (st, None, Some(a << 4)), + (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), }; let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); let stored = match child { @@ -1072,12 +1073,13 @@ where let mut kc = key.clone(); kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); let (st, ost, op) = match kc.left() { - (st, Some(v)) => { + (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), + (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { let mut so: ElasticArray36 = st.into(); - so.push((v & (255 << 4)) | a); - (st, Some(so), None) + so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); // TODO replace by push_at left?? + (st, Some(so), (0,0)) }, - (st, None) => (st, None, Some(a << 4)), + (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), }; let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); From 9e45edaf9eead96cd2a0d2bb6e7cf0095f83075a Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 2 May 2019 16:01:38 +0200 Subject: [PATCH 053/120] clean code from padding_bitmask a bit --- test-support/reference-trie/src/lib.rs | 8 ++++---- trie-db/src/nibble/mod.rs | 24 ++++++++++++++++++++++-- trie-db/src/nibble/nibbleslice.rs | 6 +++--- trie-db/src/nibble/nibblevec.rs | 25 +++++++++++-------------- trie-db/src/triedbmut.rs | 6 ------ 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 1505cd28..380328ad 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -511,7 +511,7 @@ fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec 0 { - output.push((partial.0).1 & N::PADDING_BITMASK[nb_nibble_hpe].0); + output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -554,7 +554,7 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if nb_nibble_hpe > 0 { - output.push((partial.0).1 & N::PADDING_BITMASK[nb_nibble_hpe].0); + output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -698,7 +698,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe].0 != 0 { + if nb_nibble_hpe > 0 && N::masked_left(nb_nibble_hpe as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) @@ -725,7 +725,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } NodeHeaderNoExt::Leaf(nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && input[0] & !N::PADDING_BITMASK[nb_nibble_hpe].0 != 0 { + if nb_nibble_hpe > 0 && N::masked_left(nb_nibble_hpe as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index e7620cd8..a54ec37a 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -51,6 +51,19 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } + /// mask a byte from a ix > 0 + #[inline(always)] + fn masked_right(ix: u8, b: u8) -> u8 { + debug_assert!(ix > 0); + b & Self::PADDING_BITMASK[ix as usize].0 + } + /// get value at ix from a right first byte + #[inline(always)] + fn at_right(ix: u8, b: u8) -> u8 { + (b & Self::PADDING_BITMASK[ix as usize].0) + >> Self::PADDING_BITMASK[ix as usize].1 + } + /// push u8 nib value at ix into a existing byte #[inline(always)] fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { @@ -62,8 +75,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy fn at(s: &NibbleSlice, i: usize) -> u8 { let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; - (s.data[ix] & Self::PADDING_BITMASK[pad].0) - >> Self::PADDING_BITMASK[pad].1 + Self::at_right(pad as u8, s.data[ix]) } #[inline] @@ -72,6 +84,14 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE } + /// split shifts for a given unaligned padding (pad != 0) + #[inline(always)] + fn split_shifts(pad: usize) -> (usize, usize) { + debug_assert!(pad > 0); + let s1 = Self::PADDING_BITMASK[pad - 1].1; + let s2 = 8 - s1; + (s1, s2) + } } /// half byte nibble prepend encoding diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 6a361f9e..fc1e94bf 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -146,9 +146,9 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// return first encoded byte and following slice pub fn right(&'a self) -> Partial { let split = self.offset / N::NIBBLE_PER_BYTE; - let nb = self.len() % N::NIBBLE_PER_BYTE; + let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; if nb > 0 { - ((nb as u8, self.data[split] & N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - nb].0), &self.data[split + 1 ..]) + ((nb, N::masked_right(nb, self.data[split])), &self.data[split + 1 ..]) } else { ((0,0), &self.data[split..]) } @@ -162,7 +162,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { if first.0 > 0 { let ix = N::NIBBLE_PER_BYTE - first.0 as usize; first.0 -= 1; - Some((first.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) + Some(N::at_right(ix as u8, first.1)) } else { if ix < sl.len() { ix += 1; diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 43e650e0..0459a90c 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -49,20 +49,18 @@ impl NibbleVec { pub fn at(&self, idx: usize) -> u8 { let ix = idx / N::NIBBLE_PER_BYTE; let pad = idx % N::NIBBLE_PER_BYTE; - (self.inner[ix] & N::PADDING_BITMASK[pad].0) - >> N::PADDING_BITMASK[pad].1 + N::at_right(pad as u8, self.inner[ix]) } - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. pub fn push(&mut self, nibble: u8) { let i = self.len % N::NIBBLE_PER_BYTE; - let nibble = (nibble & N::SINGLE_BITMASK) << N::PADDING_BITMASK[i].1; if i == 0 { - self.inner.push(nibble); + self.inner.push(N::push_at_left(0, nibble, 0)); } else { - *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + let dest = self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed"); + *dest = N::push_at_left(i as u8, nibble, *dest); } self.len += 1; } @@ -76,9 +74,9 @@ impl NibbleVec { self.len -= 1; let i_new = self.len % N::NIBBLE_PER_BYTE; if i_new != 0 { - self.inner.push(byte & !N::PADDING_BITMASK[i_new].0); + self.inner.push(N::masked_left(i_new as u8, byte)); } - Some((byte & N::PADDING_BITMASK[i_new].0) >> N::PADDING_BITMASK[i_new].1) + Some(N::at_right(i_new as u8, byte)) } /// remove n last nibbles. @@ -98,7 +96,7 @@ impl NibbleVec { let pos = self.len % N::NIBBLE_PER_BYTE; if pos != 0 { let kl = self.inner.len() - 1; - self.inner[kl] &= !N::PADDING_BITMASK[pos].0; + self.inner[kl] = N::masked_left(pos as u8, self.inner[kl]); } } @@ -113,11 +111,11 @@ impl NibbleVec { } } - /// push a full partial. TODO option for partial does not contain enough info + /// push a full partial. pub fn append_partial(&mut self, (o_n, sl): Partial) { for i in (1..=o_n.0).rev() { let ix = N::NIBBLE_PER_BYTE - i as usize; - self.push((o_n.1 & N::PADDING_BITMASK[ix].0) >> N::PADDING_BITMASK[ix].1) + self.push(N::at_right(ix as u8, o_n.1)); } let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; if pad == 0 { @@ -125,9 +123,8 @@ impl NibbleVec { } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - self.inner[kend] &= !N::PADDING_BITMASK[N::NIBBLE_PER_BYTE - pad].0; - let s1 = N::PADDING_BITMASK[pad - 1].1; - let s2 = 8 - s1; + self.inner[kend] = N::masked_left((N::NIBBLE_PER_BYTE - pad) as u8, self.inner[kend]); + let (s1, s2) = N::split_shifts(pad); self.inner[kend] |= sl[0] >> s1; (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); self.inner.push(sl[sl.len() - 1] << s2); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 50068683..8723bb22 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1023,7 +1023,6 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO NX !!! from this key advenac we are out of key -> can do a let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } @@ -1038,14 +1037,12 @@ where match child_node { Node::Leaf(sub_partial, value) => { let mut enc_nibble = enc_nibble; - // TODO NX!!! combine_key::(&mut enc_nibble, (1, &[a][..])); combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); Ok(Node::Leaf(enc_nibble, value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let mut enc_nibble = enc_nibble; - // TODO NX!!! // TODO a + buf + ck combine_key::(&mut enc_nibble, (1, &[a][..])); combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); @@ -1086,7 +1083,6 @@ where let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - // TODO NX!! here to let handle = self.cache(h, child_pref)?; self.storage.destroy(handle) } @@ -1104,7 +1100,6 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } - // TODO NX!! // subpartial let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); @@ -1117,7 +1112,6 @@ where // delete the cached child since we are going to replace it. self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); } - // TODO NX!! // subpartial oly let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); From 9657a156f9db325303c2167828442a5df3d71800 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 2 May 2019 18:02:35 +0200 Subject: [PATCH 054/120] Fix regression --- test-support/reference-trie/src/lib.rs | 6 +++++- trie-db/src/iter_build.rs | 13 ++++++++++++- trie-db/src/nibble/mod.rs | 2 +- trie-db/src/triedbmut.rs | 1 - 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 380328ad..754e6151 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -641,7 +641,6 @@ impl NodeCodec for ReferenceNodeCodec { fn ext_node(partial: impl Iterator, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { let mut output = partial_to_key_it::(partial, nb_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); - println!("pkitr: {:x?} {}", &output[..], nb_nibble); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -1043,6 +1042,11 @@ pub fn compare_no_ext_insert_remove( *t.root() }; } + /*{ + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:x?}",t); + }*/ let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 9f2d9054..890d678e 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -675,16 +675,27 @@ mod test { ]); } #[test] - fn fuzz_noext_ins_rem_pref () { + fn fuzz_noext_ins_rem_1 () { let data = vec![ (false, vec![0], vec![251, 255]), (false, vec![0,1], vec![251, 255]), (false, vec![0,1,2], vec![255; 32]), (true, vec![0,1], vec![0, 251]), ]; + compare_no_ext_insert_remove(data); + } + #[test] + fn fuzz_noext_ins_rem_2 () { + let data = vec![ + (false, vec![0x00], vec![0xfd, 0xff]), + (false, vec![0x10, 0x00], vec![1;32]), + (false, vec![0x11, 0x10], vec![0;32]), + (true, vec![0x10, 0x00], vec![]) + ]; compare_no_ext_insert_remove(data); } + #[test] fn two_bytes_nibble_len () { let data = vec![ diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index a54ec37a..31d2b172 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -40,7 +40,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// TODO EMCH check that array acts as constant const PADDING_BITMASK: &'static [(u8, usize)]; /// las ix for nible - const LAST_N_IX: usize = Self::NIBBLE_LEN - 1; + const LAST_N_IX: usize = Self::NIBBLE_PER_BYTE - 1; /// las ix for nible as a u8 (for pattern matching) const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; const SINGLE_BITMASK: u8; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 8723bb22..f32e857a 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1009,7 +1009,6 @@ where let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let mut kc = key.clone(); kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); - let (st, ost, op) = match kc.left() { (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { From 3bfe45153ed6af463043d42a99ba3e5c2d899256 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 3 May 2019 18:47:00 +0200 Subject: [PATCH 055/120] fuzz1 and 2 passing --- test-support/reference-trie/src/lib.rs | 72 +++++++++++++++++++++-- trie-db/src/iter_build.rs | 79 +++++++++++++++----------- trie-db/src/lib.rs | 2 +- trie-db/src/nibble/mod.rs | 23 ++++++-- trie-db/src/nibble/nibbleslice.rs | 54 ++++++++++-------- trie-db/src/nibble/nibblevec.rs | 6 +- trie-db/src/triedbmut.rs | 2 +- 7 files changed, 169 insertions(+), 69 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 754e6151..16bb7775 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -39,7 +39,7 @@ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; -pub use trie_db::{Record, TrieLayOut, NibbleHalf, NibbleOps}; +pub use trie_db::{Record, TrieLayOut, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum @@ -62,16 +62,31 @@ impl TrieLayOut for LayoutNew { type N = NibbleHalf; } +/// Test quarter nibble +pub struct LayoutNewQuarter; + +impl TrieLayOut for LayoutNewQuarter { + const USE_EXTENSION: bool = false; + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodecNoExt; + type N = NibbleQuarter; +} + + + pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, LayoutNew>; +pub type RefTrieDBNoExtQ<'a> = trie_db::TrieDB<'a, LayoutNewQuarter>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, LayoutOri>; pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, LayoutNew>; +pub type RefTrieDBMutNoExtQ<'a> = trie_db::TrieDBMut<'a, LayoutNewQuarter>; pub type RefFatDB<'a> = trie_db::FatDB<'a, LayoutOri>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, LayoutOri>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, LayoutOri>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, LayoutOri, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, LayoutNew, Q>; +pub type RefLookupNoExtQ<'a, Q> = trie_db::Lookup<'a, LayoutNewQuarter, Q>; pub fn ref_trie_root(input: I) -> ::Out where I: IntoIterator, @@ -529,7 +544,7 @@ fn partial_to_key_it>(partial: I, nibble_co fn partial_enc_it>(partial: I, nibble_count: usize, node_kind: NodeKindNoExt) -> Vec { let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); - let mut output = Vec::with_capacity(3 + nibble_count); + let mut output = Vec::with_capacity(3 + (nibble_count / N::NIBBLE_PER_BYTE)); match node_kind { NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), @@ -554,7 +569,7 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if nb_nibble_hpe > 0 { - output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); + output.push(N::masked_right((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -697,7 +712,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && N::masked_left(nb_nibble_hpe as u8, input[0]) != 0 { + if nb_nibble_hpe > 0 && N::masked_left((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) @@ -724,7 +739,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } NodeHeaderNoExt::Leaf(nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && N::masked_left(nb_nibble_hpe as u8, input[0]) != 0 { + if nb_nibble_hpe > 0 && N::masked_left((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) @@ -964,6 +979,53 @@ pub fn compare_impl_no_ext( assert_eq!(root, root_new); } +pub fn compare_impl_no_ext_q( + data: Vec<(Vec,Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMutNoExtQ::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + } + t.root().clone() + }; + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); + println!("{:?}", t); + } + + if root != root_new { + { + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} + + pub fn compare_impl_no_ext_unordered( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 890d678e..b172bfb7 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -23,30 +23,25 @@ use node_codec::NodeCodec; // TODO EMCH use L instead of HC (aka TrieLayout) // TODO EMCH move to NibbleOps to use right constants -fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { +fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { // sorted assertion preventing out of bound for a in 0..v1.len() { if v1[a] == v2[a] { } else { - if (v1[a] >> 4) == (v2[a] >> 4) { - return a * NIBBLE_PER_BYTES + 1; - } else { - return a * NIBBLE_PER_BYTES; - } + return a * N::NIBBLE_PER_BYTE + N::left_common(v1[a], v2[a]); } } - return v1.len() * NIBBLE_PER_BYTES; + return v1.len() * N::NIBBLE_PER_BYTE; } // warn! start at 0 // TODO change biggest_depth?? // warn! slow don't loop on that when possible #[inline(always)] -fn nibble_at(v1: &[u8], ix: usize) -> u8 { - if ix % NIBBLE_PER_BYTES == 0 { - v1[ix / NIBBLE_PER_BYTES] >> 4 - } else { - v1[ix / NIBBLE_PER_BYTES] & 15 - } +fn nibble_at(v1: &[u8], ix: usize) -> u8 { + N::at_left( + (ix % N::NIBBLE_PER_BYTE) as u8, + v1[ix / N::NIBBLE_PER_BYTE] + ) } /* @@ -70,10 +65,10 @@ type CacheNode = Option>; // TODO test others layout // first usize to get nb of added value, second usize last added index // second str is in branch value -struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE], bool, Option)>,PhantomData<(H,C,N)>); +struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE_BUFF], bool, Option)>,PhantomData<(H,C,N)>); #[inline(always)] -fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { +fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE_BUFF ] { [ None, None, None, None, None, None, None, None, @@ -84,8 +79,10 @@ fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE] { /// initially allocated cache const INITIAL_DEPTH: usize = 10; -const NIBBLE_SIZE: usize = 16; -const NIBBLE_PER_BYTES: usize = 2; // 2 ^ 8 / 2 ^ NIBBLE_SIZE +/// This should not exist and be associated const of trie layout +/// TODO test `new_vec_slice_buff` as function of trait and associated type +const NIBBLE_SIZE_BUFF: usize = 16; + impl CacheAccum where H: Hasher, @@ -100,6 +97,17 @@ where CacheAccum(v, PhantomData) } + #[inline(always)] + fn set_elt(&mut self, depth:usize, sl: Option) { + if depth >= self.0.len() { + for _i in self.0.len()..depth + 1 { + self.0.push((new_vec_slice_buff(), false, None)); + } + } + self.0[depth].2 = sl; + self.0[depth].1 = true; + } + #[inline(always)] fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode) { if depth >= self.0.len() { @@ -119,7 +127,7 @@ where #[inline(always)] fn reset_depth(&mut self, depth:usize) { self.0[depth].1 = false; - for i in 0..NIBBLE_SIZE { + for i in 0..N::NIBBLE_LEN { self.0[depth].0[i] = None; } } @@ -130,7 +138,7 @@ where target_depth: usize, (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), ) { - let nibble_value = nibble_at(&k2.as_ref()[..], target_depth); + let nibble_value = nibble_at::(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); // Note: fwiu, having fixed key size, all values are in leaf (no value in @@ -138,7 +146,7 @@ where let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len()); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); let hash = cb_ext.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) @@ -191,7 +199,7 @@ where self.standard_ext(&ref_branch.as_ref()[..], cb_ext, branch_d, is_root, nkey) }; // put hash in parent - let nibble: u8 = nibble_at(&ref_branch.as_ref()[..],d); + let nibble: u8 = nibble_at::(&ref_branch.as_ref()[..],d); self.set_node(d, nibble as usize, Some(h)); } } @@ -314,15 +322,16 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) for (k, v) in iter_input { //println!("!{:?},{:?}",&k.as_ref(),&v.as_ref()); - let common_depth = biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = biggest_depth::(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == prev_val.0.as_ref().len() * NIBBLE_PER_BYTES { + if common_depth == prev_val.0.as_ref().len() * N::NIBBLE_PER_BYTE { //println!("stack {} ", common_depth); // the new key include the previous one : branch value case // just stored value at branch depth - depth_queue.0[common_depth].2 = Some(prev_val.1); - depth_queue.0[common_depth].1 = true; + // TODO EMCH bound check and extend depth queue + depth_queue.set_elt(common_depth, Some(prev_val.1)); + } else if depth_item >= prev_depth { //println!("fv {}", depth_item); // put prev with next (common branch prev val can be flush) @@ -346,7 +355,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) let (k2, v2) = prev_val; let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],prev_depth); let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * NIBBLE_PER_BYTES - nkey.len()); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); cb_ext.process(pr.left(), encoded, true); } else { //println!("fbvl {}", prev_depth); @@ -497,6 +506,7 @@ mod test { compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); compare_impl_no_ext_pk(data.clone()); +// compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { @@ -514,6 +524,11 @@ mod test { let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl_no_ext(data, memdb, hashdb); } + fn compare_impl_no_ext_q(data: Vec<(Vec,Vec)>) { + let memdb = MemoryDB::<_, HashKey<_>, _>::default(); + let hashdb = MemoryDB::, DBValue>::default(); + reference_trie::compare_impl_no_ext_q(data, memdb, hashdb); + } fn compare_impl_no_ext_pk(data: Vec<(Vec,Vec)>) { // let memdb = MemoryDB::<_, HashKey<_>, _>::default(); // let hashdb = MemoryDB::, DBValue>::default(); @@ -687,12 +702,12 @@ mod test { #[test] fn fuzz_noext_ins_rem_2 () { - let data = vec![ - (false, vec![0x00], vec![0xfd, 0xff]), - (false, vec![0x10, 0x00], vec![1;32]), - (false, vec![0x11, 0x10], vec![0;32]), - (true, vec![0x10, 0x00], vec![]) - ]; + let data = vec![ + (false, vec![0x00], vec![0xfd, 0xff]), + (false, vec![0x10, 0x00], vec![1;32]), + (false, vec![0x11, 0x10], vec![0;32]), + (true, vec![0x10, 0x00], vec![]) + ]; compare_no_ext_insert_remove(data); } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index ccd9d0d6..c21472e4 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -95,7 +95,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf}; +pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter}; pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 31d2b172..2f724c99 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -45,21 +45,22 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; const SINGLE_BITMASK: u8; - /// mask a byte from a ix > 0 + /// mask a byte from a ix > 0 (ix being content) #[inline(always)] fn masked_left(ix: u8, b: u8) -> u8 { debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } - /// mask a byte from a ix > 0 + /// mask a byte from a ix > 0 (ix being padding) #[inline(always)] fn masked_right(ix: u8, b: u8) -> u8 { debug_assert!(ix > 0); b & Self::PADDING_BITMASK[ix as usize].0 } /// get value at ix from a right first byte + /// TODO EMCH here left or right does not make lot of sense #[inline(always)] - fn at_right(ix: u8, b: u8) -> u8 { + fn at_left(ix: u8, b: u8) -> u8 { (b & Self::PADDING_BITMASK[ix as usize].0) >> Self::PADDING_BITMASK[ix as usize].1 } @@ -75,7 +76,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy fn at(s: &NibbleSlice, i: usize) -> u8 { let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; - Self::at_right(pad as u8, s.data[ix]) + Self::at_left(pad as u8, s.data[ix]) } #[inline] @@ -92,6 +93,20 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy let s2 = 8 - s1; (s1, s2) } + + /// number of common bit between two left pad byte + #[inline(always)] + fn left_common(a: u8, b: u8) -> usize { + let mut i = 0; + while i < Self::NIBBLE_PER_BYTE { + if (a >> Self::PADDING_BITMASK[i].1) + != (b >> Self::PADDING_BITMASK[i].1) { + break; + } + i += 1; + } + return i; + } } /// half byte nibble prepend encoding diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index fc1e94bf..9cb58f1f 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -148,21 +148,21 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let split = self.offset / N::NIBBLE_PER_BYTE; let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; if nb > 0 { - ((nb, N::masked_right(nb, self.data[split])), &self.data[split + 1 ..]) + ((nb, N::masked_right(N::NIBBLE_PER_BYTE as u8 - nb, self.data[split])), &self.data[split + 1 ..]) } else { ((0,0), &self.data[split..]) } } - /// return encoded value as an iterator + /// return encoded value as a packed byte iterator pub fn right_iter(&'a self) -> impl Iterator + 'a { let (mut first, sl) = self.right(); let mut ix = 0; ::core_::iter::from_fn( move || { if first.0 > 0 { let ix = N::NIBBLE_PER_BYTE - first.0 as usize; - first.0 -= 1; - Some(N::at_right(ix as u8, first.1)) + first.0 = 0; + Some(N::masked_right(ix as u8, first.1)) } else { if ix < sl.len() { ix += 1; @@ -192,17 +192,18 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// get iterator over slice, slow pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { - let mut first = to % 2; - let aligned_i = (self.offset + to) % 2; + let mut pad_res = to % N::NIBBLE_PER_BYTE; + let aligned_i = (self.offset + to) % N::NIBBLE_PER_BYTE; let aligned = aligned_i == 0; - let mut ix = self.offset / 2; - let ix_lim = (self.offset + to) / 2; + let mut ix = self.offset / N::NIBBLE_PER_BYTE; + let ix_lim = (self.offset + to) / N::NIBBLE_PER_BYTE; ::core_::iter::from_fn( move || { if aligned { - if first > 0 { - first = 0; + if pad_res > 0 { + let v = N::masked_right((N::NIBBLE_PER_BYTE - pad_res) as u8, self.data[ix]); + pad_res = 0; ix += 1; - Some(self.data[ix - 1] & (255 >> 4)) + Some(v) } else if ix < ix_lim { ix += 1; Some(self.data[ix - 1]) @@ -210,14 +211,17 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { None } } else { + let (s1, s2) = N::split_shifts(aligned_i); // unaligned - if first > 0 { - first = 0; - Some((self.data[ix] & (255 << 4)) >> 4) + let pad_nib = self.offset % N::NIBBLE_PER_BYTE; + if N::NIBBLE_PER_BYTE - pad_res < N::NIBBLE_PER_BYTE - pad_nib { + let v = self.data[ix] >> s1; + pad_res = 0; + Some(v) } else if ix < ix_lim { ix += 1; - let b1 = (self.data[ix - 1] & (255 >> 4)) << 4; - let b2 = (self.data[ix] & (255 << 4)) >> 4; + let b1 = self.data[ix - 1] << s2; + let b2 = self.data[ix] >> s1; Some(b1 | b2) } else { None @@ -279,6 +283,7 @@ impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { mod tests { use crate::nibble::NibbleSlice; use crate::nibble::NibbleHalf; + use crate::nibble::NibbleQuarter; use crate::nibble::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; @@ -348,6 +353,7 @@ mod tests { #[test] fn range_iter() { let n = NibbleSlice::::new(D); + let n2 = NibbleSlice::::new(D); for i in [ vec![], vec![0x00], @@ -357,7 +363,8 @@ mod tests { vec![0x00, 0x12, 0x34], vec![0x01, 0x23, 0x45], ].iter().enumerate() { - range_iter_test(n, i.0, None, &i.1[..]); + range_iter_test::(n, i.0, None, &i.1[..]); + range_iter_test::(n2, i.0 * 2, None, &i.1[..]); } for i in [ vec![], @@ -367,7 +374,8 @@ mod tests { vec![0x12, 0x34], vec![0x01, 0x23, 0x45], ].iter().enumerate() { - range_iter_test(n, i.0, Some(1), &i.1[..]); + range_iter_test::(n, i.0, Some(1), &i.1[..]); + range_iter_test::(n2, i.0 * 2, Some(2), &i.1[..]); } for i in [ vec![], @@ -376,7 +384,8 @@ mod tests { vec![0x02, 0x34], vec![0x23, 0x45], ].iter().enumerate() { - range_iter_test(n, i.0, Some(2), &i.1[..]); + range_iter_test::(n, i.0, Some(2), &i.1[..]); + range_iter_test::(n2, i.0 * 2, Some(4), &i.1[..]); } for i in [ vec![], @@ -384,13 +393,12 @@ mod tests { vec![0x34], vec![0x03, 0x45], ].iter().enumerate() { - range_iter_test(n, i.0, Some(3), &i.1[..]); + range_iter_test::(n, i.0, Some(3), &i.1[..]); + range_iter_test::(n2, i.0 * 2, Some(6), &i.1[..]); } - - } - fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { + fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { let n = if let Some(i) = mid { n.mid(i) } else { n }; diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 0459a90c..7fe88ccb 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -49,7 +49,7 @@ impl NibbleVec { pub fn at(&self, idx: usize) -> u8 { let ix = idx / N::NIBBLE_PER_BYTE; let pad = idx % N::NIBBLE_PER_BYTE; - N::at_right(pad as u8, self.inner[ix]) + N::at_left(pad as u8, self.inner[ix]) } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. @@ -76,7 +76,7 @@ impl NibbleVec { if i_new != 0 { self.inner.push(N::masked_left(i_new as u8, byte)); } - Some(N::at_right(i_new as u8, byte)) + Some(N::at_left(i_new as u8, byte)) } /// remove n last nibbles. @@ -115,7 +115,7 @@ impl NibbleVec { pub fn append_partial(&mut self, (o_n, sl): Partial) { for i in (1..=o_n.0).rev() { let ix = N::NIBBLE_PER_BYTE - i as usize; - self.push(N::at_right(ix as u8, o_n.1)); + self.push(N::at_left(ix as u8, o_n.1)); } let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; if pad == 0 { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index f32e857a..08c4e3e8 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1365,7 +1365,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, LayoutNew}; + LayoutOri, LayoutNew, LayoutNewQuarter}; fn populate_trie<'db>( db: &'db mut HashDB, From 757f630a2509cd005197e23fd5ea34473840fcc2 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 3 May 2019 21:50:43 +0200 Subject: [PATCH 056/120] a few fix, next needed are combine_key and shifted_key --- trie-db/src/iter_build.rs | 10 +++++----- trie-db/src/nibble/nibbleslice.rs | 4 ++-- trie-db/src/triedb.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index b172bfb7..821c1b92 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -502,11 +502,11 @@ mod test { } fn compare_impl(data: Vec<(Vec,Vec)>) { - compare_impl_h(data.clone()); + /*compare_impl_h(data.clone()); compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); - compare_impl_no_ext_pk(data.clone()); -// compare_impl_no_ext_q(data.clone()); + compare_impl_no_ext_pk(data.clone());*/ + compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { @@ -658,11 +658,11 @@ mod test { compare_impl(vec![ (vec![0],vec![6, 255]), (vec![6],vec![255, 186]), - (vec![255],vec![186, 255]), +//TODO EMCH uncomment (vec![255],vec![186, 255]), ]); } #[test] - fn fuzz_noext2_bis () { + fn fuzz_noext5 () { compare_impl(vec![ (vec![0xaa], vec![0xa0]), (vec![0xaa, 0xaa], vec![0xaa]), diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 9cb58f1f..6d3e73f5 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -62,7 +62,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// helper function to get `NodeKey` stored in nodes pub fn to_stored(&self) -> NodeKey { let split = self.offset / N::NIBBLE_PER_BYTE; - let offset = N::nb_padding(self.len()); + let offset = self.offset % N::NIBBLE_PER_BYTE; (offset, self.data[split..].into()) } @@ -73,7 +73,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { // aligned let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; - (N::nb_padding(nb), ElasticArray36::from_slice(&self.data[start..end])) + (nb % N::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end])) } else { let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index e3c9ca9b..2abfa4d3 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -330,7 +330,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { status: Status::At, node: node.clone().into(), }); - self.key_nibbles.append_partial(slice.right()); + self.key_nibbles.append_partial(slice.right()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); let data = self.db.get_raw_or_lookup(&*item, key.back(full_key_nibbles).left())?; @@ -434,9 +434,9 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } /// The present key. This can only be called on valued node (key is therefore - /// aligned to byte). + /// aligned to byte). fn key(&self) -> NibbleSlice { - self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") + self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") } } From 2704459d0289279915893ac6ba157a71e58ca4ff Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 4 May 2019 18:24:21 +0200 Subject: [PATCH 057/120] fix a few test --- trie-db/src/iter_build.rs | 8 ++++---- trie-db/src/nibble/mod.rs | 29 +++++++++++++++++++++++++++++ trie-db/src/nibble/nibbleslice.rs | 16 +++++----------- trie-db/src/node.rs | 1 + trie-db/src/triedbmut.rs | 30 +++++------------------------- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 821c1b92..e8ea50e5 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -502,11 +502,11 @@ mod test { } fn compare_impl(data: Vec<(Vec,Vec)>) { - /*compare_impl_h(data.clone()); + compare_impl_h(data.clone()); compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); - compare_impl_no_ext_pk(data.clone());*/ - compare_impl_no_ext_q(data.clone()); + compare_impl_no_ext_pk(data.clone()); +// compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { @@ -658,7 +658,7 @@ mod test { compare_impl(vec![ (vec![0],vec![6, 255]), (vec![6],vec![255, 186]), -//TODO EMCH uncomment (vec![255],vec![186, 255]), + (vec![255],vec![186, 255]), ]); } #[test] diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 2f724c99..78ce0f98 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -19,6 +19,7 @@ mod nibbleslice; use ::core_::cmp::*; use ::core_::marker::PhantomData; use elastic_array::ElasticArray36; +use crate::node::NodeKey; pub const EMPTY_ENCODED: (&'static [u8], (u8, u8)) = (&[], (0, 0)); // until const fn for pow @@ -107,6 +108,34 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } return i; } + + /// shift key (right) alignment to match a given left offset, possibly leaving + /// wrong end of Nodekey (eg to combine two keys) + fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { + let old_offset = key.0; + key.0 = ofset; + if old_offset > ofset { + // shift left + let shift = old_offset - ofset; + let (s1, s2) = Self::split_shifts(shift); + let kl = key.1.len(); + (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1); + key.1[kl - 1] = key.1[kl - 1] << s2; + true + } else if old_offset < ofset { + // shift right + let shift = ofset - old_offset; + let (s1, s2) = Self::split_shifts(shift); + key.1.push(0); + (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2); + key.1[0] = key.1[0] >> s2; + true + } else { + false + } + } + + } /// half byte nibble prepend encoding diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 6d3e73f5..23c8c560 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -78,18 +78,12 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); + let ea_offset = self.offset % N::NIBBLE_PER_BYTE; let n_offset = N::nb_padding(nb); - if n_offset == 1 { - let mut result = (0, ea); - crate::triedbmut::shift_key::(&mut result, 1); - result.1.pop(); - result - } else { - let mut result = (1, ea); - crate::triedbmut::shift_key::(&mut result, 0); - result.1.pop(); - result - } + let mut result = (ea_offset, ea); + N::shift_key(&mut result, n_offset); + result.1.pop(); + result } } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 5c729a6b..eee0c983 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -22,6 +22,7 @@ use super::DBValue; use alloc::vec::Vec; /// Partial node key type: offset and owned value of a nibbleslice. +/// Offset is applied on first byte of array. pub type NodeKey = (usize, ElasticArray36); /// Type of node in the trie and essential information thereof. diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 08c4e3e8..8c2bbd11 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1036,14 +1036,13 @@ where match child_node { Node::Leaf(sub_partial, value) => { let mut enc_nibble = enc_nibble; - combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (L::N::NIBBLE_PER_BYTE - 1, &[a][..])); combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); Ok(Node::Leaf(enc_nibble, value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let mut enc_nibble = enc_nibble; - // TODO a + buf + ck - combine_key::(&mut enc_nibble, (1, &[a][..])); + combine_key::(&mut enc_nibble, (L::N::NIBBLE_PER_BYTE - 1, &[a][..])); combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); Ok(Node::NibbledBranch(enc_nibble, ch_children, ch_value)) }, @@ -1321,13 +1320,13 @@ where } } -/// TODO EMCH Consider moving to NibbleOps +/// combine two NodeKeys fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; - let _shifted = shift_key::(start, final_ofset); + let _shifted = N::shift_key(start, final_ofset); let st = if end.0 > 0 { let sl = start.1.len(); - start.1[sl - 1] |= end.1[0] & (255 >> 4); + start.1[sl - 1] |= N::masked_right(end.0 as u8, end.1[0]); 1 } else { 0 @@ -1335,25 +1334,6 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); } -/// TODO EMCH Consider moving to NibbleOps -pub(crate) fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { - let old = key.0; - key.0 = ofset; - if old > ofset { - let kl = key.1.len(); - (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << 4 | key.1[i+1]>>4); - key.1[kl - 1] = key.1[kl - 1] << 4; - true - } else if old < ofset { - key.1.push(0); - (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << 4 | key.1[i] >> 4); - key.1[0] = key.1[0] >> 4; - true - } else { - false - } -} - #[cfg(test)] mod tests { use env_logger; From d844cd22fc9301f9c4948050d28db39074fed1a7 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sun, 5 May 2019 12:04:16 +0200 Subject: [PATCH 058/120] fix range iter --- trie-db/src/iter_build.rs | 2 +- trie-db/src/nibble/nibbleslice.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index e8ea50e5..fec81352 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -506,7 +506,7 @@ mod test { compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); compare_impl_no_ext_pk(data.clone()); -// compare_impl_no_ext_q(data.clone()); + compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 23c8c560..29a3eee1 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -186,16 +186,16 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// get iterator over slice, slow pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { - let mut pad_res = to % N::NIBBLE_PER_BYTE; + let mut nib_res = to % N::NIBBLE_PER_BYTE; let aligned_i = (self.offset + to) % N::NIBBLE_PER_BYTE; let aligned = aligned_i == 0; let mut ix = self.offset / N::NIBBLE_PER_BYTE; let ix_lim = (self.offset + to) / N::NIBBLE_PER_BYTE; ::core_::iter::from_fn( move || { if aligned { - if pad_res > 0 { - let v = N::masked_right((N::NIBBLE_PER_BYTE - pad_res) as u8, self.data[ix]); - pad_res = 0; + if nib_res > 0 { + let v = N::masked_right((N::NIBBLE_PER_BYTE - nib_res) as u8, self.data[ix]); + nib_res = 0; ix += 1; Some(v) } else if ix < ix_lim { @@ -207,10 +207,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } else { let (s1, s2) = N::split_shifts(aligned_i); // unaligned - let pad_nib = self.offset % N::NIBBLE_PER_BYTE; - if N::NIBBLE_PER_BYTE - pad_res < N::NIBBLE_PER_BYTE - pad_nib { + if nib_res > 0 { let v = self.data[ix] >> s1; - pad_res = 0; + let v = N::masked_right((N::NIBBLE_PER_BYTE - nib_res) as u8, v); + nib_res = 0; Some(v) } else if ix < ix_lim { ix += 1; From 8f428f17ee8b88783f6ee66f46fa39484bbc9d65 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 May 2019 12:15:10 +0200 Subject: [PATCH 059/120] remove a few todos and fix bench (except memdb). --- memory-db/benches/bench.rs | 17 +++---- test-support/reference-trie/benches/bench.rs | 3 +- test-support/reference-trie/src/lib.rs | 9 ++-- trie-db/benches/bench.rs | 4 +- trie-db/src/iter_build.rs | 30 ++----------- trie-db/src/nibble/mod.rs | 23 +++++++++- trie-db/src/nibble/nibblevec.rs | 47 ++++++++++++++++++++ trie-db/src/node_codec.rs | 3 -- trie-db/src/triedb.rs | 16 ++----- trie-db/src/triedbmut.rs | 31 ++----------- 10 files changed, 95 insertions(+), 88 deletions(-) diff --git a/memory-db/benches/bench.rs b/memory-db/benches/bench.rs index 20cb9e55..8aa3120e 100644 --- a/memory-db/benches/bench.rs +++ b/memory-db/benches/bench.rs @@ -24,12 +24,13 @@ extern crate memory_db; extern crate keccak_hasher; use memory_db::MemoryDB; +use memory_db::HashKey; use keccak_hasher::KeccakHasher; -use hash_db::{HashDB, Hasher}; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; fn instantiation(b: &mut Criterion) { b.bench_function("instantiation", |b| b.iter(|| { - MemoryDB::>::default(); + MemoryDB::, Vec>::default(); })); } @@ -52,19 +53,19 @@ fn compare_to_null_in_const(b: &mut Criterion) { } fn contains_with_non_null_key(b: &mut Criterion) { - let mut m = MemoryDB::>::default(); + let mut m = MemoryDB::, Vec>::default(); let key = KeccakHasher::hash(b"abc"); - m.insert(b"abcefghijklmnopqrstuvxyz"); + m.insert(EMPTY_PREFIX, b"abcefghijklmnopqrstuvxyz"); b.bench_function("contains_with_non_null_key", |b| b.iter(|| { - m.contains(&key); + m.contains(&key, EMPTY_PREFIX); })); } fn contains_with_null_key(b: &mut Criterion) { - let mut m = MemoryDB::>::default(); + let mut m = MemoryDB::, Vec>::default(); let null_key = KeccakHasher::hash(&[0u8][..]); - m.insert(b"abcefghijklmnopqrstuvxyz"); + m.insert(EMPTY_PREFIX, b"abcefghijklmnopqrstuvxyz"); b.bench_function("contains_with_null_key", |b| b.iter(|| { - m.contains(&null_key); + m.contains(&null_key, EMPTY_PREFIX); })); } diff --git a/test-support/reference-trie/benches/bench.rs b/test-support/reference-trie/benches/bench.rs index ed183d78..e8868c88 100644 --- a/test-support/reference-trie/benches/bench.rs +++ b/test-support/reference-trie/benches/bench.rs @@ -24,8 +24,7 @@ extern crate trie_bench; fn benchmark(c: &mut Criterion) { trie_bench::standard_benchmark::< - keccak_hasher::KeccakHasher, - reference_trie::ReferenceNodeCodec, + reference_trie::LayoutOri, reference_trie::ReferenceTrieStream, >(c, "ref"); } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 16bb7775..64604dd4 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1005,16 +1005,17 @@ pub fn compare_impl_no_ext_q( if root != root_new { { - let db : &dyn hash_db::HashDB<_,_> = &memdb; - let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); + let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); } } + { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; - let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); + let db : &dyn hash_db::HashDB<_,_> = &memdb; + let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 7ba1e013..af57dcc1 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -26,7 +26,7 @@ extern crate trie_standardmap; extern crate trie_db; extern crate rand; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; -use trie_db::NibbleSlice; +use trie_db::{NibbleSlice, NibbleHalf}; fn nibble_common_prefix(b: &mut Criterion) { let st = StandardMap { @@ -39,7 +39,7 @@ fn nibble_common_prefix(b: &mut Criterion) { let (keys, values): (Vec<_>, Vec<_>) = st.make().into_iter().unzip(); b.bench_function("nibble_common_prefix", move |b| { let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { - (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) + (NibbleSlice::::new(pair.0), NibbleSlice::::new(pair.1)) }).collect(); b.iter(&mut ||{ diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index fec81352..52433358 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -21,28 +21,6 @@ use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; use node_codec::NodeCodec; -// TODO EMCH use L instead of HC (aka TrieLayout) -// TODO EMCH move to NibbleOps to use right constants -fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { - // sorted assertion preventing out of bound - for a in 0..v1.len() { - if v1[a] == v2[a] { - } else { - return a * N::NIBBLE_PER_BYTE + N::left_common(v1[a], v2[a]); - } - } - return v1.len() * N::NIBBLE_PER_BYTE; -} - -// warn! start at 0 // TODO change biggest_depth?? -// warn! slow don't loop on that when possible -#[inline(always)] -fn nibble_at(v1: &[u8], ix: usize) -> u8 { - N::at_left( - (ix % N::NIBBLE_PER_BYTE) as u8, - v1[ix / N::NIBBLE_PER_BYTE] - ) -} /* // TODO remove for nibbleslice api TODO can be variable size @@ -138,7 +116,7 @@ where target_depth: usize, (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), ) { - let nibble_value = nibble_at::(&k2.as_ref()[..], target_depth); + let nibble_value = N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); // Note: fwiu, having fixed key size, all values are in leaf (no value in @@ -199,7 +177,7 @@ where self.standard_ext(&ref_branch.as_ref()[..], cb_ext, branch_d, is_root, nkey) }; // put hash in parent - let nibble: u8 = nibble_at::(&ref_branch.as_ref()[..],d); + let nibble: u8 = N::left_nibble_at(&ref_branch.as_ref()[..],d); self.set_node(d, nibble as usize, Some(h)); } } @@ -322,16 +300,14 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) for (k, v) in iter_input { //println!("!{:?},{:?}",&k.as_ref(),&v.as_ref()); - let common_depth = biggest_depth::(&prev_val.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; if common_depth == prev_val.0.as_ref().len() * N::NIBBLE_PER_BYTE { //println!("stack {} ", common_depth); // the new key include the previous one : branch value case // just stored value at branch depth - // TODO EMCH bound check and extend depth queue depth_queue.set_elt(common_depth, Some(prev_val.1)); - } else if depth_item >= prev_depth { //println!("fv {}", depth_item); // put prev with next (common branch prev val can be flush) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 78ce0f98..0534998a 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -38,7 +38,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); /// padding bitmasks (could be calculated with a constant function). /// First is bit mask to apply, second is right shift needed. - /// TODO EMCH check that array acts as constant const PADDING_BITMASK: &'static [(u8, usize)]; /// las ix for nible const LAST_N_IX: usize = Self::NIBBLE_PER_BYTE - 1; @@ -59,13 +58,21 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy b & Self::PADDING_BITMASK[ix as usize].0 } /// get value at ix from a right first byte - /// TODO EMCH here left or right does not make lot of sense #[inline(always)] fn at_left(ix: u8, b: u8) -> u8 { (b & Self::PADDING_BITMASK[ix as usize].0) >> Self::PADDING_BITMASK[ix as usize].1 } + /// get nibble for left aligned array + #[inline(always)] + fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { + Self::at_left( + (ix % Self::NIBBLE_PER_BYTE) as u8, + v1[ix / Self::NIBBLE_PER_BYTE] + ) + } + /// push u8 nib value at ix into a existing byte #[inline(always)] fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { @@ -95,6 +102,18 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy (s1, s2) } + /// get biggest common depth between two left aligned packed nibble arrays + fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { + // sorted assertion preventing out of bound + for a in 0..v1.len() { + if v1[a] == v2[a] { + } else { + return a * Self::NIBBLE_PER_BYTE + Self::left_common(v1[a], v2[a]); + } + } + return v1.len() * Self::NIBBLE_PER_BYTE; + } + /// number of common bit between two left pad byte #[inline(always)] fn left_common(a: u8, b: u8) -> usize { diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 7fe88ccb..744d68c3 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -111,6 +111,24 @@ impl NibbleVec { } } + /// push a full partial. + pub fn append(&mut self, v: &NibbleVec) { + + if v.len == 0 { return; } + let offset = self.len % N::NIBBLE_PER_BYTE; + let last_ix = self.len / N::NIBBLE_PER_BYTE; + if offset > 0 { + let (s1, s2) = N::split_shifts(offset); + self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); + (0..v.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); + } else { + (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); + } + self.len += v.len; + } + + + /// push a full partial. pub fn append_partial(&mut self, (o_n, sl): Partial) { for i in (1..=o_n.0).rev() { @@ -133,6 +151,35 @@ impl NibbleVec { self.len += sl.len() * N::NIBBLE_PER_BYTE; } + /// append slice or nibble + pub fn append_slice_nibble( + &mut self, + o_sl: Option<&NibbleSlice>, + o_ix: Option + ) -> usize { + let mut res = 0; + if let Some(sl) = o_sl { + self.append_partial(sl.right()); + res += sl.len(); + } + if let Some(ix) = o_ix { + self.push(ix); + res += 1; + } + res + } + /// clone then append slice or nibble + pub fn clone_append_slice_nibble( + &self, + o_sl: Option<&NibbleSlice>, + o_ix: Option + ) -> Self { + let mut p = self.clone(); + p.append_slice_nibble(o_sl, o_ix); + p + } + + /// Get the underlying byte slice. pub fn inner(&self) -> &[u8] { &self.inner[..] diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 35fcc774..127af9e4 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -48,9 +48,6 @@ pub trait NodeCodec: Sized { /// Codec error type type Error: Error; - // TODO EMCH since refact to use nibble ops: use of this method is super awkward - // -> making reference code implement statically other nibble could be the simpler - // way of fixing that /// Get the hashed null node. fn hashed_null_node() -> H::Out; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 2abfa4d3..bb4e91c2 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -19,7 +19,6 @@ use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; -use triedbmut::{concat_key_clone}; use super::nibble::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; @@ -175,7 +174,7 @@ where .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: concat_key_clone(&self.partial_key, Some(&slice), None), + partial_key: self.partial_key.clone_append_slice_nibble(Some(&slice), None), index: None, }) .finish(), @@ -187,7 +186,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: concat_key_clone(&self.partial_key, None, Some(i as u8)), + partial_key: self.partial_key.clone_append_slice_nibble(None, Some(i as u8)), }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -206,7 +205,7 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: concat_key_clone(&self.partial_key, Some(&slice), Some(i as u8)), + partial_key: self.partial_key.clone_append_slice_nibble(Some(&slice), Some(i as u8)), }).collect(); match (f.debug_struct("Node::NibbledBranch"), self.index) { (ref mut d, Some(ref i)) => d.field("index", i), @@ -421,14 +420,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) | &OwnedNode::NibbledBranch(ref n, _) - => { - if let Some(part) = n.as_nibbleslice() { - key_nibbles.append_partial(part.right()); - } else { - // TODO EMCH range_iter on nibble vec or simply cat of two nibblevec - (0..n.len()).for_each(|i|key_nibbles.push(n.at(i))); - } - }, + => key_nibbles.append(n), _ => {} } } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 8c2bbd11..45cf2c75 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -204,8 +204,6 @@ where .map(|(i, maybe_child)|{ //let branch_ix = [i as u8]; maybe_child.map(|child| { - // TODO EMCH this clone should be avoid by having a lower limit to pkm - // and reseting each time (also good to secure pop!!) let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); child_cb(child, Some(&pr), Some(i as u8)) }) @@ -1003,8 +1001,6 @@ where match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { - // TODO EMCH can simplify code by transforming to an extension like in branch - // (extension only temp value before being transformed). // only one onward node. use child instead let child = children[a as usize].take().expect("used_index only set if occupied; qed"); let mut kc = key.clone(); @@ -1013,7 +1009,7 @@ where (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { let mut so: ElasticArray36 = st.into(); - so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); // TODO replace by push_at left?? + so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); (st, Some(so), (0,0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), @@ -1154,7 +1150,7 @@ where Stored::New(node) => { let mut k = NibbleVec::new(); let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { - let mov = concat_key(&mut k, o_sl, o_ix); + let mov = k.append_slice_nibble(o_sl, o_ix); let cr = self.commit_child(child, &mut k); k.drop_lasts(mov); cr @@ -1187,7 +1183,7 @@ where Stored::New(node) => { let encoded = { let commit_child = |node_handle, o_sl: Option<&NibbleSlice>, o_ix: Option| { - let mov = concat_key(prefix, o_sl, o_ix); + let mov = prefix.append_slice_nibble(o_sl, o_ix); let cr = self.commit_child(node_handle, prefix); prefix.drop_lasts(mov); cr @@ -1221,27 +1217,6 @@ where } -// TODO EMCH change usage here to run on self buffer -// TODO EMCH a with_concat_key function using a closure and truncating correctly -pub(crate) fn concat_key(prefix: &mut NibbleVec, o_sl: Option<&NibbleSlice>, o_ix: Option) -> usize { - let mut res = 0; - if let Some(sl) = o_sl { - prefix.append_partial(sl.right()); - res += sl.len(); - } - if let Some(ix) = o_ix { - prefix.push(ix); - res += 1; - } - res -} - -pub(crate) fn concat_key_clone(prefix: &NibbleVec, o_sl: Option<&NibbleSlice>, o_ix: Option) -> NibbleVec { - let mut p = prefix.clone(); - concat_key(&mut p, o_sl, o_ix); - p -} - impl<'a, L> TrieMut for TrieDBMut<'a, L> where L: TrieLayOut, From e88dbc2d520a638d18d5ec88b2fb2dc2b546c219 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 May 2019 15:21:59 +0200 Subject: [PATCH 060/120] cleaning --- trie-db/src/iter_build.rs | 59 ++++++------------------- trie-db/src/nibble/mod.rs | 43 +++++++++--------- trie-db/src/nibble/nibbleslice.rs | 2 +- trie-db/src/nibble/nibblevec.rs | 72 +++++++++++++++---------------- trie-db/src/node_codec.rs | 1 + trie-db/src/triedb.rs | 22 +++++----- trie-db/src/triedbmut.rs | 8 ++-- 7 files changed, 87 insertions(+), 120 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 52433358..1d797f03 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -22,25 +22,9 @@ use crate::nibble::NibbleOps; use node_codec::NodeCodec; -/* -// TODO remove for nibbleslice api TODO can be variable size -fn encoded_nibble(ori: &[u8], is_leaf: bool) -> ElasticArray36 { - let l = ori.len(); - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {0x10 + ori[0]} else {0} + if is_leaf {0x20} else {0}); - while i < l { - r.push(ori[i] * 16 + ori[i+1]); - i += 2; - } - r -} -*/ - type CacheNode = Option>; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) -// TODO test others layout // first usize to get nb of added value, second usize last added index // second str is in branch value struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE_BUFF], bool, Option)>,PhantomData<(H,C,N)>); @@ -58,7 +42,7 @@ fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE_BUFF ] { /// initially allocated cache const INITIAL_DEPTH: usize = 10; /// This should not exist and be associated const of trie layout -/// TODO test `new_vec_slice_buff` as function of trait and associated type +/// TODO EMCH test `new_vec_slice_buff` as function of trait and associated type const NIBBLE_SIZE_BUFF: usize = 16; impl CacheAccum @@ -119,11 +103,7 @@ where let nibble_value = N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); - // Note: fwiu, having fixed key size, all values are in leaf (no value in - // branch). TODO run metrics on a node to count branch with values let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); - // TODO redesign nibleslice encoded to allow an inputstream (avoid this alloc) + design the - // thing other array of slice to avoid those concatenation unsecure or costy + same for nodes let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); let hash = cb_ext.process(pr.left(), encoded, false); @@ -151,8 +131,6 @@ where let last_root = d == 0 && is_last; // reduce slice for branch let parent_branch = touched; - // TODO change this offset to not use nibble slice api (makes it hard to get the index - // thing) let (slice_size, offset) = if parent_branch && last_root { // corner branch last (branch_d - d - 1, d + 1) @@ -294,55 +272,47 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) // compare iter ordering let mut iter_input = input.into_iter(); if let Some(mut prev_val) = iter_input.next() { - //println!("!st{:?},{:?}",&prev_val.0.as_ref(),&prev_val.1.as_ref()); - // depth of last item TODO rename to last_depth - let mut prev_depth = 0; + // depth of last item + let mut last_depth = 0; for (k, v) in iter_input { - //println!("!{:?},{:?}",&k.as_ref(),&v.as_ref()); let common_depth = N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; if common_depth == prev_val.0.as_ref().len() * N::NIBBLE_PER_BYTE { - //println!("stack {} ", common_depth); // the new key include the previous one : branch value case // just stored value at branch depth depth_queue.set_elt(common_depth, Some(prev_val.1)); - } else if depth_item >= prev_depth { - //println!("fv {}", depth_item); + } else if depth_item >= last_depth { // put prev with next (common branch prev val can be flush) depth_queue.flush_val(cb_ext, depth_item, &prev_val); - } else if depth_item < prev_depth { - //println!("fbv {}", prev_depth); + } else if depth_item < last_depth { // do not put with next, previous is last of a branch - depth_queue.flush_val(cb_ext, prev_depth, &prev_val); + depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - //println!("fb {} {}", depth_item, prev_depth); - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, prev_depth, false); // TODO flush at prev flush depth instead ?? + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, last_depth, false); } prev_val = (k, v); - prev_depth = depth_item; + last_depth = depth_item; } // last pendings - if prev_depth == 0 + if last_depth == 0 && !depth_queue.touched(0) { // one single element corner case let (k2, v2) = prev_val; - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],prev_depth); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],last_depth); let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); cb_ext.process(pr.left(), encoded, true); } else { - //println!("fbvl {}", prev_depth); - depth_queue.flush_val(cb_ext, prev_depth, &prev_val); + depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - //println!("fbl {} {}", 0, prev_depth); - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, prev_depth, true); + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, last_depth, true); } } else { - // nothing null root corner case TODO warning hardcoded empty nibbleslice - cb_ext.process(crate::nibble::EMPTY_ENCODED, C::empty_node().to_vec(), true); + // nothing null root corner case + cb_ext.process(crate::nibble::EMPTY_NIBBLE, C::empty_node().to_vec(), true); } } @@ -376,7 +346,6 @@ impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> f } let hash = self.db.insert(encoded_prefix, &enc_ext[..]); if is_root { - //println!("isroot touch"); self.root = Some(hash.clone()); }; ChildReference::Hash(hash) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 0534998a..dadd7083 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -21,7 +21,7 @@ use ::core_::marker::PhantomData; use elastic_array::ElasticArray36; use crate::node::NodeKey; -pub const EMPTY_ENCODED: (&'static [u8], (u8, u8)) = (&[], (0, 0)); +pub const EMPTY_NIBBLE: (&'static [u8], (u8, u8)) = (&[], (0, 0)); // until const fn for pow const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Nibble specific variants @@ -43,7 +43,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy const LAST_N_IX: usize = Self::NIBBLE_PER_BYTE - 1; /// las ix for nible as a u8 (for pattern matching) const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; - const SINGLE_BITMASK: u8; /// mask a byte from a ix > 0 (ix being content) #[inline(always)] @@ -64,14 +63,14 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy >> Self::PADDING_BITMASK[ix as usize].1 } - /// get nibble for left aligned array - #[inline(always)] - fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { - Self::at_left( - (ix % Self::NIBBLE_PER_BYTE) as u8, - v1[ix / Self::NIBBLE_PER_BYTE] - ) - } + /// get nibble for left aligned array + #[inline(always)] + fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { + Self::at_left( + (ix % Self::NIBBLE_PER_BYTE) as u8, + v1[ix / Self::NIBBLE_PER_BYTE] + ) + } /// push u8 nib value at ix into a existing byte #[inline(always)] @@ -102,17 +101,17 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy (s1, s2) } - /// get biggest common depth between two left aligned packed nibble arrays - fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { - // sorted assertion preventing out of bound - for a in 0..v1.len() { - if v1[a] == v2[a] { - } else { - return a * Self::NIBBLE_PER_BYTE + Self::left_common(v1[a], v2[a]); - } - } - return v1.len() * Self::NIBBLE_PER_BYTE; - } + /// get biggest common depth between two left aligned packed nibble arrays + fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { + // sorted assertion preventing out of bound + for a in 0..v1.len() { + if v1[a] == v2[a] { + } else { + return a * Self::NIBBLE_PER_BYTE + Self::left_common(v1[a], v2[a]); + } + } + return v1.len() * Self::NIBBLE_PER_BYTE; + } /// number of common bit between two left pad byte #[inline(always)] @@ -178,7 +177,6 @@ pub enum ByteLayout { impl NibbleOps for NibbleHalf { const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; - const SINGLE_BITMASK: u8 = 0x0F; } #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] @@ -193,7 +191,6 @@ impl NibbleOps for NibbleQuarter { (0b0000_1111, 2), (0b0000_0011, 0), ]; - const SINGLE_BITMASK: u8 = 0b0000_0011; } diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 29a3eee1..d563f8ab 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -78,7 +78,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); - let ea_offset = self.offset % N::NIBBLE_PER_BYTE; + let ea_offset = self.offset % N::NIBBLE_PER_BYTE; let n_offset = N::nb_padding(nb); let mut result = (ea_offset, ea); N::shift_key(&mut result, n_offset); diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 744d68c3..ccab2679 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -111,20 +111,20 @@ impl NibbleVec { } } - /// push a full partial. + /// push a full partial. pub fn append(&mut self, v: &NibbleVec) { - if v.len == 0 { return; } + if v.len == 0 { return; } let offset = self.len % N::NIBBLE_PER_BYTE; let last_ix = self.len / N::NIBBLE_PER_BYTE; - if offset > 0 { - let (s1, s2) = N::split_shifts(offset); - self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); + if offset > 0 { + let (s1, s2) = N::split_shifts(offset); + self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); (0..v.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); - } else { - (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); - } - self.len += v.len; + } else { + (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); + } + self.len += v.len; } @@ -151,33 +151,33 @@ impl NibbleVec { self.len += sl.len() * N::NIBBLE_PER_BYTE; } - /// append slice or nibble - pub fn append_slice_nibble( - &mut self, - o_sl: Option<&NibbleSlice>, - o_ix: Option - ) -> usize { - let mut res = 0; - if let Some(sl) = o_sl { - self.append_partial(sl.right()); - res += sl.len(); - } - if let Some(ix) = o_ix { - self.push(ix); - res += 1; - } - res - } - /// clone then append slice or nibble - pub fn clone_append_slice_nibble( - &self, - o_sl: Option<&NibbleSlice>, - o_ix: Option - ) -> Self { - let mut p = self.clone(); - p.append_slice_nibble(o_sl, o_ix); - p - } + /// append slice or nibble + pub fn append_slice_nibble( + &mut self, + o_sl: Option<&NibbleSlice>, + o_ix: Option + ) -> usize { + let mut res = 0; + if let Some(sl) = o_sl { + self.append_partial(sl.right()); + res += sl.len(); + } + if let Some(ix) = o_ix { + self.push(ix); + res += 1; + } + res + } + /// clone then append slice or nibble + pub fn clone_append_slice_nibble( + &self, + o_sl: Option<&NibbleSlice>, + o_ix: Option + ) -> Self { + let mut p = self.clone(); + p.append_slice_nibble(o_sl, o_ix); + p + } /// Get the underlying byte slice. diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 127af9e4..1d832ae9 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -44,6 +44,7 @@ pub type Partial<'a> = ((u8,u8), &'a[u8]); /// Trait for trie node encoding/decoding /// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> /// avoid Vec by all means. +/// TODO also add inner trait impl for nibble ops encoding pub trait NodeCodec: Sized { /// Codec error type type Error: Error; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index bb4e91c2..3551a5ce 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -14,7 +14,7 @@ use hash_db::{HashDBRef, Prefix}; use nibble::{NibbleSlice, NibbleOps}; -use nibble::EMPTY_ENCODED as NIBBLE_EMPTY_ENCODED; +use nibble::EMPTY_NIBBLE; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; @@ -80,7 +80,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { - if !db.contains(root, NIBBLE_EMPTY_ENCODED) { + if !db.contains(root, EMPTY_NIBBLE) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { Ok(TrieDB {db, root, hash_count: 0}) @@ -93,7 +93,7 @@ where /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, NIBBLE_EMPTY_ENCODED) + .get(self.root, EMPTY_NIBBLE) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } @@ -145,7 +145,7 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: NibbleVec, + partial_key: NibbleVec, index: Option, } @@ -246,7 +246,7 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: NibbleVec::new(), + partial_key: NibbleVec::new(), index: None, }) .finish() @@ -382,7 +382,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { status: Status::AtChild(i as usize), node: node.clone().into(), }); - self.key_nibbles.append_partial(slice.right()); + self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push(i); if let Some(ref child) = nodes[i as usize] { full_key_nibbles += slice.len() + 1; @@ -413,8 +413,8 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { /// Descend into a payload. fn descend_into_node(&mut self, node: OwnedNode) { - let trail = &mut self.trail; - let key_nibbles = &mut self.key_nibbles; + let trail = &mut self.trail; + let key_nibbles = &mut self.key_nibbles; trail.push(Crumb { status: Status::Entering, node }); match &trail.last().expect("just pushed item; qed").node { &OwnedNode::Leaf(ref n, _) @@ -490,9 +490,9 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { match i { 0 => self.key_nibbles.push(0), i => { - self.key_nibbles.pop(); - self.key_nibbles.push(i as u8); - }, + self.key_nibbles.pop(); + self.key_nibbles.push(i as u8); + }, } IterStep::Descend::, CError>(self.db.get_raw_or_lookup( &branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 45cf2c75..d5875ff8 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -20,7 +20,7 @@ use super::node::Node as EncodedNode; use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; -use nibble::EMPTY_ENCODED as NIBBLE_EMPTY_ENCODED; +use nibble::EMPTY_NIBBLE; use hash_db::{HashDB, Hasher, Prefix}; use nibble::{NibbleVec, NibbleSlice, NibbleOps}; use elastic_array::ElasticArray36; @@ -378,7 +378,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { - if !db.contains(root, NIBBLE_EMPTY_ENCODED) { + if !db.contains(root, EMPTY_NIBBLE) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -1067,7 +1067,7 @@ where (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { let mut so: ElasticArray36 = st.into(); - so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); // TODO replace by push_at left?? + so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); (st, Some(so), (0,0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), @@ -1156,7 +1156,7 @@ where cr }); trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(NIBBLE_EMPTY_ENCODED, &encoded_root[..]); + *self.root = self.db.insert(EMPTY_NIBBLE, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); From 8a4f75ecc4b7fc159bd5fc6a0bba3a7afa0fee46 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 May 2019 18:22:14 +0200 Subject: [PATCH 061/120] Abstract buff size of iter_build --- test-support/reference-trie/src/lib.rs | 23 +++++--- trie-db/src/iter_build.rs | 80 ++++++++++++++++++-------- trie-db/src/lib.rs | 4 +- trie-db/src/nibble/mod.rs | 1 - 4 files changed, 72 insertions(+), 36 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 64604dd4..038f2e49 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -34,6 +34,8 @@ use trie_db::{ TrieBuilder, TrieRoot, Partial, + Cache16, + Cache4, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; @@ -50,6 +52,7 @@ impl TrieLayOut for LayoutOri { type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodec; type N = NibbleHalf; + type CB = Cache16; } /// trie layout similar to substrate one @@ -60,6 +63,7 @@ impl TrieLayOut for LayoutNew { type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; type N = NibbleHalf; + type CB = Cache16; } /// Test quarter nibble @@ -70,6 +74,7 @@ impl TrieLayOut for LayoutNewQuarter { type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; type N = NibbleQuarter; + type CB = Cache4; } @@ -822,7 +827,7 @@ pub fn compare_impl + Eq> ( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -864,7 +869,7 @@ pub fn compare_root( ) { let root_new = { let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -884,7 +889,7 @@ pub fn compare_unhashed( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed(data); @@ -897,7 +902,7 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -916,7 +921,7 @@ pub fn calc_root( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -929,7 +934,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -940,7 +945,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -986,7 +991,7 @@ pub fn compare_impl_no_ext_q( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit_no_ext::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -1044,7 +1049,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit_no_ext::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 1d797f03..b35d80c8 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -21,23 +21,49 @@ use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; use node_codec::NodeCodec; +macro_rules! exp_disp { + (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; + (@2, [$($inpp:expr),*]) => { exp_disp!(@1, [$($inpp,)* $($inpp),*]) }; + (@1, [$($inpp:expr),*]) => { [$($inpp,)* $($inpp),*] }; +} type CacheNode = Option>; +/// a builder for fix constant len cache, should match nibble ops `NIBBLE_LEN` +pub trait CacheBuilder { + /// size of cache + const SIZE: usize; + /// the fix cache + type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; + /// builder for cache + fn new_vec_slice_buff() -> Self::AN; +} + +pub struct Cache16; +pub struct Cache4; + +impl CacheBuilder for Cache16 { + const SIZE: usize = 16; + type AN = [CacheNode; 16]; + #[inline(always)] + fn new_vec_slice_buff() -> Self::AN { + exp_disp!(@3, [None,None]) + } +} + +impl CacheBuilder for Cache4 { + const SIZE: usize = 4; + type AN = [CacheNode; 4]; + #[inline(always)] + fn new_vec_slice_buff() -> Self::AN { + exp_disp!(@2, [None]) + } +} + // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index // second str is in branch value -struct CacheAccum (Vec<([CacheNode<::Out>; NIBBLE_SIZE_BUFF], bool, Option)>,PhantomData<(H,C,N)>); - -#[inline(always)] -fn new_vec_slice_buff() -> [CacheNode; NIBBLE_SIZE_BUFF ] { - [ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ] -} +struct CacheAccum,V> (Vec<(CB::AN, bool, Option)>,PhantomData<(H,C,N,CB)>); /// initially allocated cache const INITIAL_DEPTH: usize = 10; @@ -45,17 +71,18 @@ const INITIAL_DEPTH: usize = 10; /// TODO EMCH test `new_vec_slice_buff` as function of trait and associated type const NIBBLE_SIZE_BUFF: usize = 16; -impl CacheAccum +impl CacheAccum where H: Hasher, N: NibbleOps, + CB: CacheBuilder<::Out>, C: NodeCodec, V: AsRef<[u8]>, { fn new() -> Self { let mut v = Vec::with_capacity(INITIAL_DEPTH); - (0..INITIAL_DEPTH).for_each(|_|v.push((new_vec_slice_buff(), false, None))); + (0..INITIAL_DEPTH).for_each(|_|v.push((CB::new_vec_slice_buff(), false, None))); CacheAccum(v, PhantomData) } @@ -63,7 +90,7 @@ where fn set_elt(&mut self, depth:usize, sl: Option) { if depth >= self.0.len() { for _i in self.0.len()..depth + 1 { - self.0.push((new_vec_slice_buff(), false, None)); + self.0.push((CB::new_vec_slice_buff(), false, None)); } } self.0[depth].2 = sl; @@ -74,10 +101,10 @@ where fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode) { if depth >= self.0.len() { for _i in self.0.len()..depth + 1 { - self.0.push((new_vec_slice_buff(), false, None)); + self.0.push((CB::new_vec_slice_buff(), false, None)); } } - self.0[depth].0[nibble_ix] = node; + self.0[depth].0.as_mut()[nibble_ix] = node; self.0[depth].1 = true; } @@ -90,7 +117,7 @@ where fn reset_depth(&mut self, depth:usize) { self.0[depth].1 = false; for i in 0..N::NIBBLE_LEN { - self.0[depth].0[i] = None; + self.0[depth].0.as_mut()[i] = None; } } @@ -189,7 +216,7 @@ where // enc branch let v = self.0[branch_d].2.take(); - let encoded = C::branch_node(self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); + let encoded = C::branch_node(self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); @@ -222,7 +249,7 @@ where // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); pr.right_range_iter(nkeyix.1), nkeyix.1, - self.0[branch_d].0.iter(), v.as_ref().map(|v|v.as_ref())); + self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); @@ -231,44 +258,47 @@ where } -pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) +pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, N: NibbleOps, + CB: CacheBuilder<::Out>, C: NodeCodec, F: ProcessEncodedNode<::Out>, { - trie_visit_inner::(input, cb_ext, true) + trie_visit_inner::(input, cb_ext, true) } -pub fn trie_visit(input: I, cb_ext: &mut F) +pub fn trie_visit(input: I, cb_ext: &mut F) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, N: NibbleOps, + CB: CacheBuilder<::Out>, C: NodeCodec, F: ProcessEncodedNode<::Out>, { - trie_visit_inner::(input, cb_ext, false) + trie_visit_inner::(input, cb_ext, false) } // put no_ext as a trait: probably not worth it (fn designed for that)? -fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) +fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, N: NibbleOps, + CB: CacheBuilder<::Out>, C: NodeCodec, F: ProcessEncodedNode<::Out>, { - let mut depth_queue = CacheAccum::::new(); + let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); if let Some(mut prev_val) = iter_input.next() { diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index c21472e4..db691048 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,8 @@ pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter}; pub use node_codec::{NodeCodec, Partial}; -pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; +pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, + TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; pub type DBValue = elastic_array::ElasticArray128; @@ -369,6 +370,7 @@ pub trait TrieLayOut { type H: Hasher; type C: NodeCodec; type N: NibbleOps; + type CB: CacheBuilder<::Out>; } /// alias to acces hasher hash output type from a `TrieLayout` diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index dadd7083..2af9d6b2 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -153,7 +153,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } } - } /// half byte nibble prepend encoding From 398c91538b8974edb8f5306b9d82b295be6a5b3c Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 May 2019 18:46:09 +0200 Subject: [PATCH 062/120] plug iter_build on TrieLayout --- test-support/reference-trie/src/lib.rs | 19 ++--- trie-db/src/iter_build.rs | 112 +++++++++---------------- trie-db/src/lib.rs | 2 +- 3 files changed, 50 insertions(+), 83 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 038f2e49..2971cf29 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -30,7 +30,6 @@ use trie_db::{ triedbmut::ChildReference, DBValue, trie_visit, - trie_visit_no_ext, TrieBuilder, TrieRoot, Partial, @@ -827,7 +826,7 @@ pub fn compare_impl + Eq> ( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -869,7 +868,7 @@ pub fn compare_root( ) { let root_new = { let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -889,7 +888,7 @@ pub fn compare_unhashed( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed(data); @@ -902,7 +901,7 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -921,7 +920,7 @@ pub fn calc_root( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -934,7 +933,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit_no_ext::(data.into_iter(), &mut cb); + trie_db::trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -945,7 +944,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -991,7 +990,7 @@ pub fn compare_impl_no_ext_q( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -1049,7 +1048,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit_no_ext::(b_map.into_iter(), &mut cb); + trie_visit::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index b35d80c8..bc802bdb 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -20,6 +20,7 @@ use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; use node_codec::NodeCodec; +use crate::{TrieLayOut, TrieHash}; macro_rules! exp_disp { (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; @@ -59,11 +60,11 @@ impl CacheBuilder for Cache4 { exp_disp!(@2, [None]) } } - +type ArrayNode = <::CB as CacheBuilder>>::AN; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index // second str is in branch value -struct CacheAccum,V> (Vec<(CB::AN, bool, Option)>,PhantomData<(H,C,N,CB)>); +struct CacheAccum (Vec<(ArrayNode, bool, Option)>,PhantomData); /// initially allocated cache const INITIAL_DEPTH: usize = 10; @@ -71,18 +72,15 @@ const INITIAL_DEPTH: usize = 10; /// TODO EMCH test `new_vec_slice_buff` as function of trait and associated type const NIBBLE_SIZE_BUFF: usize = 16; -impl CacheAccum +impl CacheAccum where - H: Hasher, - N: NibbleOps, - CB: CacheBuilder<::Out>, - C: NodeCodec, + T: TrieLayOut, V: AsRef<[u8]>, { fn new() -> Self { let mut v = Vec::with_capacity(INITIAL_DEPTH); - (0..INITIAL_DEPTH).for_each(|_|v.push((CB::new_vec_slice_buff(), false, None))); + (0..INITIAL_DEPTH).for_each(|_|v.push((T::CB::new_vec_slice_buff(), false, None))); CacheAccum(v, PhantomData) } @@ -90,7 +88,7 @@ where fn set_elt(&mut self, depth:usize, sl: Option) { if depth >= self.0.len() { for _i in self.0.len()..depth + 1 { - self.0.push((CB::new_vec_slice_buff(), false, None)); + self.0.push((T::CB::new_vec_slice_buff(), false, None)); } } self.0[depth].2 = sl; @@ -98,10 +96,10 @@ where } #[inline(always)] - fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode) { + fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode>) { if depth >= self.0.len() { for _i in self.0.len()..depth + 1 { - self.0.push((CB::new_vec_slice_buff(), false, None)); + self.0.push((T::CB::new_vec_slice_buff(), false, None)); } } self.0[depth].0.as_mut()[nibble_ix] = node; @@ -116,22 +114,22 @@ where #[inline(always)] fn reset_depth(&mut self, depth:usize) { self.0[depth].1 = false; - for i in 0..N::NIBBLE_LEN { + for i in 0..T::N::NIBBLE_LEN { self.0[depth].0.as_mut()[i] = None; } } fn flush_val ( &mut self, - cb_ext: &mut impl ProcessEncodedNode<::Out>, + cb_ext: &mut impl ProcessEncodedNode>, target_depth: usize, (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), ) { - let nibble_value = N::left_nibble_at(&k2.as_ref()[..], target_depth); + let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); - let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); + let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len()); let hash = cb_ext.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) @@ -141,7 +139,7 @@ where fn flush_branch( &mut self, no_ext: bool, - cb_ext: &mut impl ProcessEncodedNode<::Out>, + cb_ext: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, new_depth: usize, old_depth: usize, @@ -182,7 +180,7 @@ where self.standard_ext(&ref_branch.as_ref()[..], cb_ext, branch_d, is_root, nkey) }; // put hash in parent - let nibble: u8 = N::left_nibble_at(&ref_branch.as_ref()[..],d); + let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..],d); self.set_node(d, nibble as usize, Some(h)); } } @@ -208,23 +206,23 @@ where fn standard_ext( &mut self, key_branch: &[u8], - cb_ext: &mut impl ProcessEncodedNode<::Out>, + cb_ext: &mut impl ProcessEncodedNode>, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, - ) -> ChildReference<::Out> { + ) -> ChildReference> { // enc branch let v = self.0[branch_d].2.take(); - let encoded = C::branch_node(self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); + let encoded = T::C::branch_node(self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); - let encoded = C::ext_node(nib, nkeyix.1, branch_hash); + let encoded = T::C::ext_node(nib, nkeyix.1, branch_hash); let h = cb_ext.process(pr.left(), encoded, is_root); h } else { @@ -236,69 +234,39 @@ where fn alt_no_ext( &mut self, key_branch: &[u8], - cb_ext: &mut impl ProcessEncodedNode<::Out>, + cb_ext: &mut impl ProcessEncodedNode>, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, - ) -> ChildReference<::Out> { + ) -> ChildReference> { // enc branch let v = self.0[branch_d].2.take(); let nkeyix = nkey.unwrap_or((0,0)); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); - let encoded = C::branch_node_nibbled( + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); + let encoded = T::C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); pr.right_range_iter(nkeyix.1), nkeyix.1, self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); cb_ext.process(pr.left(), encoded, is_root) } } -pub fn trie_visit_no_ext(input: I, cb_ext: &mut F) - where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, - H: Hasher, - N: NibbleOps, - CB: CacheBuilder<::Out>, - C: NodeCodec, - F: ProcessEncodedNode<::Out>, - { - trie_visit_inner::(input, cb_ext, true) - } - -pub fn trie_visit(input: I, cb_ext: &mut F) - where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, - H: Hasher, - N: NibbleOps, - CB: CacheBuilder<::Out>, - C: NodeCodec, - F: ProcessEncodedNode<::Out>, - { - trie_visit_inner::(input, cb_ext, false) - } - -// put no_ext as a trait: probably not worth it (fn designed for that)? -fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: bool) +/// visit trie +pub fn trie_visit(input: I, cb_ext: &mut F) where + T: TrieLayOut, I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, - H: Hasher, - N: NibbleOps, - CB: CacheBuilder<::Out>, - C: NodeCodec, - F: ProcessEncodedNode<::Out>, + F: ProcessEncodedNode>, { - let mut depth_queue = CacheAccum::::new(); + let no_ext = !T::USE_EXTENSION; + let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); if let Some(mut prev_val) = iter_input.next() { @@ -306,10 +274,10 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: b let mut last_depth = 0; for (k, v) in iter_input { - let common_depth = N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = T::N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == prev_val.0.as_ref().len() * N::NIBBLE_PER_BYTE { + if common_depth == prev_val.0.as_ref().len() * T::N::NIBBLE_PER_BYTE { // the new key include the previous one : branch value case // just stored value at branch depth depth_queue.set_elt(common_depth, Some(prev_val.1)); @@ -331,9 +299,9 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: b && !depth_queue.touched(0) { // one single element corner case let (k2, v2) = prev_val; - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],last_depth); - let encoded = C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * N::NIBBLE_PER_BYTE - nkey.len()); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],last_depth); + let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len()); cb_ext.process(pr.left(), encoded, true); } else { depth_queue.flush_val(cb_ext, last_depth, &prev_val); @@ -342,7 +310,7 @@ fn trie_visit_inner(input: I, cb_ext: &mut F, no_ext: b } } else { // nothing null root corner case - cb_ext.process(crate::nibble::EMPTY_NIBBLE, C::empty_node().to_vec(), true); + cb_ext.process(crate::nibble::EMPTY_NIBBLE, T::C::empty_node().to_vec(), true); } } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index db691048..4dde2b24 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,7 @@ pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter}; pub use node_codec::{NodeCodec, Partial}; -pub use iter_build::{trie_visit, trie_visit_no_ext, ProcessEncodedNode, +pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; pub type DBValue = elastic_array::ElasticArray128; From 38c48996c5b35a3c15d1c78d594d4b6c1ec656dd Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 7 May 2019 12:27:23 +0200 Subject: [PATCH 063/120] Make children slice variable length depending on trait. --- test-support/reference-trie/src/lib.rs | 162 ++++++++++++++++++------- trie-db/src/iter_build.rs | 6 +- trie-db/src/lib.rs | 10 +- trie-db/src/lookup.rs | 6 +- trie-db/src/nibble/mod.rs | 77 +++++++++++- trie-db/src/node.rs | 74 ++++++----- trie-db/src/node_codec.rs | 24 ++++ trie-db/src/triedb.rs | 12 +- trie-db/src/triedbmut.rs | 59 ++++----- 9 files changed, 305 insertions(+), 125 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 2971cf29..06c099e9 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -23,6 +23,7 @@ extern crate keccak_hasher; use std::fmt; use std::error::Error as StdError; use std::iter::once; +use std::marker::PhantomData; use codec::{Decode, Input, Output, Encode, Compact}; use trie_root::Hasher; use trie_db::{ @@ -33,13 +34,14 @@ use trie_db::{ TrieBuilder, TrieRoot, Partial, - Cache16, - Cache4, + Cache16, + Cache4, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, ChildBitmap, + BitMap, ChildSliceIx}; pub use trie_db::{Record, TrieLayOut, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; @@ -49,7 +51,7 @@ pub struct LayoutOri; impl TrieLayOut for LayoutOri { const USE_EXTENSION: bool = true; type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodec; + type C = ReferenceNodeCodec; type N = NibbleHalf; type CB = Cache16; } @@ -60,7 +62,7 @@ pub struct LayoutNew; impl TrieLayOut for LayoutNew { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; + type C = ReferenceNodeCodecNoExt; type N = NibbleHalf; type CB = Cache16; } @@ -71,12 +73,71 @@ pub struct LayoutNewQuarter; impl TrieLayOut for LayoutNewQuarter { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; + type C = ReferenceNodeCodecNoExt; type N = NibbleQuarter; type CB = Cache4; } +/// bitmap codec for radix 16 +pub struct BitMap16(u16); +impl ChildBitmap for BitMap16 { + const ENCODED_LEN: usize = 2; + type Error = ReferenceError; + type Buff = [u8;3]; // need a byte for header + + fn decode(data: &[u8]) -> Result { + u16::decode(&mut &data[..]) + .ok_or(ReferenceError::BadFormat) + .map(|v|BitMap16(v)) + } + + fn value_at(&self, i: usize) -> bool { + self.0 & (1u16 << i) != 0 + } + + fn encode>(has_children: I , dest: &mut [u8]) { + let mut bitmap: u16 = 0; + let mut cursor: u16 = 1; + for v in has_children { + if v { bitmap |= cursor } + cursor <<= 1; + } + dest[0] = (bitmap % 256) as u8; + dest[1] = (bitmap / 256) as u8; + } +} +/// bitmap codec for radix 4 +pub struct BitMap4(u8); + +impl ChildBitmap for BitMap4 { + const ENCODED_LEN: usize = 1; + type Error = ReferenceError; + type Buff = [u8;2]; // need a byte for header + + fn decode(data: &[u8]) -> Result { + if data.len() == 0 || data[0] & 0xf0 != 0 { + Err(ReferenceError::BadFormat) + } else { + Ok(BitMap4(data[0])) + } + } + + fn value_at(&self, i: usize) -> bool { + self.0 & (1u8 << i) != 0 + } + + fn encode>(has_children: I , dest: &mut [u8]) { + let mut bitmap: u8 = 0; + let mut cursor: u8 = 1; + for v in has_children { + if v { bitmap |= cursor } + cursor <<= 1; + } + dest[0] = bitmap; + } + +} pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, LayoutNew>; @@ -198,17 +259,23 @@ fn fuse_nibbles_node_noext<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl I //.chain(if nibbles.len() % 2 == 1 { Some(nibbles[nibbles.len() - 1] << 4) } else { None }) } -pub fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { +fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { + let mut res = [0, 0, 0]; + branch_node_buf::(has_value, has_children, &mut res[..]); + res +} + +fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE }; - let bm = branch_node_bit_mask(has_children); - [first, bm.0, bm.1] + dest[0] = first; + BM::encode(has_children, &mut dest[1..]); } -pub fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { +fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { let mut bitmap: u16 = 0; let mut cursor: u16 = 1; for v in has_children { @@ -489,12 +556,12 @@ impl Decode for NodeHeaderNoExt { /// Simple reference implementation of a `NodeCodec`. /// This is similar to ethereum implementation. #[derive(Default, Clone)] -pub struct ReferenceNodeCodec; +pub struct ReferenceNodeCodec(PhantomData); /// Simple reference implementation of a `NodeCodec`. /// This is following https://github.com/w3f/polkadot-re-spec/issues/8 #[derive(Default, Clone)] -pub struct ReferenceNodeCodecNoExt; +pub struct ReferenceNodeCodecNoExt(PhantomData); #[derive(Debug, PartialEq, Eq, Clone)] /// Error concerning the Parity-Codec based decoder. @@ -583,8 +650,9 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< // `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` // but due to the current limitations of Rust const evaluation we can't // do `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. Perhaps one day soon? -impl NodeCodec for ReferenceNodeCodec { +impl> NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; + type BM = BM; fn hashed_null_node() -> ::Out { KeccakHasher::hash(>::empty_node()) @@ -595,25 +663,28 @@ impl NodeCodec for ReferenceNodeCodec { match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - // TODO EMCH var len bitmap up to 256 from 2 see NibbleOps variant - let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; + let bm_slice = take(input, BM::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap = BM::decode(&bm_slice[..])?; + let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) } else { None }; - // TODO EMCH could not parameterized this on associated constant - let mut children = [None; 16]; - let mut pot_cursor = 1; + let mut children: N::ChildSliceIx = Default::default(); + let child_val = &**input; + let mut ix = 0; + children.as_mut()[0] = ix; for i in 0..N::NIBBLE_LEN { - if bitmap & pot_cursor != 0 { + if bitmap.value_at(i) { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; - children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); + let _ = take(input, count); + ix += count + N::ChildSliceIx::CONTENT_HEADER_SIZE; } - pot_cursor <<= 1; + children.as_mut()[i + 1] = ix; } - Ok(Node::Branch(children, value)) + Ok(Node::Branch((children, child_val), value)) } NodeHeader::Extension(nibble_count) => { let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) @@ -670,14 +741,15 @@ impl NodeCodec for ReferenceNodeCodec { fn branch_node( children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { - let mut output = vec![0, 0, 0]; + let mut output = vec![0; BM::ENCODED_LEN + 1]; + let mut prefix: BM::Buff = Default::default(); let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); true } else { false }; - let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child.borrow() { + branch_node_buf::(have_value, children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -687,8 +759,8 @@ impl NodeCodec for ReferenceNodeCodec { true } None => false, - })); - output[0..3].copy_from_slice(&prefix[..]); + }), prefix.as_mut()); + output[0..BM::ENCODED_LEN + 1].copy_from_slice(prefix.as_ref()); output } @@ -702,8 +774,9 @@ impl NodeCodec for ReferenceNodeCodec { } -impl NodeCodec for ReferenceNodeCodecNoExt { +impl> NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; + type BM = BM; fn hashed_null_node() -> ::Out { KeccakHasher::hash(>::empty_node()) @@ -723,23 +796,27 @@ impl NodeCodec for ReferenceNodeCodecNoExt { .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, N::nb_padding(nibble_count)); - let bitmap = u16::decode(input).ok_or(ReferenceError::BadFormat)?; + let bm_slice = take(input, BM::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap = BM::decode(&bm_slice[..])?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) } else { None }; - let mut children = [None; 16]; - let mut pot_cursor = 1; + let mut children: N::ChildSliceIx = Default::default(); + let child_val = &**input; + let mut ix = 0; + children.as_mut()[0] = ix; for i in 0..N::NIBBLE_LEN { - if bitmap & pot_cursor != 0 { + if bitmap.value_at(i) { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; - children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); + let _ = take(input, count); + ix += count + N::ChildSliceIx::CONTENT_HEADER_SIZE; } - pot_cursor <<= 1; + children.as_mut()[i + 1] = ix; } - Ok(Node::NibbledBranch(nibble_slice, children, value)) + Ok(Node::NibbledBranch(nibble_slice, (children, child_val), value)) } NodeHeaderNoExt::Leaf(nibble_count) => { let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; @@ -757,7 +834,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { } fn try_decode_hash(data: &[u8]) -> Option<::Out> { - >::try_decode_hash(data) + as NodeCodec<_, N>>::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { @@ -795,12 +872,12 @@ impl NodeCodec for ReferenceNodeCodecNoExt { partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchNoValue) }; let bm_ix = output.len(); - output.push(0); - output.push(0); + let mut bm: BM::Buff = Default::default(); + (0..BM::ENCODED_LEN).for_each(|_|output.push(0)); if let Some(value) = maybe_value { value.encode_to(&mut output); }; - let bm = branch_node_bit_mask(children.map(|maybe_child| match maybe_child.borrow() { + BM::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -810,9 +887,8 @@ impl NodeCodec for ReferenceNodeCodecNoExt { true } None => false, - })); - output[bm_ix] = bm.0; - output[bm_ix + 1] = bm.1; + }), bm.as_mut()); + output[bm_ix..bm_ix + BM::ENCODED_LEN].copy_from_slice(&bm.as_ref()[..BM::ENCODED_LEN]); output } @@ -1125,8 +1201,8 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = >::leaf_node(((0,0),&input), &[1]); - let dec = >::decode(&enc).unwrap(); + let enc = as NodeCodec<_, NibbleHalf>>::leaf_node(((0,0),&input), &[1]); + let dec = as NodeCodec<_, NibbleHalf>>::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) } else { None }; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index bc802bdb..71c104fa 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -68,9 +68,6 @@ struct CacheAccum (Vec<(ArrayNode, bool, Option)>,Phantom /// initially allocated cache const INITIAL_DEPTH: usize = 10; -/// This should not exist and be associated const of trie layout -/// TODO EMCH test `new_vec_slice_buff` as function of trait and associated type -const NIBBLE_SIZE_BUFF: usize = 16; impl CacheAccum where @@ -80,7 +77,8 @@ where fn new() -> Self { let mut v = Vec::with_capacity(INITIAL_DEPTH); - (0..INITIAL_DEPTH).for_each(|_|v.push((T::CB::new_vec_slice_buff(), false, None))); + (0..INITIAL_DEPTH).for_each(|_| + v.push((T::CB::new_vec_slice_buff(), false, None))); CacheAccum(v, PhantomData) } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 4dde2b24..eedba6b9 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -95,10 +95,11 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter}; -pub use node_codec::{NodeCodec, Partial}; +pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, + ChildSliceIx}; +pub use node_codec::{NodeCodec, Partial, ChildBitmap}; pub use iter_build::{trie_visit, ProcessEncodedNode, - TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; + TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; pub type DBValue = elastic_array::ElasticArray128; @@ -375,6 +376,7 @@ pub trait TrieLayOut { /// alias to acces hasher hash output type from a `TrieLayout` pub type TrieHash = <::H as Hasher>::Out; +/// alias to acces bitmap codec from a `TrieLayout` +pub type BitMap = <::C as NodeCodec<::H, ::N>>::BM; /// alias to acces `NodeCodec` `Error` type from a `TrieLayout` pub type CError = <::C as NodeCodec<::H, ::N>>::Error; - diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 372a5118..3a0c2068 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -18,7 +18,7 @@ use hash_db::HashDBRef; use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query, TrieLayOut, CError, TrieHash}; +use super::{DBValue, Result, TrieError, Query, TrieLayOut, CError, TrieHash, ChildSliceIx}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -85,7 +85,7 @@ where } Node::Branch(children, value) => match partial.is_empty() { true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children[partial.at(0) as usize] { + false => match children.0.slice_at(partial.at(0) as usize, children.1) { Some(x) => { node_data = x; partial = partial.mid(1); @@ -101,7 +101,7 @@ where match partial.len() == slice.len() { true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children[partial.at(slice.len()) as usize] { + false => match children.0.slice_at(partial.at(slice.len()) as usize, children.1) { Some(x) => { node_data = x; partial = partial.mid(slice.len() + 1); diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 2af9d6b2..a492f79c 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -44,6 +44,11 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// las ix for nible as a u8 (for pattern matching) const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; + /// Buff for slice index store (we do not include + /// directly slice in it to avoid lifetime in + /// trait + type ChildSliceIx: ChildSliceIx; + /// mask a byte from a ix > 0 (ix being content) #[inline(always)] fn masked_left(ix: u8, b: u8) -> u8 { @@ -152,7 +157,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy false } } - } /// half byte nibble prepend encoding @@ -176,6 +180,7 @@ pub enum ByteLayout { impl NibbleOps for NibbleHalf { const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; + type ChildSliceIx = ChildSliceIx16; } #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] @@ -190,6 +195,7 @@ impl NibbleOps for NibbleQuarter { (0b0000_1111, 2), (0b0000_0011, 0), ]; + type ChildSliceIx = ChildSliceIx4; } @@ -236,4 +242,73 @@ pub struct NibbleSliceIterator<'a, N: NibbleOps> { i: usize, } +/// trait only here to avoid lifetime when storing slice +/// This would be useless with https://github.com/rust-lang/rust/issues/43408 +pub trait ChildSliceIx: AsRef<[usize]> + + AsMut<[usize]> + Default + Eq + PartialEq + crate::MaybeDebug + + Clone { + + /// nb nibble in slice + const NIBBLE_LEN : usize; + /// bit to skip between slice + const CONTENT_HEADER_SIZE: usize; + + /// get an optional slice + fn slice_at<'a>(&self, ix: usize, data: &'a [u8]) -> Option<&'a[u8]> { + let b = (self.as_ref().get(ix), self.as_ref().get(ix + 1)); + if let (Some(s), Some(e)) = b { + let s = s + Self::CONTENT_HEADER_SIZE; + if s < *e { + Some(&data[s..*e]) + } else { + None + } + } else { + None + } + } + fn iter<'a>(&'a self, data: &'a [u8]) -> IterChildSliceIx<'a, Self> { + IterChildSliceIx(self, 0, data) + } +} + +/// iterator over `ChildSliceIx` trait +pub struct IterChildSliceIx<'a, CS>(&'a CS, usize, &'a[u8]); + +impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { + type Item = Option<&'a[u8]>; + fn next(&mut self) -> Option { + if self.1 == CS::NIBBLE_LEN { + return None; + } + self.1 += 1; + Some(self.0.slice_at(self.1 - 1, self.2)) + } +} + +macro_rules! child_slice_ix { + ($me: ident, $size: expr, $pre: expr) => { + #[derive(Default, Eq, PartialEq, Debug, Clone)] + /// child slice ix for radix $size + pub struct $me([usize; $size + 1]); + + impl AsRef<[usize]> for $me { + fn as_ref(&self) -> &[usize] { + &self.0[..] + } + } + impl AsMut<[usize]> for $me { + fn as_mut(&mut self) -> &mut [usize] { + &mut self.0[..] + } + } + + impl ChildSliceIx for $me { + const CONTENT_HEADER_SIZE: usize = $pre; + const NIBBLE_LEN: usize = $size; + } + } +} +child_slice_ix!(ChildSliceIx16, 16, 1); +child_slice_ix!(ChildSliceIx4, 4, 1); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index eee0c983..cc99f097 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -14,7 +14,7 @@ use elastic_array::ElasticArray36; use nibble::NibbleSlice; -use nibble::NibbleOps; +use nibble::{NibbleOps, ChildSliceIx}; use nibble::NibbleVec; use super::DBValue; @@ -25,6 +25,9 @@ use alloc::vec::Vec; /// Offset is applied on first byte of array. pub type NodeKey = (usize, ElasticArray36); +/// alias to branch children slice +pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); + /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Debug, Clone)] pub enum Node<'a, N: NibbleOps> { @@ -39,44 +42,40 @@ pub enum Node<'a, N: NibbleOps> { // for lifetime, so we should ultimately use something similar to struct `Branch` it decodes from // a slice that is already aligned (need 2* bound in case there is some headers). /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. - Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), + Branch(BranchChildrenSlice<'a, N>, Option<&'a [u8]>), /// Branch node with support for a nibble (to avoid extension node) - NibbledBranch(NibbleSlice<'a, N>, [Option<&'a [u8]>; 16], Option<&'a [u8]>), + NibbledBranch(NibbleSlice<'a, N>, BranchChildrenSlice<'a, N>, Option<&'a [u8]>), } /// A Sparse (non mutable) owned vector struct to hold branch keys and value #[derive(Eq, PartialEq, Debug, Clone)] pub struct Branch { data: Vec, - ubounds: [usize; 18], - has_value: bool, + data_ix: usize, + ubounds_ix: usize, + child_head: usize, } impl Branch { - fn new(children: [Option<&[u8]>; 16], maybe_value: Option<&[u8]>) -> Self { - let mut data = Vec::with_capacity(children.iter() - .filter_map(|n| n.clone()) - .map(|child| child.len()) - .sum() - ); - let mut ubounds = [0; 18]; - for (maybe_child, ub) in children.iter().zip(ubounds.iter_mut().skip(1)) { - if let Some(child) = maybe_child { - data.extend_from_slice(child); - } - *ub = data.len(); + fn new(children_slice: &BranchChildrenSlice, maybe_value: Option<&[u8]>) -> Self { + let mut data: Vec = children_slice.1.into(); + let data_ix = data.len(); + let ubounds_ix = data_ix + maybe_value.map(|v|{ + data.extend_from_slice(v); + v.len() + }).unwrap_or(0); + let mut i = 0; + while let Some(ix) = children_slice.0.as_ref().get(i) { + i += 1; + data.extend_from_slice(&ix.to_ne_bytes()[..]); } - if let Some(value) = maybe_value { - data.extend_from_slice(value); - ubounds[17] = data.len(); - } - Branch { data, ubounds, has_value: maybe_value.is_some() } + Branch { data, data_ix, ubounds_ix, child_head: N::ChildSliceIx::CONTENT_HEADER_SIZE } } /// Get the node value, if any pub fn get_value(&self) -> Option<&[u8]> { - if self.has_value { - Some(&self.data[self.ubounds[16]..self.ubounds[17]]) + if self.has_value() { + Some(&self.data[self.data_ix..self.ubounds_ix]) } else { None } @@ -84,16 +83,28 @@ impl Branch { /// Test if the node has a value pub fn has_value(&self) -> bool { - self.has_value + self.data_ix < self.ubounds_ix } - pub fn index(&self, index: usize) -> Option<&[u8]> { - assert!(index < 16); - if self.ubounds[index] == self.ubounds[index + 1] { + fn index_bound(&self, index: usize) -> Option { + use core_::convert::TryInto; + let s = self.ubounds_ix + index * 8; + let e = s + 8; + if self.data.len() < e { None } else { - Some(&self.data[self.ubounds[index]..self.ubounds[index + 1]]) + self.data[s..e].try_into().ok().map(usize::from_ne_bytes) + } + } + pub fn index(&self, index: usize) -> Option<&[u8]> { + let b = (self.index_bound(index), self.index_bound(index + 1)); + if let (Some(s), Some(e)) = b { + let s = s + self.child_head; + if s < e { + return Some(&self.data[s..e]) + } } + None } } @@ -109,6 +120,7 @@ pub enum OwnedNode { /// Branch node: 16 children and an optional value. Branch(Branch), /// Branch node: 16 children and an optional value. + /// TODO can put nibble vec into branch data vec NibbledBranch(NibbleVec, Branch), } @@ -118,8 +130,8 @@ impl<'a, N: NibbleOps> From> for OwnedNode { Node::Empty => OwnedNode::Empty, Node::Leaf(k, v) => OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), Node::Extension(k, child) => OwnedNode::Extension(k.into(), DBValue::from_slice(child)), - Node::Branch(c, val) => OwnedNode::Branch(Branch::new(c, val)), - Node::NibbledBranch(k, c, val) => OwnedNode::NibbledBranch(k.into(), Branch::new(c, val)), + Node::Branch(c, val) => OwnedNode::Branch(Branch::new::(&c, val)), + Node::NibbledBranch(k, c, val) => OwnedNode::NibbledBranch(k.into(), Branch::new::(&c, val)), } } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 1d832ae9..fe44c3c7 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -48,6 +48,8 @@ pub type Partial<'a> = ((u8,u8), &'a[u8]); pub trait NodeCodec: Sized { /// Codec error type type Error: Error; + /// child bitmap codec to use + type BM: ChildBitmap; /// Get the hashed null node. fn hashed_null_node() -> H::Out; @@ -76,3 +78,25 @@ pub trait NodeCodec: Sized { /// Returns an encoded branch node with a possible partial path. fn branch_node_nibbled(partial: impl Iterator, nb_nibble: usize, children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; } + +/// extract child bitmap encoding that is related to the trie number of children +/// This would be useless with https://github.com/rust-lang/rust/issues/43408 +pub trait ChildBitmap: Sized { + /// length to encode the bitmap + const ENCODED_LEN: usize; + /// Codec error type + type Error: Error; + + /// Codec buf to use + type Buff: AsRef<[u8]> + AsMut<[u8]> + Default; + + /// decode bitmap + fn decode(data: &[u8]) -> Result; + + /// got values + fn value_at(&self, i: usize) -> bool; + + /// encode to dest of right len + fn encode>(has_children: I , dest: &mut [u8]); + +} diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 3551a5ce..0841febc 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{HashDBRef, Prefix}; -use nibble::{NibbleSlice, NibbleOps}; +use nibble::{NibbleSlice, NibbleOps, ChildSliceIx}; use nibble::EMPTY_NIBBLE; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; @@ -179,7 +179,7 @@ where }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { - let nodes: Vec> = nodes.into_iter() + let nodes: Vec> = nodes.0.iter(nodes.1) .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { @@ -198,10 +198,10 @@ where .finish() }, Ok(Node::NibbledBranch(slice, nodes, value)) => { - let nodes: Vec> = nodes.into_iter() + let nodes: Vec> = nodes.0.iter(nodes.1) .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) - .map(|(i, n)| TrieAwareDebugNode { + .map(|(i, n)| TrieAwareDebugNode { trie: self.trie, index: Some(i as u8), node_key: n, @@ -354,7 +354,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { node: node.clone().into(), }); self.key_nibbles.push(i); - if let Some(ref child) = nodes[i as usize] { + if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { full_key_nibbles += 1; partial = partial.mid(1); let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; @@ -384,7 +384,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { }); self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push(i); - if let Some(ref child) = nodes[i as usize] { + if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { full_key_nibbles += slice.len() + 1; partial = partial.mid(slice.len() + 1); let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index d5875ff8..d239445f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -22,7 +22,7 @@ use super::{DBValue, node::NodeKey}; use nibble::EMPTY_NIBBLE; use hash_db::{HashDB, Hasher, Prefix}; -use nibble::{NibbleVec, NibbleSlice, NibbleOps}; +use nibble::{NibbleVec, NibbleSlice, NibbleOps, ChildSliceIx, IterChildSliceIx}; use elastic_array::ElasticArray36; use ::core_::mem; use ::core_::ops::Index; @@ -63,11 +63,10 @@ impl From for NodeHandle { } } -fn empty_children() -> Box<[Option>; 16]> { - Box::new([ - None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, - ]) +fn empty_children() -> Vec>> { + let mut res = Vec::with_capacity(N::NIBBLE_LEN); + (0..N::NIBBLE_LEN).for_each(|_|res.push(None)); + res } /// type alias to indicate the nible cover a full key, @@ -89,9 +88,9 @@ enum Node { /// The child node is always a branch. Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option), + Branch(Vec>>, Option), /// Branch node with support for a nibble (to avoid extension node) - NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), + NibbledBranch(NodeKey, Vec>>, Option), } impl Node @@ -121,19 +120,13 @@ where fn from_encoded<'a, 'b, C, H, N>(data: &'a[u8], db: &HashDB, storage: &'b mut NodeStorage) -> Self where N: NibbleOps, C: NodeCodec, H: Hasher, { - let dec_children = |encoded_children: &[Option<&'a [u8]>; 16], storage: &'b mut NodeStorage| { - let mut child = |i:usize| { - encoded_children[i].map(|data| - Self::inline_or_hash::(data, db, storage) - ) - }; - - Box::new([ - child(0), child(1), child(2), child(3), - child(4), child(5), child(6), child(7), - child(8), child(9), child(10), child(11), - child(12), child(13), child(14), child(15), - ]) + let dec_children = |encoded_children: IterChildSliceIx, storage: &'b mut NodeStorage| { + let mut res = Vec::with_capacity(N::ChildSliceIx::NIBBLE_LEN); + encoded_children.for_each(|o_data|{ + let v = o_data.map(|data|Self::inline_or_hash::(data, db, storage)); + res.push(v) + }); + res }; match C::decode(data).unwrap_or(EncodedNode::Empty) { @@ -145,11 +138,11 @@ where Self::inline_or_hash::(cb, db, storage)) }, EncodedNode::Branch(encoded_children, val) => { - let children = dec_children(&encoded_children, storage); + let children = dec_children(encoded_children.0.iter(encoded_children.1), storage); Node::Branch(children, val.map(DBValue::from_slice)) }, EncodedNode::NibbledBranch(k, encoded_children, val) => { - let children = dec_children(&encoded_children, storage); + let children = dec_children(encoded_children.0.iter(encoded_children.1), storage); Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) }, } @@ -576,7 +569,7 @@ where trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); let low = Node::NibbledBranch(existing_key.mid(cp + 1).to_stored(), children, stored_value); let ix = existing_key.at(cp); - let mut children = empty_children(); + let mut children = empty_children::<_, L::N>(); let alloc_storage = self.storage.alloc(Stored::New(low)); @@ -647,7 +640,7 @@ where trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // one of us isn't empty: transmute to branch here - let mut children = empty_children(); + let mut children = empty_children::<_, L::N>(); let branch = if L::USE_EXTENSION && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) @@ -671,7 +664,7 @@ where // fully-shared prefix for an extension. // make a stub branch - let branch = Node::NibbledBranch(existing_key.to_stored(), empty_children(), Some(stored_value)); + let branch = Node::NibbledBranch(existing_key.to_stored(), empty_children::<_, L::N>(), Some(stored_value)); // augment the new branch. let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -683,7 +676,7 @@ where // fully-shared prefix for an extension. // make a stub branch and an extension. - let branch = Node::Branch(empty_children(), Some(stored_value)); + let branch = Node::Branch(empty_children::<_, L::N>(), Some(stored_value)); // augment the new branch. key.advance(cp); let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -722,7 +715,7 @@ where assert!(!existing_key.is_empty()); let idx = existing_key.at(0) as usize; - let mut children = empty_children(); + let mut children = empty_children::<_, L::N>(); children[idx] = if existing_key.len() == 1 { // direct extension, just replace. Some(child_branch) @@ -1320,7 +1313,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, LayoutNew, LayoutNewQuarter}; + LayoutOri, LayoutNew, LayoutNewQuarter, BitMap16, BitMap}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1398,7 +1391,7 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie(&mut memtrie, &x); memtrie.commit(); - let hashed_null_node = ::N>>::hashed_null_node(); + let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); @@ -1441,7 +1434,7 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie_no_ext(&mut memtrie, &x); memtrie.commit(); - let hashed_null_node = ::N>>::hashed_null_node(); + let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); @@ -1460,7 +1453,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - let hashed_null_node = ::N>>::hashed_null_node(); + let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); assert_eq!(*t.root(), hashed_null_node); } @@ -1736,7 +1729,7 @@ mod tests { } assert!(t.is_empty()); - let hashed_null_node = ::N>>::hashed_null_node(); + let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); assert_eq!(*t.root(), hashed_null_node); } From 45009f85ba34458f18a22946e58ccdfb8fa3c3f6 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 8 May 2019 15:26:40 +0200 Subject: [PATCH 064/120] Clone and debug for kf function --- memory-db/src/lib.rs | 2 ++ trie-db/src/node_codec.rs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index c9b55f79..3c2396d7 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -168,6 +168,7 @@ pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { key.clone() } +#[derive(Clone,Debug)] /// Key function that only uses the hash pub struct HashKey(PhantomData); @@ -179,6 +180,7 @@ impl KeyFunction for HashKey { } } +#[derive(Clone,Debug)] /// Key function that concatenates prefix and hash. pub struct PrefixedKey(PhantomData); diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index fe44c3c7..af34be5b 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -38,7 +38,9 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} +/// a nible slice, a partial length (0 to max nb nibble - 1) first byte and a slice of full bytes pub type Partial<'a> = ((u8,u8), &'a[u8]); + // TODO EMCH change node codec trait to use &mut self as input in order to run on internal buffer. // (not for decode actually!!; code seems fine to do that and new layout trait is ok too /// Trait for trie node encoding/decoding @@ -48,7 +50,8 @@ pub type Partial<'a> = ((u8,u8), &'a[u8]); pub trait NodeCodec: Sized { /// Codec error type type Error: Error; - /// child bitmap codec to use + /// child bitmap codec to use TODO EMCH nodecodec does not have to use a bitmap codec: this should be + /// only in codec implementation, having it as a subtype seems useless type BM: ChildBitmap; /// Get the hashed null node. From 1c40a98c223f5a0136548c17c7db62bda338d856 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 8 May 2019 20:27:13 +0200 Subject: [PATCH 065/120] nibble vec append fix --- trie-db/src/nibble/nibblevec.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index ccab2679..8c24a998 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -115,12 +115,18 @@ impl NibbleVec { pub fn append(&mut self, v: &NibbleVec) { if v.len == 0 { return; } + let final_len = self.len + v.len; let offset = self.len % N::NIBBLE_PER_BYTE; + let final_offset = final_len % N::NIBBLE_PER_BYTE; let last_ix = self.len / N::NIBBLE_PER_BYTE; if offset > 0 { let (s1, s2) = N::split_shifts(offset); self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); - (0..v.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); + if final_offset > 0 { + (0..v.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); + } else { + (1..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); + } } else { (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); } @@ -128,7 +134,6 @@ impl NibbleVec { } - /// push a full partial. pub fn append_partial(&mut self, (o_n, sl): Partial) { for i in (1..=o_n.0).rev() { From c4ace9ae91321dca00bbcf549d1094bcc85fb038 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 9 May 2019 10:21:05 +0200 Subject: [PATCH 066/120] right vec append fix --- trie-db/src/iter_build.rs | 70 ++++++++++++++++++++++----------- trie-db/src/nibble/nibblevec.rs | 17 ++++---- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 71c104fa..801b05e1 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -23,42 +23,42 @@ use node_codec::NodeCodec; use crate::{TrieLayOut, TrieHash}; macro_rules! exp_disp { - (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; - (@2, [$($inpp:expr),*]) => { exp_disp!(@1, [$($inpp,)* $($inpp),*]) }; - (@1, [$($inpp:expr),*]) => { [$($inpp,)* $($inpp),*] }; + (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; + (@2, [$($inpp:expr),*]) => { exp_disp!(@1, [$($inpp,)* $($inpp),*]) }; + (@1, [$($inpp:expr),*]) => { [$($inpp,)* $($inpp),*] }; } type CacheNode = Option>; /// a builder for fix constant len cache, should match nibble ops `NIBBLE_LEN` pub trait CacheBuilder { - /// size of cache - const SIZE: usize; - /// the fix cache - type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; - /// builder for cache - fn new_vec_slice_buff() -> Self::AN; + /// size of cache + const SIZE: usize; + /// the fix cache + type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; + /// builder for cache + fn new_vec_slice_buff() -> Self::AN; } pub struct Cache16; pub struct Cache4; impl CacheBuilder for Cache16 { - const SIZE: usize = 16; - type AN = [CacheNode; 16]; - #[inline(always)] - fn new_vec_slice_buff() -> Self::AN { - exp_disp!(@3, [None,None]) - } + const SIZE: usize = 16; + type AN = [CacheNode; 16]; + #[inline(always)] + fn new_vec_slice_buff() -> Self::AN { + exp_disp!(@3, [None,None]) + } } impl CacheBuilder for Cache4 { - const SIZE: usize = 4; - type AN = [CacheNode; 4]; - #[inline(always)] - fn new_vec_slice_buff() -> Self::AN { - exp_disp!(@2, [None]) - } + const SIZE: usize = 4; + type AN = [CacheNode; 4]; + #[inline(always)] + fn new_vec_slice_buff() -> Self::AN { + exp_disp!(@2, [None]) + } } type ArrayNode = <::CB as CacheBuilder>>::AN; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) @@ -78,7 +78,7 @@ where fn new() -> Self { let mut v = Vec::with_capacity(INITIAL_DEPTH); (0..INITIAL_DEPTH).for_each(|_| - v.push((T::CB::new_vec_slice_buff(), false, None))); + v.push((T::CB::new_vec_slice_buff(), false, None))); CacheAccum(v, PhantomData) } @@ -263,7 +263,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) B: AsRef<[u8]>, F: ProcessEncodedNode>, { - let no_ext = !T::USE_EXTENSION; + let no_ext = !T::USE_EXTENSION; let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); @@ -442,7 +442,31 @@ mod test { ]); } + fn test_iter(data: Vec<(Vec,Vec)>) { + use reference_trie::{RefTrieDBMut, TrieMut, RefTrieDB, Trie}; + + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMut::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + let t = RefTrieDB::new(&db, &root).unwrap(); + for (i, kv) in t.iter().unwrap().enumerate() { + let (k,v) = kv.unwrap(); + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + assert_eq!(k,key); + assert_eq!(v,val); + } + } + fn compare_impl(data: Vec<(Vec,Vec)>) { + test_iter(data.clone()); compare_impl_h(data.clone()); compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 8c24a998..19a27316 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -86,13 +86,11 @@ impl NibbleVec { self.clear(); return; } - let nb_rem = if (self.len - mov) % N::NIBBLE_PER_BYTE > 0 { - mov / N::NIBBLE_PER_BYTE - } else { - (mov + N::NIBBLE_PER_BYTE - 1) / N::NIBBLE_PER_BYTE - }; - (0..nb_rem).for_each(|_|{ self.inner.pop(); }); - self.len -= mov; + let end = self.len - mov; + let end_ix = end / N::NIBBLE_PER_BYTE + + if end % N::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; + (end_ix..self.inner.len()).for_each(|_|{ self.inner.pop(); }); + self.len = end; let pos = self.len % N::NIBBLE_PER_BYTE; if pos != 0 { let kl = self.inner.len() - 1; @@ -122,10 +120,9 @@ impl NibbleVec { if offset > 0 { let (s1, s2) = N::split_shifts(offset); self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); + (0..v.inner.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); if final_offset > 0 { - (0..v.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); - } else { - (1..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); + self.inner.push(v.inner[v.inner.len() - 1] << s1); } } else { (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); From 530f5f1dbcdec20f7f09c7e694c8ae8e46d02dcf Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 9 May 2019 10:40:54 +0200 Subject: [PATCH 067/120] Add debug assertion to help implementation of codec. --- trie-db/src/triedbmut.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index d239445f..aba92235 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1290,6 +1290,8 @@ where /// combine two NodeKeys fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { + debug_assert!(start.0 < N::NIBBLE_LEN); + debug_assert!(end.0 < N::NIBBLE_LEN); let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; let _shifted = N::shift_key(start, final_ofset); let st = if end.0 > 0 { From a8bcdf5d899e1ee407279171bdb6f22118a81285 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 9 May 2019 10:45:55 +0200 Subject: [PATCH 068/120] fix --- trie-db/src/triedbmut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index aba92235..e4bd1e91 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1290,8 +1290,8 @@ where /// combine two NodeKeys fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { - debug_assert!(start.0 < N::NIBBLE_LEN); - debug_assert!(end.0 < N::NIBBLE_LEN); + debug_assert!(start.0 < N::NIBBLE_PER_BYTE); + debug_assert!(end.0 < N::NIBBLE_PER_BYTE); let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; let _shifted = N::shift_key(start, final_ofset); let st = if end.0 > 0 { From 28271c2a7408a945465d0fa1baa773377de8944f Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 9 May 2019 16:59:33 +0200 Subject: [PATCH 069/120] lookup advance branch for other than 16 --- trie-db/src/triedb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 0841febc..bb78b972 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -277,7 +277,7 @@ impl Crumb { | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), (&Status::AtChild(x), &OwnedNode::Branch(_)) | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) - if x < 15 => Status::AtChild(x + 1), + if x < N::NIBBLE_LEN => Status::AtChild(x + 1), _ => Status::Exiting, } } From 57bb429b0fb5b1e02d6c0a4fb5f10b14cb430557 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 10 May 2019 10:38:40 +0200 Subject: [PATCH 070/120] no_std update --- hash-db/src/lib.rs | 6 +++--- memory-db/src/lib.rs | 20 +++++++++++++++----- trie-db/src/iter_build.rs | 5 ++++- trie-db/src/lib.rs | 1 - trie-db/src/node.rs | 3 ++- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index bb734d9f..7979ba98 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -20,13 +20,13 @@ use std::fmt::Debug; #[cfg(feature = "std")] use std::hash; +#[cfg(not(feature = "std"))] +use core::hash; + #[cfg(feature = "std")] pub trait MaybeDebug: Debug {} #[cfg(feature = "std")] impl MaybeDebug for T {} - -#[cfg(not(feature = "std"))] -use core::hash; #[cfg(not(feature = "std"))] pub trait MaybeDebug {} #[cfg(not(feature = "std"))] diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 3c2396d7..18ed43dd 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -26,7 +26,8 @@ extern crate hashmap_core; extern crate alloc; #[cfg(test)] extern crate keccak_hasher; -use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, AsHashDB, AsPlainDB, Prefix}; +use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, + AsHashDB, AsPlainDB, Prefix}; #[cfg(feature = "std")] use heapsize::HeapSizeOf; #[cfg(feature = "std")] @@ -58,6 +59,15 @@ use core::{ #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +pub trait MaybeDebug: std::fmt::Debug {} +#[cfg(feature = "std")] +impl MaybeDebug for T {} +#[cfg(not(feature = "std"))] +pub trait MaybeDebug {} +#[cfg(not(feature = "std"))] +impl MaybeDebug for T {} + /// Reference-counted memory-based `HashDB` implementation. /// /// Use `new()` to create a new database. Insert items with `insert()`, remove items @@ -121,8 +131,8 @@ impl PartialEq> for MemoryDB where H: KeyHasher, KF: KeyFunction, - >::Key: Eq + std::fmt::Debug, - T: Eq + std::fmt::Debug, + >::Key: Eq + MaybeDebug, + T: Eq + MaybeDebug, { fn eq(&self, other: &MemoryDB) -> bool { for a in self.data.iter() { @@ -140,8 +150,8 @@ impl Eq for MemoryDB where H: KeyHasher, KF: KeyFunction, - >::Key: Eq + std::fmt::Debug, - T: Eq + std::fmt::Debug, + >::Key: Eq + MaybeDebug, + T: Eq + MaybeDebug, {} pub trait KeyFunction { diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 801b05e1..b2da3ba1 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -15,13 +15,16 @@ //! Alternative tools for working with key value iterator without recursion. use hash_db::{Hasher, HashDB, Prefix}; -use std::marker::PhantomData; +use core_::marker::PhantomData; use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; use node_codec::NodeCodec; use crate::{TrieLayOut, TrieHash}; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + macro_rules! exp_disp { (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; (@2, [$($inpp:expr),*]) => { exp_disp!(@1, [$($inpp,)* $($inpp),*]) }; diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index eedba6b9..1acdcb79 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] //! Trie interface and implementation. diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index cc99f097..9bbff32b 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -29,7 +29,8 @@ pub type NodeKey = (usize, ElasticArray36); pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); /// Type of node in the trie and essential information thereof. -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum Node<'a, N: NibbleOps> { /// Null trie node; could be an empty root or an empty branch entry. Empty, From bce5c660a462804191d533ce77ecc8cb3a5038a3 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 13 May 2019 11:41:04 +0200 Subject: [PATCH 071/120] Fix no ext key lookup --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/iter_build.rs | 30 ++++++++++++++++++++++++++ trie-db/src/lookup.rs | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 06c099e9..d2526ccf 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -107,6 +107,7 @@ impl ChildBitmap for BitMap16 { dest[1] = (bitmap / 256) as u8; } } + /// bitmap codec for radix 4 pub struct BitMap4(u8); @@ -554,7 +555,6 @@ impl Decode for NodeHeaderNoExt { } /// Simple reference implementation of a `NodeCodec`. -/// This is similar to ethereum implementation. #[derive(Default, Clone)] pub struct ReferenceNodeCodec(PhantomData); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index b2da3ba1..b38602bd 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -466,10 +466,40 @@ mod test { assert_eq!(k,key); assert_eq!(v,val); } + for ((k, v)) in data.into_iter() { + assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); + } + } + + fn test_iter_no_ext(data: Vec<(Vec,Vec)>) { + use reference_trie::{RefTrieDBMutNoExt, TrieMut, RefTrieDBNoExt, Trie}; + + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + for (i, kv) in t.iter().unwrap().enumerate() { + let (k,v) = kv.unwrap(); + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + assert_eq!(k,key); + assert_eq!(v,val); + } + for ((k, v)) in data.into_iter() { + assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); + } } fn compare_impl(data: Vec<(Vec,Vec)>) { test_iter(data.clone()); + test_iter_no_ext(data.clone()); compare_impl_h(data.clone()); compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 3a0c2068..f1f8e8ea 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -105,6 +105,7 @@ where Some(x) => { node_data = x; partial = partial.mid(slice.len() + 1); + key_nibbles += slice.len() + 1; } None => return Ok(None) } From f9430e02baed171e1eafeaa92152bc9eff0d290b Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 13 May 2019 14:53:03 +0200 Subject: [PATCH 072/120] smaller empty prefix result --- memory-db/src/lib.rs | 2 +- trie-db/benches/bench.rs | 114 ++++++++++++++++++++++++++++++ trie-db/src/lib.rs | 6 +- trie-db/src/nibble/mod.rs | 12 ++-- trie-db/src/nibble/nibbleslice.rs | 1 + trie-db/src/node.rs | 7 +- trie-db/src/recorder.rs | 5 +- trie-db/src/triedb.rs | 7 +- trie-db/src/triedbmut.rs | 10 +-- 9 files changed, 145 insertions(+), 19 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 18ed43dd..62005bfc 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -167,8 +167,8 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { prefixed_key.extend_from_slice(prefix.0); if (prefix.1).0 > 0 { prefixed_key.push((prefix.1).1); + prefixed_key.push((prefix.1).0); // put size to avoid any possible collision } - prefixed_key.push((prefix.1).0); // put size to avoid any possible collision prefixed_key.extend_from_slice(key.as_ref()); prefixed_key } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index af57dcc1..bf9ef4ef 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -19,6 +19,9 @@ criterion_group!(benches, nibble_common_prefix, root_old, root_new, + root_a_big_v, + root_a_small_v, + root_b_small_v, ); criterion_main!(benches); @@ -50,6 +53,87 @@ fn nibble_common_prefix(b: &mut Criterion) { }); } +fn root_a_big_v(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input2(29, 204800, 1024), + ]; + + c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + +fn root_b_big_v(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input2(29, 204800 / 2, 1024 * 2), + ]; + + c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + + +fn root_a_small_v(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input2(29, 204800, 32), + ]; + + c.bench_function_over_inputs("root_a_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + +fn root_b_small_v(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input2(29, 204800 / 2, 32), + ]; + + c.bench_function_over_inputs("root_b_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + fn root_old(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ input(1, 5120), @@ -122,6 +206,26 @@ fn fuzz_to_data(input: Vec) -> Vec<(Vec,Vec)> { result } +fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let minkeylen = 1; + let maxkeylen = 32; + let mut ix = 0; + loop { + let keylen = 32; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = vec![input[ix];vl]; + result.push((key,val)); + } + result +} + + fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { let mut m = std::collections::BTreeMap::new(); for (k,v) in input.into_iter() { @@ -139,3 +243,13 @@ fn input(seed: u64, len: usize) -> Vec<(Vec,Vec)> { let data = data_sorted_unique(fuzz_to_data(data)); data } + +fn input2(seed: u64, len: usize, vl: usize) -> Vec<(Vec,Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + let data = data_sorted_unique(fuzz_to_data2(data,vl)); + data +} diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 1acdcb79..32906442 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -106,7 +106,8 @@ pub type DBValue = elastic_array::ElasticArray128; /// /// These borrow the data within them to avoid excessive copying on every /// trie operation. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum TrieError { /// Attempted to create a trie with a state root not in the DB. InvalidStateRoot(T), @@ -246,7 +247,8 @@ pub trait TrieIterator: Iterator { } /// Trie types -#[derive(Debug, PartialEq, Clone)] +#[derive(PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum TrieSpec { /// Generic trie. Generic, diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index a492f79c..e93fedfd 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -160,7 +160,8 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } /// half byte nibble prepend encoding -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] pub struct NibbleHalf; @@ -183,7 +184,8 @@ impl NibbleOps for NibbleHalf { type ChildSliceIx = ChildSliceIx16; } -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Debug)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] pub struct NibbleQuarter; // new_padded_end merged @@ -200,7 +202,8 @@ impl NibbleOps for NibbleQuarter { /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. -#[derive(Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq)] pub struct NibbleVec { inner: ElasticArray36, len: usize, @@ -288,7 +291,8 @@ impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { macro_rules! child_slice_ix { ($me: ident, $size: expr, $pre: expr) => { - #[derive(Default, Eq, PartialEq, Debug, Clone)] + #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Default, Eq, PartialEq, Clone)] /// child slice ix for radix $size pub struct $me([usize; $size + 1]); diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index d563f8ab..82372381 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -261,6 +261,7 @@ impl<'a, N: NibbleOps> Ord for NibbleSlice<'a, N> { } } +#[cfg(feature = "std")] impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in 0..self.len() { diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 9bbff32b..9ec47b80 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -47,9 +47,9 @@ pub enum Node<'a, N: NibbleOps> { /// Branch node with support for a nibble (to avoid extension node) NibbledBranch(NibbleSlice<'a, N>, BranchChildrenSlice<'a, N>, Option<&'a [u8]>), } - /// A Sparse (non mutable) owned vector struct to hold branch keys and value -#[derive(Eq, PartialEq, Debug, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Eq, PartialEq, Clone)] pub struct Branch { data: Vec, data_ix: usize, @@ -110,7 +110,8 @@ impl Branch { } /// An owning node type. Useful for trie iterators. -#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq)] pub enum OwnedNode { /// Empty trie node. Empty, diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index 0d6bd3ea..28879e24 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -18,7 +18,8 @@ use alloc::vec::Vec; /// A record of a visited node. -#[derive(PartialEq, Eq, Debug, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone)] pub struct Record { /// The depth of this node. pub depth: u32, @@ -31,7 +32,7 @@ pub struct Record { } /// Records trie nodes as they pass it. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] pub struct Recorder { nodes: Vec>, min_depth: u32, diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index bb78b972..f736d84c 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -253,7 +253,9 @@ where } } -#[derive(Clone, Eq, PartialEq, Debug)] + +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Eq, PartialEq)] enum Status { Entering, At, @@ -261,7 +263,8 @@ enum Status { Exiting, } -#[derive(Eq, PartialEq, Debug)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Eq, PartialEq)] struct Crumb { node: OwnedNode, status: Status, diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index e4bd1e91..2241fb26 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -45,11 +45,11 @@ use alloc::vec::Vec; // For lookups into the Node storage buffer. // This is deliberately non-copyable. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] struct StorageHandle(usize); // Handles to nodes in the trie. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] enum NodeHandle { /// Loaded into memory. InMemory(StorageHandle), @@ -74,7 +74,7 @@ fn empty_children() -> Vec>> { type NibbleFullKey<'key, N> = NibbleSlice<'key, N>; /// Node types in the Trie. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] enum Node { /// Empty node. Empty, @@ -931,7 +931,7 @@ where match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. - #[derive(Debug)] + #[cfg_attr(feature = "std", derive(Debug))] enum UsedIndex { None, One(u8), @@ -973,7 +973,7 @@ where }, Node::NibbledBranch(enc_nibble, mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. - #[derive(Debug)] + #[cfg_attr(feature = "std", derive(Debug))] enum UsedIndex { None, One(u8), From 0ada0af8b1c27e3ad089601e8c9881f6a484413e Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 13 May 2019 17:44:42 +0200 Subject: [PATCH 073/120] Fix wasm code (rust std doc for usize is unaccurate). --- memory-db/src/lib.rs | 1 - trie-db/src/node.rs | 6 ++++-- trie-db/src/triedbmut.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 62005bfc..f44e7203 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -15,7 +15,6 @@ //! Reference-counted memory-based `HashDB` implementation. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] extern crate hash_db; #[cfg(feature = "std")] diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 9ec47b80..af098416 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -89,8 +89,10 @@ impl Branch { fn index_bound(&self, index: usize) -> Option { use core_::convert::TryInto; - let s = self.ubounds_ix + index * 8; - let e = s + 8; + use core_::mem; + let usize_len = mem::size_of::(); + let s = self.ubounds_ix + index * usize_len; + let e = s + usize_len; if self.data.len() < e { None } else { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 2241fb26..143f1f19 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -509,15 +509,19 @@ where /// the insertion inspector. fn insert_inspector(&mut self, node: Node>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { let partial = key.clone(); + + #[cfg(feature = "std")] trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); Ok(match node { Node::Empty => { + #[cfg(feature = "std")] trace!(target: "trie", "empty: COMPOSE"); InsertAction::Replace(Node::Leaf(partial.to_stored(), value)) }, Node::Branch(mut children, stored_value) => { debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { @@ -551,6 +555,7 @@ where }, Node::NibbledBranch(encoded, mut children, stored_value) => { debug_assert!(!L::USE_EXTENSION); + #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); let existing_key = NibbleSlice::from_stored(&encoded); @@ -566,6 +571,7 @@ where } } else if cp < existing_key.len() { // insert a branch value in between + #[cfg(feature = "std")] trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); let low = Node::NibbledBranch(existing_key.mid(cp + 1).to_stored(), children, stored_value); let ix = existing_key.at(cp); @@ -598,6 +604,7 @@ where } else { // append after cp == existing_key and partial > cp + #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; key.advance(cp + 1); @@ -625,6 +632,7 @@ where let existing_key = NibbleSlice::from_stored(&encoded); let cp = partial.common_prefix(&existing_key); if cp == existing_key.len() && cp == partial.len() { + #[cfg(feature = "std")] trace!(target: "trie", "equivalent-leaf: REPLACE"); // equivalent leaf. let unchanged = stored_value == value; @@ -637,6 +645,7 @@ where } } else if (L::USE_EXTENSION && cp == 0) || (!L::USE_EXTENSION && cp < existing_key.len()) { + #[cfg(feature = "std")] trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // one of us isn't empty: transmute to branch here @@ -660,6 +669,7 @@ where let branch_action = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch_action) } else if !L::USE_EXTENSION { + #[cfg(feature = "std")] trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -672,6 +682,7 @@ where } else if cp == existing_key.len() { debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix for an extension. @@ -686,6 +697,7 @@ where InsertAction::Replace(Node::Extension(existing_key.to_stored(), branch_handle)) } else { debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared prefix for an extension. @@ -708,6 +720,7 @@ where let existing_key = NibbleSlice::from_stored(&encoded); let cp = partial.common_prefix(&existing_key); if cp == 0 { + #[cfg(feature = "std")] trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); // partial isn't empty: make a branch here @@ -729,6 +742,7 @@ where let branch_action = self.insert_inspector(Node::Branch(children, None), key, value, old_val)?.unwrap_node(); InsertAction::Replace(branch_action) } else if cp == existing_key.len() { + #[cfg(feature = "std")] trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); // fully-shared prefix. @@ -744,6 +758,7 @@ where false => InsertAction::Restore(new_ext), } } else { + #[cfg(feature = "std")] trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); // partially-shared. @@ -798,6 +813,7 @@ where (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; if let Some(child) = children[idx].take() { + #[cfg(feature = "std")] trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); let prefix = key.clone(); key.advance(1); @@ -815,6 +831,7 @@ where None => { // the child we took was deleted. // the node may need fixing. + #[cfg(feature = "std")] trace!(target: "trie", "branch child deleted, partial={:?}", partial); Action::Replace(self.fix(Node::Branch(children, value), prefix)?) } @@ -848,6 +865,7 @@ where let idx = partial.at(cp) as usize; if let Some(child) = children[idx].take() { + #[cfg(feature = "std")] trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); let prefix = key.clone(); key.advance(cp + 1); @@ -865,6 +883,7 @@ where None => { // the child we took was deleted. // the node may need fixing. + #[cfg(feature = "std")] trace!(target: "trie", "branch child deleted, partial={:?}", partial); Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) }, @@ -882,6 +901,7 @@ where Action::Delete } else { // leaf the node alone. + #[cfg(feature = "std")] trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::::from_stored(&encoded)); Action::Restore(Node::Leaf(encoded, value)) } @@ -893,6 +913,7 @@ where }; if cp == existing_len { // try to remove from the child branch. + #[cfg(feature = "std")] trace!(target: "trie", "removing from extension child, partial={:?}", partial); let prefix = key.clone(); key.advance(cp); @@ -961,11 +982,13 @@ where } (UsedIndex::None, Some(value)) => { // make a leaf. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: branch -> leaf"); Ok(Node::Leaf(NibbleSlice::::new(&[]).to_stored(), value)) } (_, value) => { // all is well. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: restoring branch"); Ok(Node::Branch(children, value)) } @@ -1040,11 +1063,13 @@ where }, (UsedIndex::None, Some(value)) => { // make a leaf. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: branch -> leaf"); Ok(Node::Leaf(enc_nibble, value)) }, (_, value) => { // all is well. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: restoring branch"); Ok(Node::NibbledBranch(enc_nibble, children, value)) }, @@ -1090,6 +1115,7 @@ where // subpartial let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + #[cfg(feature = "std")] trace!(target: "trie", "fixing: extension combination. new_partial={:?}", partial); self.fix(Node::Extension(partial, sub_child), key) } @@ -1102,10 +1128,12 @@ where // subpartial oly let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + #[cfg(feature = "std")] trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", partial); Ok(Node::Leaf(partial, value)) } child_node => { + #[cfg(feature = "std")] trace!(target: "trie", "fixing: restoring extension"); // reallocate the child node. @@ -1126,9 +1154,11 @@ where /// Commit the in-memory changes to disk, freeing their storage and /// updating the state root. pub fn commit(&mut self) { + #[cfg(feature = "std")] trace!(target: "trie", "Committing trie changes to db."); // always kill all the nodes on death row. + #[cfg(feature = "std")] trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for (hash, prefix) in self.death_row.drain() { self.db.remove(&hash, (&prefix.0[..], prefix.1)); @@ -1148,6 +1178,7 @@ where k.drop_lasts(mov); cr }); + #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); *self.root = self.db.insert(EMPTY_NIBBLE, &encoded_root[..]); self.hash_count += 1; @@ -1240,6 +1271,7 @@ where let mut old_val = None; + #[cfg(feature = "std")] trace!(target: "trie", "insert: key={:#x?}, value={:#x?}", key, value); let root_handle = self.root_handle(); @@ -1250,6 +1282,7 @@ where &mut old_val, )?; + #[cfg(feature = "std")] trace!(target: "trie", "insert: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(new_handle); @@ -1257,6 +1290,7 @@ where } fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + #[cfg(feature = "std")] trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); @@ -1265,10 +1299,12 @@ where match self.remove_at(root_handle, &mut key, &mut old_val)? { Some((handle, changed)) => { + #[cfg(feature = "std")] trace!(target: "trie", "remove: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(handle); } None => { + #[cfg(feature = "std")] trace!(target: "trie", "remove: obliterated trie"); self.root_handle = NodeHandle::Hash(L::C::hashed_null_node()); *self.root = L::C::hashed_null_node(); From 1a090449ebbb175ef1bc7758da25fed2e6485951 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 14 May 2019 09:37:02 +0200 Subject: [PATCH 074/120] bench len/size factor 2 --- trie-db/benches/bench.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index bf9ef4ef..e0a24c39 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -19,9 +19,10 @@ criterion_group!(benches, nibble_common_prefix, root_old, root_new, - root_a_big_v, - root_a_small_v, - root_b_small_v, + root_a_big_v, + root_b_big_v, + root_a_small_v, + root_b_small_v, ); criterion_main!(benches); @@ -55,7 +56,7 @@ fn nibble_common_prefix(b: &mut Criterion) { fn root_a_big_v(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - input2(29, 204800, 1024), + input2(29, 204800 / 2, 512 * 2), ]; c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| @@ -75,10 +76,10 @@ fn root_a_big_v(c: &mut Criterion) { fn root_b_big_v(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - input2(29, 204800 / 2, 1024 * 2), + input2(29, 204800, 512), ]; - c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + c.bench_function_over_inputs("root_b_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); // this is in `ref_trie_root` added here to make things comparable @@ -116,7 +117,7 @@ fn root_a_small_v(c: &mut Criterion) { fn root_b_small_v(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - input2(29, 204800 / 2, 32), + input2(29, 204800 / 2, 32 * 2), ]; c.bench_function_over_inputs("root_b_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| From 7cec6af6486ecd257b2e033ed3fb9fc76f93f8f6 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 23 May 2019 09:38:36 +0200 Subject: [PATCH 075/120] Add test with values on encode_size, fix debug output (previous change was related to nightly and should not have been include in commit) --- test-support/reference-trie/src/lib.rs | 25 ++++++++++++++++++++ trie-db/src/triedb.rs | 32 +++++++++++++------------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index d2526ccf..d3580a21 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1208,3 +1208,28 @@ fn too_big_nibble_len () { } else { None }; assert!(o_sl.is_some()); } + +#[test] +fn size_encode_limit_values () { + let sizes = [0, 1, 62, 63, 64, 317, 318, 319, 572, 573, 574]; + let encs = [ + vec![0], + vec![1], + vec![0x3e], + vec![0x3f, 0], + vec![0x3f, 1], + vec![0x3f, 0xfe], + vec![0x3f, 0xff, 0], + vec![0x3f, 0xff, 1], + vec![0x3f, 0xff, 0xfe], + vec![0x3f, 0xff, 0xff, 0], + vec![0x3f, 0xff, 0xff, 1], + ]; + for i in 0..sizes.len() { + let mut enc = Vec::new(); + s_encode_size_and_prefix(sizes[i], 0, &mut enc); + assert_eq!(enc, encs[i]); + let s_dec = s_decode_size(encs[i][0], &mut &encs[i][1..]); + assert_eq!(s_dec, Some(sizes[i])); + } +} diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index f736d84c..a8b04fdf 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -792,38 +792,38 @@ mod tests { slice: , value: [ 65, - 65, - ], + 65 + ] }, Node::Leaf { index: 2, slice: , value: [ 65, - 66, - ], - }, + 66 + ] + } ], - value: None, - }, + value: None + } ], value: Some( [ - 65, - ], - ), + 65 + ] + ) }, Node::Leaf { index: 2, slice: , value: [ - 66, - ], - }, + 66 + ] + } ], - value: None, - }, - }, + value: None + } + } }"); } From b13bb9a7f864cb18974ec2b334fd4a95111aa086 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 23 May 2019 09:40:22 +0200 Subject: [PATCH 076/120] indent --- test-support/reference-trie/src/lib.rs | 42 +++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index d3580a21..75e576fa 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1211,25 +1211,25 @@ fn too_big_nibble_len () { #[test] fn size_encode_limit_values () { - let sizes = [0, 1, 62, 63, 64, 317, 318, 319, 572, 573, 574]; - let encs = [ - vec![0], - vec![1], - vec![0x3e], - vec![0x3f, 0], - vec![0x3f, 1], - vec![0x3f, 0xfe], - vec![0x3f, 0xff, 0], - vec![0x3f, 0xff, 1], - vec![0x3f, 0xff, 0xfe], - vec![0x3f, 0xff, 0xff, 0], - vec![0x3f, 0xff, 0xff, 1], - ]; - for i in 0..sizes.len() { - let mut enc = Vec::new(); - s_encode_size_and_prefix(sizes[i], 0, &mut enc); - assert_eq!(enc, encs[i]); - let s_dec = s_decode_size(encs[i][0], &mut &encs[i][1..]); - assert_eq!(s_dec, Some(sizes[i])); - } + let sizes = [0, 1, 62, 63, 64, 317, 318, 319, 572, 573, 574]; + let encs = [ + vec![0], + vec![1], + vec![0x3e], + vec![0x3f, 0], + vec![0x3f, 1], + vec![0x3f, 0xfe], + vec![0x3f, 0xff, 0], + vec![0x3f, 0xff, 1], + vec![0x3f, 0xff, 0xfe], + vec![0x3f, 0xff, 0xff, 0], + vec![0x3f, 0xff, 0xff, 1], + ]; + for i in 0..sizes.len() { + let mut enc = Vec::new(); + s_encode_size_and_prefix(sizes[i], 0, &mut enc); + assert_eq!(enc, encs[i]); + let s_dec = s_decode_size(encs[i][0], &mut &encs[i][1..]); + assert_eq!(s_dec, Some(sizes[i])); + } } From 06f53601803222fa5c99a381ddbb3277f6103431 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 May 2019 10:04:48 +0200 Subject: [PATCH 077/120] do not allow decoding of longer length node (encoding still truncate) --- test-support/reference-trie/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 75e576fa..0d084bcd 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -356,7 +356,12 @@ impl TrieStream for ReferenceTrieStreamNoExt { value.encode_to(&mut self.buffer); } - fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator) { + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator + ) { if let Some(partial) = maybe_key { if maybe_value.is_some() { self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchWithValue)); @@ -466,7 +471,7 @@ fn s_decode_size(first: u8, input: &mut I) -> Option { } result += 255; } - Some(s_cst::NIBBLE_SIZE_BOUND) + None } #[test] From ed68da63492347db6757d23ed3bee5de96b5afec Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 May 2019 17:26:52 +0200 Subject: [PATCH 078/120] format --- test-support/reference-trie/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 0d084bcd..ddd5ed6f 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -856,13 +856,18 @@ impl> NodeCodec, _nbnibble: usize, _child: ChildReference<::Out>) -> Vec { + fn ext_node( + _partial: impl Iterator, + _nbnibble: usize, + _child: ChildReference<::Out>, + ) -> Vec { unreachable!() } fn branch_node( _children: impl Iterator::Out>>>>, - _maybe_value: Option<&[u8]>) -> Vec { + _maybe_value: Option<&[u8]>, + ) -> Vec { unreachable!() } @@ -870,7 +875,8 @@ impl> NodeCodec, nb_nibble: usize, children: impl Iterator::Out>>>>, - maybe_value: Option<&[u8]>) -> Vec { + maybe_value: Option<&[u8]>, + ) -> Vec { let mut output = if maybe_value.is_some() { partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchWithValue) } else { From feb0660b927991ec1944fd287a46f45bb0355a0c Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jun 2019 21:27:22 +0200 Subject: [PATCH 079/120] Legacy prefix tools. --- memory-db/src/lib.rs | 34 ++++++++++++++ test-support/reference-trie/src/lib.rs | 18 ++++--- trie-db/src/iter_build.rs | 2 +- trie-db/src/lib.rs | 65 +++++++++++++++++++++++++- trie-db/src/triedb.rs | 2 +- 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index f44e7203..26ae16b2 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -172,6 +172,24 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { prefixed_key } +/// Legacy method for db using previous version of prefix encoding. +/// Only for trie radix 16. +pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + if (prefix.1).0 == 0 { + prefixed_key.extend_from_slice(prefix.0); + } else { + let mut prev = 0u8; + for i in prefix.0.iter() { + prefixed_key.push((prev << 4) + (*i >> 4)); + prev = *i; + } + prefixed_key.push((prev << 4) + ((prefix.1).1 >> 4)); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + /// Make database key from hash only. pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { key.clone() @@ -201,6 +219,22 @@ impl KeyFunction for PrefixedKey { } } +#[derive(Clone,Debug)] +/// Key function that concatenates prefix and hash. +/// This is doing useless computation and should only be +/// used for legacy purpose. +pub struct LegacyPrefixedKey(PhantomData); + +impl KeyFunction for LegacyPrefixedKey { + type Key = Vec; + + fn key(hash: &H::Out, prefix: Prefix) -> Vec { + legacy_prefixed_key::(hash, prefix) + } +} + + + impl<'a, H, KF, T> Default for MemoryDB where H: KeyHasher, diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index ddd5ed6f..d08d0a0f 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -42,7 +42,7 @@ use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, ChildBitmap, BitMap, ChildSliceIx}; -pub use trie_db::{Record, TrieLayOut, NibbleHalf, NibbleQuarter, NibbleOps}; +pub use trie_db::{Record, TrieLayOut, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum @@ -56,6 +56,8 @@ impl TrieLayOut for LayoutOri { type CB = Cache16; } +impl TrieOps for LayoutOri { } + /// trie layout similar to substrate one pub struct LayoutNew; @@ -67,17 +69,21 @@ impl TrieLayOut for LayoutNew { type CB = Cache16; } +impl TrieOps for LayoutNew { } + /// Test quarter nibble pub struct LayoutNewQuarter; impl TrieLayOut for LayoutNewQuarter { - const USE_EXTENSION: bool = false; - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; - type N = NibbleQuarter; - type CB = Cache4; + const USE_EXTENSION: bool = false; + type H = keccak_hasher::KeccakHasher; + type C = ReferenceNodeCodecNoExt; + type N = NibbleQuarter; + type CB = Cache4; } +impl TrieOps for LayoutNewQuarter { } + /// bitmap codec for radix 16 pub struct BitMap16(u16); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index b38602bd..44457154 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -323,7 +323,7 @@ pub trait ProcessEncodedNode { /// As for all `ProcessEncodedNode` implementation, it /// is only for full trie parsing (not existing trie). pub struct TrieBuilder<'a, H, HO, V, DB> { - pub db: &'a mut DB, + db: &'a mut DB, pub root: Option, _ph: PhantomData<(H,V)>, } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 32906442..e3636b3f 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2019 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -365,7 +365,7 @@ where pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } -/// trait with definition of trie layout +/// Trait with definition of trie layout pub trait TrieLayOut { /// does the trie use extension before its branch const USE_EXTENSION: bool; @@ -375,6 +375,67 @@ pub trait TrieLayOut { type CB: CacheBuilder<::Out>; } +/// Trait with operation on key value iterator. +/// This trait contains its own default implementation. +/// Implementing it allows to use alternate algorithm. +pub trait TrieOps: Sized + TrieLayOut { + /// Operation to build a trie db from its ordered iterator of key value + fn trie_build(db: &mut DB, input: I) -> ::Out where + DB: HashDB, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieBuilder::new(db); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + /// Determine a trie root given its ordered contents, closed form. + fn trie_root(input: I) -> ::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieRoot::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + + /// Determine a trie root node's data given its ordered contents, closed form. + fn trie_root_unhashed(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieRootUnhashed::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + + /// Encoding of index as a key (when reusing general trie for + /// indexed trie). + fn encode_index(input: u32) -> Vec { + // be for byte ordering + input.to_be_bytes().to_vec() + } + /// A trie root formed from the items, with keys attached according to their + /// compact-encoded index (using `parity-codec` crate). + fn ordered_trie_root(input: I) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]>, + { + Self::trie_root(input + .into_iter() + .enumerate() + .map(|(i, v)| (Self::encode_index(i as u32), v)) + ) + } +/* /// Operation to build a trie from an unordered iterator of key value. + /// Operation to calculate a trie root from an ordered iterator of key value. + /// Operation to build a trie from an ordered iterator of key value.*/ +} + /// alias to acces hasher hash output type from a `TrieLayout` pub type TrieHash = <::H as Hasher>::Out; /// alias to acces bitmap codec from a `TrieLayout` diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index a8b04fdf..5b6a057b 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2019 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 776a1f33a12f26681e2543f020c8fe6c9a000ffd Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jun 2019 00:22:19 +0200 Subject: [PATCH 080/120] Fix legacy prefix --- memory-db/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 26ae16b2..ae6b76eb 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -177,9 +177,10 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); if (prefix.1).0 == 0 { + prefixed_key.push(0); prefixed_key.extend_from_slice(prefix.0); } else { - let mut prev = 0u8; + let mut prev = 0x01u8; for i in prefix.0.iter() { prefixed_key.push((prev << 4) + (*i >> 4)); prev = *i; From 43b32250901a252556fecf9339be8c1e8a5d816c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jun 2019 12:33:08 +0200 Subject: [PATCH 081/120] Some documentation, and switch `masked_right` semantic to be similar to `masked_left`. --- hash-db/src/lib.rs | 15 ++- memory-db/src/lib.rs | 10 +- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/iter_build.rs | 6 +- trie-db/src/nibble/mod.rs | 133 +++++++++++++++---------- trie-db/src/nibble/nibbleslice.rs | 35 ++++--- trie-db/src/nibble/nibblevec.rs | 9 +- trie-db/src/node_codec.rs | 8 +- trie-db/src/triedb.rs | 7 +- trie-db/src/triedbmut.rs | 11 +- trie-root/src/lib.rs | 33 ++++-- 11 files changed, 161 insertions(+), 108 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 7979ba98..c7e2ff09 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -33,13 +33,18 @@ pub trait MaybeDebug {} impl MaybeDebug for T {} -/// Empty prefix constant +/// An empty prefix constant. Mainly use for comparison +/// and for root nodes. pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0,0)); -/// prefix for trie value, encoded as reference and a last padded byte to -/// match `left` function of nibbleslice. -/// Padded byte is represented as a pair with number of nibble first and -/// padded value second. +/// The Prefix of a trie node. Prefix is nibble path up to +/// the node in the trie. +/// For a value node it is the value key without the Partial +/// bytes (encoded in the node). +/// Being the leftmost portion of a byte, its internal representation +/// is byte slice and and a padded byte. +/// The padded byte is represented as a pair with number of nibble first, +/// and left aligned padded value second. pub type Prefix<'a> = (&'a[u8], (u8, u8)); /// Trait describing an object that can hash a slice of bytes. Used to abstract diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index ae6b76eb..b95fd4be 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -151,7 +151,7 @@ impl Eq for MemoryDB KF: KeyFunction, >::Key: Eq + MaybeDebug, T: Eq + MaybeDebug, -{} +{ } pub trait KeyFunction { type Key: Send + Sync + Clone + hash::Hash + Eq ; @@ -161,6 +161,7 @@ pub trait KeyFunction { /// Make database key from hash and prefix. +/// This is byte ordered similarilly to the original trie key. pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); prefixed_key.extend_from_slice(prefix.0); @@ -234,8 +235,6 @@ impl KeyFunction for LegacyPrefixedKey { } } - - impl<'a, H, KF, T> Default for MemoryDB where H: KeyHasher, @@ -460,11 +459,10 @@ where } let key = KF::key(key, prefix); - let r = match self.data.get(&key) { + match self.data.get(&key) { Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), _ => None - }; - r + } } fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index d08d0a0f..5ff0c098 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -651,7 +651,7 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if nb_nibble_hpe > 0 { - output.push(N::masked_right((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, (partial.0).1)); + output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 44457154..68ff189c 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -311,7 +311,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) } } else { // nothing null root corner case - cb_ext.process(crate::nibble::EMPTY_NIBBLE, T::C::empty_node().to_vec(), true); + cb_ext.process(hash_db::EMPTY_PREFIX, T::C::empty_node().to_vec(), true); } } @@ -466,7 +466,7 @@ mod test { assert_eq!(k,key); assert_eq!(v,val); } - for ((k, v)) in data.into_iter() { + for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); } } @@ -492,7 +492,7 @@ mod test { assert_eq!(k,key); assert_eq!(v,val); } - for ((k, v)) in data.into_iter() { + for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); } } diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index e93fedfd..4849603c 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Nibble oriented methods +//! Nibble oriented methods. mod nibblevec; mod nibbleslice; @@ -20,55 +20,75 @@ use ::core_::cmp::*; use ::core_::marker::PhantomData; use elastic_array::ElasticArray36; use crate::node::NodeKey; +use super::MaybeDebug; -pub const EMPTY_NIBBLE: (&'static [u8], (u8, u8)) = (&[], (0, 0)); -// until const fn for pow +// Workaround no constant function for pow. const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; -/// Nibble specific variants -/// Note that some function are defined here but ideally it should just be a set of -/// constant (with function handling all constant case). -pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + super::MaybeDebug { - /// variant repr +/// This trait contain Trie nibble specific definitions. +/// This trait is mostly a collection of associated constant and some generic +/// methods. +/// Generic methods should not need redefinition except for optimization +/// purpose. +pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + MaybeDebug { + /// See [`ByteLayout`]. const REPR : ByteLayout; - /// Number of bit per nibble + /// Single nibble length in bit. const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); - /// Number of nibble per byte + /// Number of nibble per byte. const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; - /// Number of nibble per node (must be power of 2 and under 256) - const NIBBLE_LEN : usize = TWO_EXP[8 / Self::NIBBLE_PER_BYTE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); - /// padding bitmasks (could be calculated with a constant function). - /// First is bit mask to apply, second is right shift needed. + /// Number of child for a branch (trie radix). + const NIBBLE_LEN : usize = TWO_EXP[Self::BIT_PER_NIBBLE]; + //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); + /// Padding bitmasks, internally use for working on padding byte. + /// Length of this array is `Self::BIT_PER_NIBBLE`. + /// The first element of each pair is a bit mask to apply, + /// the second element is a right shift to apply in some case. + /// const PADDING_BITMASK: &'static [(u8, usize)] = &[ + /// Similar to following const function TODO EMCH switch to two + /// const function (will make code simpler). + /// ```rust + /// const BIT_PER_NIBBLE: usize = 4; + /// const fn padding_bitmask(ix: usize) -> (u8, usize) { + /// //assert!(ix < 8 / BIT_PER_NIBBLE); + /// let offset = BIT_PER_NIBBLE * ix; + /// (1u8 >> offset, 8 - offset) + /// } + /// ``` const PADDING_BITMASK: &'static [(u8, usize)]; - /// las ix for nible - const LAST_N_IX: usize = Self::NIBBLE_PER_BYTE - 1; - /// las ix for nible as a u8 (for pattern matching) - const LAST_N_IX_U8: u8 = Self::LAST_N_IX as u8; + /// Last nibble index as u8, just a convenience constant for iteration on all nibble. + const LAST_N_IX_U8: u8 = (Self::NIBBLE_PER_BYTE - 1) as u8; - /// Buff for slice index store (we do not include + /// Buffer type for slice index store (we do not include /// directly slice in it to avoid lifetime in /// trait type ChildSliceIx: ChildSliceIx; - /// mask a byte from a ix > 0 (ix being content) + + /// Mask a byte from a `ix` > 0 (ix being content). + /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. #[inline(always)] fn masked_left(ix: u8, b: u8) -> u8 { debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } - /// mask a byte from a ix > 0 (ix being padding) + /// Mask a byte from a ix > 0 (ix being content) + /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. #[inline(always)] fn masked_right(ix: u8, b: u8) -> u8 { - debug_assert!(ix > 0); - b & Self::PADDING_BITMASK[ix as usize].0 + if ix > 0 { + b & Self::PADDING_BITMASK[Self::NIBBLE_PER_BYTE - ix as usize].0 + } else { + b + } } - /// get value at ix from a right first byte + /// Get u8 nibble value at a given index of a byte. #[inline(always)] fn at_left(ix: u8, b: u8) -> u8 { (b & Self::PADDING_BITMASK[ix as usize].0) >> Self::PADDING_BITMASK[ix as usize].1 } - /// get nibble for left aligned array + /// Get u8 nibble value at a given index in a left aligned array. #[inline(always)] fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { Self::at_left( @@ -77,13 +97,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy ) } - /// push u8 nib value at ix into a existing byte - #[inline(always)] - fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { - into | (v << Self::PADDING_BITMASK[ix as usize].1) - } - - /// Get the nibble at position `i`. + /// Get u8 nibble value at a given index in a `NibbleSlice`. #[inline(always)] fn at(s: &NibbleSlice, i: usize) -> u8 { let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; @@ -91,13 +105,20 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy Self::at_left(pad as u8, s.data[ix]) } + /// Push u8 nibble value at a given index into an existing byte. + #[inline(always)] + fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { + into | (v << Self::PADDING_BITMASK[ix as usize].1) + } + #[inline] - /// Number of padding needed for a length `i`. + /// Calculate the number of needed padding a array of nibble length `i`. fn nb_padding(i: usize) -> usize { (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE } - /// split shifts for a given unaligned padding (pad != 0) + /// Calculate the array nibble shifts needed + /// for alignment a given unaligned padding (pad != 0). #[inline(always)] fn split_shifts(pad: usize) -> (usize, usize) { debug_assert!(pad > 0); @@ -106,7 +127,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy (s1, s2) } - /// get biggest common depth between two left aligned packed nibble arrays + /// Count the biggest common depth between two left aligned packed nibble slice. fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { // sorted assertion preventing out of bound for a in 0..v1.len() { @@ -118,7 +139,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy return v1.len() * Self::NIBBLE_PER_BYTE; } - /// number of common bit between two left pad byte + /// Calculate the number of common nibble between two left pad byte. #[inline(always)] fn left_common(a: u8, b: u8) -> usize { let mut i = 0; @@ -132,8 +153,9 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy return i; } - /// shift key (right) alignment to match a given left offset, possibly leaving - /// wrong end of Nodekey (eg to combine two keys) + /// Shifts right aligned key to add a given left offset. + /// Resulting in possibly padding at both left and right + /// (example usage when combining two keys). fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { let old_offset = key.0; key.0 = ofset; @@ -159,22 +181,22 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } } -/// half byte nibble prepend encoding +/// Radix 16 `NibbleOps` definition. #[cfg_attr(feature = "std", derive(Debug))] #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] pub struct NibbleHalf; - -/// Type of nibble in term of byte size +/// Ordered enumeration of the different possible number of nibble in +/// a byte. #[repr(usize)] pub enum ByteLayout { - /// nibble of one bit length + /// Radix 2 trie. Eight nibble per byte. Bit = 0, // 1, 8, 2 - /// nibble of a quarter byte length + /// Radix 4 trie. Four nibble per byte. Quarter = 1, // 2, 4, 4 - /// nibble of a half byte length + /// Radix 16 trie. Two nibble per byte. Half = 2, // 4, 2, 16 - /// nibble of one byte length + /// Radix 256 trie. One nibble per byte. Full = 3, // 8, 1, 256 } @@ -184,6 +206,7 @@ impl NibbleOps for NibbleHalf { type ChildSliceIx = ChildSliceIx16; } +/// Radix 4 `NibbleOps` definition. #[cfg_attr(feature = "std", derive(Debug))] #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] pub struct NibbleQuarter; @@ -202,6 +225,8 @@ impl NibbleOps for NibbleQuarter { /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. +/// Nibbles are always left aligned, so making a `NibbleVec` from +/// a `NibbleSlice` can get costy. #[cfg_attr(feature = "std", derive(Debug))] #[derive(Clone, PartialEq, Eq)] pub struct NibbleVec { @@ -245,18 +270,21 @@ pub struct NibbleSliceIterator<'a, N: NibbleOps> { i: usize, } -/// trait only here to avoid lifetime when storing slice -/// This would be useless with https://github.com/rust-lang/rust/issues/43408 +/// Technical trait only to access child slice from an encoded +/// representation of a branch. +/// This is use instead of `&[&[u8]]` to allow associated type +/// with a constant lenght. pub trait ChildSliceIx: AsRef<[usize]> + AsMut<[usize]> + Default + Eq + PartialEq + crate::MaybeDebug + Clone { - /// nb nibble in slice + /// Constant length for the number of children. const NIBBLE_LEN : usize; - /// bit to skip between slice + /// Constant size of header + /// Should only be use for inner implementation. const CONTENT_HEADER_SIZE: usize; - /// get an optional slice + /// Access a children slice at a given index. fn slice_at<'a>(&self, ix: usize, data: &'a [u8]) -> Option<&'a[u8]> { let b = (self.as_ref().get(ix), self.as_ref().get(ix + 1)); if let (Some(s), Some(e)) = b { @@ -270,12 +298,13 @@ pub trait ChildSliceIx: AsRef<[usize]> None } } + /// Iterator over the children slice. fn iter<'a>(&'a self, data: &'a [u8]) -> IterChildSliceIx<'a, Self> { IterChildSliceIx(self, 0, data) } } -/// iterator over `ChildSliceIx` trait +/// Iterator over `ChildSliceIx` trait. pub struct IterChildSliceIx<'a, CS>(&'a CS, usize, &'a[u8]); impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { @@ -293,7 +322,7 @@ macro_rules! child_slice_ix { ($me: ident, $size: expr, $pre: expr) => { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Default, Eq, PartialEq, Clone)] - /// child slice ix for radix $size + /// Child slice indexes for radix $size. pub struct $me([usize; $size + 1]); impl AsRef<[usize]> for $me { diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 82372381..5981f2a5 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -55,18 +55,21 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { NibbleSliceIterator { p: self, i: 0 } } - /// helper function for getting slice from `NodeKey` stored in nodes - pub fn from_stored(i: &(usize,ElasticArray36)) -> NibbleSlice { + /// Get nibble slice from a `NodeKey`. + pub fn from_stored(i: &NodeKey) -> NibbleSlice { NibbleSlice::::new_offset(&i.1[..], i.0) } - /// helper function to get `NodeKey` stored in nodes + /// Helper function to create a owned `NodeKey` from this `NibbleSlice`. pub fn to_stored(&self) -> NodeKey { let split = self.offset / N::NIBBLE_PER_BYTE; let offset = self.offset % N::NIBBLE_PER_BYTE; (offset, self.data[split..].into()) } - /// helper function to get `NodeKey` stored in nodes, warning slow + /// Helper function to create a owned `NodeKey` from this `NibbleSlice`, + /// and for a given number of nibble. + /// Warning this method can be slow (number of nibble does not align the + /// original padding). pub fn to_stored_range(&self, nb: usize) -> NodeKey { if nb >= self.len() { return self.to_stored() } if (self.offset + nb) % N::NIBBLE_PER_BYTE == 0 { @@ -75,6 +78,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; (nb % N::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end])) } else { + // unaligned let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); @@ -87,7 +91,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } - /// Is this an empty slice? + /// Return true if the slice contains no nibbles. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Get the length (in nibbles, naturally) of this slice. @@ -108,12 +112,14 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { marker: PhantomData, } } - /// Advance the view on the slice by `i` nibbles + + /// Advance the view on the slice by `i` nibbles. pub fn advance(&mut self, i: usize) { debug_assert!(self.len() >= i); self.offset += i; } - /// Return object to an offset position + + /// Move back to a previously valid fix offset position. pub fn back(&self, i: usize) -> NibbleSlice<'a, N> { NibbleSlice { data: self.data, @@ -122,7 +128,6 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } - /// Do we start with the same nibbles as the whole of `them`? pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } @@ -137,12 +142,13 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { i } - /// return first encoded byte and following slice + /// Return `Partial` representation of this slice: + /// first encoded byte and following slice. pub fn right(&'a self) -> Partial { let split = self.offset / N::NIBBLE_PER_BYTE; let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; if nb > 0 { - ((nb, N::masked_right(N::NIBBLE_PER_BYTE as u8 - nb, self.data[split])), &self.data[split + 1 ..]) + ((nb, N::masked_right(nb, self.data[split])), &self.data[split + 1 ..]) } else { ((0,0), &self.data[split..]) } @@ -152,11 +158,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn right_iter(&'a self) -> impl Iterator + 'a { let (mut first, sl) = self.right(); let mut ix = 0; - ::core_::iter::from_fn( move || { + ::core_::iter::from_fn(move || { if first.0 > 0 { - let ix = N::NIBBLE_PER_BYTE - first.0 as usize; first.0 = 0; - Some(N::masked_right(ix as u8, first.1)) + Some(N::masked_right(first.0, first.1)) } else { if ix < sl.len() { ix += 1; @@ -194,7 +199,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { ::core_::iter::from_fn( move || { if aligned { if nib_res > 0 { - let v = N::masked_right((N::NIBBLE_PER_BYTE - nib_res) as u8, self.data[ix]); + let v = N::masked_right(nib_res as u8, self.data[ix]); nib_res = 0; ix += 1; Some(v) @@ -209,7 +214,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { // unaligned if nib_res > 0 { let v = self.data[ix] >> s1; - let v = N::masked_right((N::NIBBLE_PER_BYTE - nib_res) as u8, v); + let v = N::masked_right(nib_res as u8, v); nib_res = 0; Some(v) } else if ix < ix_lim { diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 19a27316..8260a924 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -153,8 +153,9 @@ impl NibbleVec { self.len += sl.len() * N::NIBBLE_PER_BYTE; } - /// append slice or nibble - pub fn append_slice_nibble( + /// Utility function for chaining two optional appending + /// of `NibbleSliec` and/or a byte. + pub(crate) fn append_slice_nibble( &mut self, o_sl: Option<&NibbleSlice>, o_ix: Option @@ -170,8 +171,8 @@ impl NibbleVec { } res } - /// clone then append slice or nibble - pub fn clone_append_slice_nibble( + /// Utility function for `append_slice_nibble` after a clone. + pub(crate) fn clone_append_slice_nibble( &self, o_sl: Option<&NibbleSlice>, o_ix: Option diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index af34be5b..96143492 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -38,15 +38,15 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} -/// a nible slice, a partial length (0 to max nb nibble - 1) first byte and a slice of full bytes +/// Immutable representation of a nible slice (right aligned). +/// It contains a right aligned padded first byte (first pair element is the number of nibble +/// (0 to max nb nibble - 1), second pair element is the padded nibble), and a slice over +/// the remaining bytes. pub type Partial<'a> = ((u8,u8), &'a[u8]); -// TODO EMCH change node codec trait to use &mut self as input in order to run on internal buffer. -// (not for decode actually!!; code seems fine to do that and new layout trait is ok too /// Trait for trie node encoding/decoding /// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> /// avoid Vec by all means. -/// TODO also add inner trait impl for nibble ops encoding pub trait NodeCodec: Sized { /// Codec error type type Error: Error; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 5b6a057b..7a1feed4 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{HashDBRef, Prefix}; +use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; use nibble::{NibbleSlice, NibbleOps, ChildSliceIx}; -use nibble::EMPTY_NIBBLE; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; @@ -80,7 +79,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { - if !db.contains(root, EMPTY_NIBBLE) { + if !db.contains(root, EMPTY_PREFIX) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { Ok(TrieDB {db, root, hash_count: 0}) @@ -93,7 +92,7 @@ where /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, EMPTY_NIBBLE) + .get(self.root, EMPTY_PREFIX) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 143f1f19..3a7fb2e8 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -20,8 +20,7 @@ use super::node::Node as EncodedNode; use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; -use nibble::EMPTY_NIBBLE; -use hash_db::{HashDB, Hasher, Prefix}; +use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use nibble::{NibbleVec, NibbleSlice, NibbleOps, ChildSliceIx, IterChildSliceIx}; use elastic_array::ElasticArray36; use ::core_::mem; @@ -371,7 +370,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { - if !db.contains(root, EMPTY_NIBBLE) { + if !db.contains(root, EMPTY_PREFIX) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -1180,7 +1179,7 @@ where }); #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(EMPTY_NIBBLE, &encoded_root[..]); + *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -1332,7 +1331,7 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { let _shifted = N::shift_key(start, final_ofset); let st = if end.0 > 0 { let sl = start.1.len(); - start.1[sl - 1] |= N::masked_right(end.0 as u8, end.1[0]); + start.1[sl - 1] |= N::masked_right((N::NIBBLE_PER_BYTE - end.0) as u8, end.1[0]); 1 } else { 0 @@ -1351,7 +1350,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, LayoutNew, LayoutNewQuarter, BitMap16, BitMap}; + LayoutOri, LayoutNew, BitMap}; fn populate_trie<'db>( db: &'db mut HashDB, diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 8227cad2..b9e4e8d6 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -43,7 +43,12 @@ pub use hash_db::Hasher; pub trait TrieStream { fn new() -> Self; fn append_empty_data(&mut self); - fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator); + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator, + ); fn append_empty_child(&mut self) {} fn end_branch(&mut self, _value: Option<&[u8]>) {} fn append_leaf(&mut self, key: &[u8], value: &[u8]); @@ -124,7 +129,8 @@ fn trie_root_inner(input: I, no_ext: bool) -> H::Out where H::hash(&stream.out()) } -/// variant of `trie_root` without extension +/// Variant of `trie_root` for patricia trie without extension node. +/// See [`trie_root`]. pub fn trie_root_no_ext(input: I) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, @@ -135,9 +141,10 @@ pub fn trie_root_no_ext(input: I) -> H::Out where trie_root_inner::(input, true) } - - //#[cfg(test)] // consider feature="std" +/// Method similar to `trie_root` but returning the root encoded +/// node instead of its hash. +/// Mainly use for testing or debugging. pub fn unhashed_trie(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, @@ -182,6 +189,8 @@ fn unhashed_trie_inner(input: I, no_ext: bool) -> Vec where stream.out() } +/// Variant of `unhashed_trie` for patricia trie without extension node. +/// See [`unhashed_trie`]. pub fn unhashed_trie_no_ext(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, @@ -192,7 +201,6 @@ pub fn unhashed_trie_no_ext(input: I) -> Vec where unhashed_trie_inner::(input, true) } - /// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. /// /// ```rust @@ -228,7 +236,6 @@ pub fn sec_trie_root(input: I) -> H::Out where /// Takes a slice of key/value tuples where the key is a slice of nibbles /// and encodes it into the provided `Stream`. -// pub fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ext: bool) where A: AsRef<[u8]>, B: AsRef<[u8]>, @@ -304,7 +311,12 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex for &count in &shared_nibble_counts { if count > 0 { // println!("[build_trie] branch slot {}; recursing with cursor={}, begin={}, shared nibbles={}, input={:?}", i, cursor, begin, shared_nibble_count, &input[begin..(begin + shared_nibble_count)]); - build_trie_trampoline::(&input[begin..(begin + count)], cursor + 1, stream, no_ext); + build_trie_trampoline::( + &input[begin..(begin + count)], + cursor + 1, + stream, + no_ext, + ); begin += count; } else { stream.append_empty_child(); @@ -319,7 +331,12 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex } } -fn build_trie_trampoline(input: &[(A, B)], cursor: usize, stream: &mut S, no_ext: bool) where +fn build_trie_trampoline( + input: &[(A, B)], + cursor: usize, + stream: &mut S, + no_ext: bool, +) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, From 727837d5ca4f184c4109802325852d29ac5b95d3 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jun 2019 15:40:36 +0200 Subject: [PATCH 082/120] Documentation improvement and breaking long lines. --- trie-db/src/iter_build.rs | 51 ++++++++----- trie-db/src/lib.rs | 30 ++++---- trie-db/src/nibble/mod.rs | 1 + trie-db/src/nibble/nibbleslice.rs | 47 ++++++------ trie-db/src/nibble/nibblevec.rs | 37 +++++----- trie-db/src/node.rs | 30 ++++---- trie-db/src/node_codec.rs | 45 ++++++++---- trie-db/src/triedb.rs | 63 ++++++++++++---- trie-db/src/triedbmut.rs | 118 ++++++++++++++++++++++++------ 9 files changed, 286 insertions(+), 136 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 68ff189c..fffe27c3 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Alternative tools for working with key value iterator without recursion. +//! Alternative tools for working with key value ordered iterator without recursion. use hash_db::{Hasher, HashDB, Prefix}; use core_::marker::PhantomData; @@ -33,17 +33,19 @@ macro_rules! exp_disp { type CacheNode = Option>; -/// a builder for fix constant len cache, should match nibble ops `NIBBLE_LEN` +/// A builder for fix constant length cache, should match `NibbleOps` `NIBBLE_LEN`. pub trait CacheBuilder { - /// size of cache + /// Size of cache. const SIZE: usize; - /// the fix cache + /// The type of the cache. type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; - /// builder for cache + /// Create a new cache. fn new_vec_slice_buff() -> Self::AN; } +/// Cache builder for radix 16 trie. pub struct Cache16; +/// Cache builder for radix 4 trie. pub struct Cache4; impl CacheBuilder for Cache16 { @@ -63,13 +65,21 @@ impl CacheBuilder for Cache4 { exp_disp!(@2, [None]) } } + type ArrayNode = <::CB as CacheBuilder>>::AN; + // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index // second str is in branch value +/// Struct containing cache while iterating, can be at most the length of the lowest nibble. +/// +/// Note that it is not memory optimal (all depth are allocated even if some are empty due +/// to node partial). +/// Three field are used, a cache over the children, a boolean to indicate a change occured, +/// and an optional associated value. struct CacheAccum (Vec<(ArrayNode, bool, Option)>,PhantomData); -/// initially allocated cache +/// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; impl CacheAccum @@ -257,7 +267,9 @@ where } -/// visit trie +/// Function visiting trie from key value with a `ProccessEncodedNode`. +/// Calls to each node occurs ordered but with longest depth first (from node to +/// branch to root), this differs form key ordering a bit. pub fn trie_visit(input: I, cb_ext: &mut F) where T: TrieLayOut, @@ -315,12 +327,19 @@ pub fn trie_visit(input: I, cb_ext: &mut F) } } +/// Visitor trait to implement when using `trie_visit`. pub trait ProcessEncodedNode { + /// Function call with prefix, encoded value and a boolean indicating if the + /// node is the root for each node of the trie. + /// + /// Note that the returned value can be change depending on implementation, + /// but usually it should be the Hash of encoded node. This is returned for + /// optimisation purpose only (for builder hash_db does return this value). fn process(&mut self, encoded_prefix: Prefix, Vec, bool) -> ChildReference; } -/// Get trie root and insert node in hash db on parsing. -/// As for all `ProcessEncodedNode` implementation, it +/// Get trie root and insert visited node in a hash_db. +/// As for all `ProcessEncodedNode` implementation, it /// is only for full trie parsing (not existing trie). pub struct TrieBuilder<'a, H, HO, V, DB> { db: &'a mut DB, @@ -351,8 +370,9 @@ impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> f } } -/// Get trie root hash on parsing +/// Calculate the trie root of the trie. pub struct TrieRoot { + /// The resulting root. pub root: Option, _ph: PhantomData<(H)>, } @@ -380,16 +400,9 @@ impl ProcessEncodedNode<::Out> for TrieRoot this seems to match current implementation -/// of trie_root but I think it should return the -/// full stream of trie (which would not be doable -/// with current `ProcessEncodedNode` definition -/// but can be doable by switching to something -/// similar to `TrieStream` (initially the trait -/// was a simple FnMut but it make sense to move -/// to something more refined). +/// Get the trie root node encoding. pub struct TrieRootUnhashed { + /// The resulting encoded root. pub root: Option>, _ph: PhantomData<(H)>, } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index e3636b3f..886f2d9c 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -365,21 +365,30 @@ where pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } -/// Trait with definition of trie layout +/// Trait with definition of trie layout. +/// It contains all associated trait needed for +/// a trie definition. pub trait TrieLayOut { - /// does the trie use extension before its branch + /// If true, the trie will use extension nodes and + /// no partial in branch, if false the trie will only + /// use branch and node with partials in both. const USE_EXTENSION: bool; + /// Hasher to use for this trie. type H: Hasher; + /// Codec to use (need to match hasher and nibble ops) type C: NodeCodec; + /// Trie nibble constants. type N: NibbleOps; + /// Technical trait for cache, it should match the radix + /// of `NibbleOps`. type CB: CacheBuilder<::Out>; } /// Trait with operation on key value iterator. -/// This trait contains its own default implementation. -/// Implementing it allows to use alternate algorithm. +/// This trait contains its own default implementations +/// and exists only to allow alternate algorithm usage. pub trait TrieOps: Sized + TrieLayOut { - /// Operation to build a trie db from its ordered iterator of key value + /// Operation to build a trie db from its ordered iterator over its key/values. fn trie_build(db: &mut DB, input: I) -> ::Out where DB: HashDB, I: IntoIterator, @@ -400,7 +409,6 @@ pub trait TrieOps: Sized + TrieLayOut { trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - /// Determine a trie root node's data given its ordered contents, closed form. fn trie_root_unhashed(input: I) -> Vec where I: IntoIterator, @@ -411,7 +419,6 @@ pub trait TrieOps: Sized + TrieLayOut { trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - /// Encoding of index as a key (when reusing general trie for /// indexed trie). fn encode_index(input: u32) -> Vec { @@ -431,14 +438,11 @@ pub trait TrieOps: Sized + TrieLayOut { .map(|(i, v)| (Self::encode_index(i as u32), v)) ) } -/* /// Operation to build a trie from an unordered iterator of key value. - /// Operation to calculate a trie root from an ordered iterator of key value. - /// Operation to build a trie from an ordered iterator of key value.*/ } -/// alias to acces hasher hash output type from a `TrieLayout` +/// Alias accessor to hasher hash output type from a `TrieLayout`. pub type TrieHash = <::H as Hasher>::Out; -/// alias to acces bitmap codec from a `TrieLayout` +/// Alias accessor to bitmap codec from a `TrieLayout`. pub type BitMap = <::C as NodeCodec<::H, ::N>>::BM; -/// alias to acces `NodeCodec` `Error` type from a `TrieLayout` +/// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = <::C as NodeCodec<::H, ::N>>::Error; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 4849603c..70e55bd9 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -24,6 +24,7 @@ use super::MaybeDebug; // Workaround no constant function for pow. const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; + /// This trait contain Trie nibble specific definitions. /// This trait is mostly a collection of associated constant and some generic /// methods. diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 5981f2a5..224dedab 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -67,9 +67,9 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// Helper function to create a owned `NodeKey` from this `NibbleSlice`, - /// and for a given number of nibble. + /// and for a given number of nibble. /// Warning this method can be slow (number of nibble does not align the - /// original padding). + /// original padding). pub fn to_stored_range(&self, nb: usize) -> NodeKey { if nb >= self.len() { return self.to_stored() } if (self.offset + nb) % N::NIBBLE_PER_BYTE == 0 { @@ -143,7 +143,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// Return `Partial` representation of this slice: - /// first encoded byte and following slice. + /// first encoded byte and following slice. pub fn right(&'a self) -> Partial { let split = self.offset / N::NIBBLE_PER_BYTE; let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; @@ -154,7 +154,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } } - /// return encoded value as a packed byte iterator + /// Return an iterator over `Partial` bytes representation. pub fn right_iter(&'a self) -> impl Iterator + 'a { let (mut first, sl) = self.right(); let mut ix = 0; @@ -173,23 +173,8 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { }) } - /// return left of key nibble - pub fn left(&'a self) -> Prefix { - let split = self.offset / N::NIBBLE_PER_BYTE; - let ix = (self.offset % N::NIBBLE_PER_BYTE) as u8; - if ix == 0 { - (&self.data[..split], (0,0)) - } else { - (&self.data[..split], (ix, N::masked_left(ix, self.data[split]))) - } - } - pub fn left_owned(&'a self) -> (ElasticArray36, (u8,u8)) { - let (a, b) = self.left(); - (a.into(), b) - } - - - /// get iterator over slice, slow + /// Return `Partial` bytes iterator over a range of byte.. + /// Warning can be slow when unaligned (similar to `to_stored_range`). pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { let mut nib_res = to % N::NIBBLE_PER_BYTE; let aligned_i = (self.offset + to) % N::NIBBLE_PER_BYTE; @@ -226,9 +211,27 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { None } } - }) } + + /// Return left portion of `NibbleSlice`, if the slice + /// originates from a full key it will be the `Prefix of + /// the node`. + pub fn left(&'a self) -> Prefix { + let split = self.offset / N::NIBBLE_PER_BYTE; + let ix = (self.offset % N::NIBBLE_PER_BYTE) as u8; + if ix == 0 { + (&self.data[..split], (0,0)) + } else { + (&self.data[..split], (ix, N::masked_left(ix, self.data[split]))) + } + } + + /// Owned version of a `Prefix` from a `left` method call. + pub fn left_owned(&'a self) -> (ElasticArray36, (u8,u8)) { + let (a, b) = self.left(); + (a.into(), b) + } } impl<'a, N: NibbleOps> Into for NibbleSlice<'a, N> { diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 8260a924..8d18f90e 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -28,7 +28,7 @@ impl Default for NibbleVec { } impl NibbleVec { - /// Make a new `NibbleVec` + /// Make a new `NibbleVec`. pub fn new() -> Self { NibbleVec { inner: ElasticArray36::new(), @@ -37,11 +37,11 @@ impl NibbleVec { } } - /// Length of the `NibbleVec` + /// Length of the `NibbleVec`. #[inline(always)] pub fn len(&self) -> usize { self.len } - /// Retrurns true if `NibbleVec` has zero length + /// Retrurns true if `NibbleVec` has zero length. pub fn is_empty(&self) -> bool { self.len == 0 } /// Try to get the nibble at the given offset. @@ -79,14 +79,14 @@ impl NibbleVec { Some(N::at_left(i_new as u8, byte)) } - /// remove n last nibbles. - pub fn drop_lasts(&mut self, mov: usize) { - if mov == 0 { return; } - if mov >= self.len { + /// Remove then n last nibbles in a faster way than popping n times. + pub fn drop_lasts(&mut self, n: usize) { + if n == 0 { return; } + if n >= self.len { self.clear(); return; } - let end = self.len - mov; + let end = self.len - n; let end_ix = end / N::NIBBLE_PER_BYTE + if end % N::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; (end_ix..self.inner.len()).for_each(|_|{ self.inner.pop(); }); @@ -98,7 +98,7 @@ impl NibbleVec { } } - /// Get prefix from `NibbleVec` (when used as a prefix stack of nibble). + /// Get `Prefix` representation of this `NibbleVec`. pub fn as_prefix(&self) -> Prefix { let split = self.len / N::NIBBLE_PER_BYTE; let pos = (self.len % N::NIBBLE_PER_BYTE) as u8; @@ -109,7 +109,7 @@ impl NibbleVec { } } - /// push a full partial. + /// Append another `NibbleVec`. Can be slow (alignement of second vec). pub fn append(&mut self, v: &NibbleVec) { if v.len == 0 { return; } @@ -130,8 +130,7 @@ impl NibbleVec { self.len += v.len; } - - /// push a full partial. + /// Append a `Partial`. Can be slow (alignement of partial). pub fn append_partial(&mut self, (o_n, sl): Partial) { for i in (1..=o_n.0).rev() { let ix = N::NIBBLE_PER_BYTE - i as usize; @@ -154,11 +153,12 @@ impl NibbleVec { } /// Utility function for chaining two optional appending - /// of `NibbleSliec` and/or a byte. + /// of `NibbleSlice` and/or a byte. + /// Can be slow. pub(crate) fn append_slice_nibble( &mut self, o_sl: Option<&NibbleSlice>, - o_ix: Option + o_ix: Option, ) -> usize { let mut res = 0; if let Some(sl) = o_sl { @@ -172,17 +172,17 @@ impl NibbleVec { res } /// Utility function for `append_slice_nibble` after a clone. + /// Can be slow. pub(crate) fn clone_append_slice_nibble( &self, o_sl: Option<&NibbleSlice>, - o_ix: Option + o_ix: Option, ) -> Self { let mut p = self.clone(); p.append_slice_nibble(o_sl, o_ix); p } - /// Get the underlying byte slice. pub fn inner(&self) -> &[u8] { &self.inner[..] @@ -194,7 +194,7 @@ impl NibbleVec { self.len = 0; } - /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. + /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if there is no padding. pub fn as_nibbleslice(&self) -> Option> { if self.len % N::NIBBLE_PER_BYTE == 0 { Some(NibbleSlice::new(self.inner())) @@ -257,6 +257,7 @@ mod tests { append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((1,1), &[0x23, 0x12])); append_partial_inner::(&[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((3,0b111001), &[0x23, 0x12])); } + fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8,u8), &[u8])) { let mut resv = NibbleVec::::new(); res.iter().for_each(|r|resv.push(*r)); @@ -266,7 +267,6 @@ mod tests { assert_eq!(resv, initv); } - #[test] fn drop_lasts_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { @@ -290,5 +290,4 @@ mod tests { test_trun(&[1,2,3], 4, (&[], 0)); } - } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index af098416..db1e9764 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -22,10 +22,11 @@ use super::DBValue; use alloc::vec::Vec; /// Partial node key type: offset and owned value of a nibbleslice. -/// Offset is applied on first byte of array. +/// Offset is applied on first byte of array (bytes are right aligned). pub type NodeKey = (usize, ElasticArray36); -/// alias to branch children slice +/// Alias to branch children slice, it is equivalent to '&[&[u8]]'. +/// Reason for using it is https://github.com/rust-lang/rust/issues/43408. pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); /// Type of node in the trie and essential information thereof. @@ -38,13 +39,10 @@ pub enum Node<'a, N: NibbleOps> { Leaf(NibbleSlice<'a, N>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. Extension(NibbleSlice<'a, N>, &'a [u8]), - // TODO EMCH var length for children array is stuck behind https://github.com/rust-lang/rust/issues/43408 - // So we should also put it as associated type of N, but generic_associated_types will be needed - // for lifetime, so we should ultimately use something similar to struct `Branch` it decodes from - // a slice that is already aligned (need 2* bound in case there is some headers). - /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. + /// Branch node; has slice of child nodes (each possibly null) + /// and an optional immediate node data. Branch(BranchChildrenSlice<'a, N>, Option<&'a [u8]>), - /// Branch node with support for a nibble (to avoid extension node) + /// Branch node with support for a nibble (when extension nodes are not used). NibbledBranch(NibbleSlice<'a, N>, BranchChildrenSlice<'a, N>, Option<&'a [u8]>), } /// A Sparse (non mutable) owned vector struct to hold branch keys and value @@ -58,7 +56,10 @@ pub struct Branch { } impl Branch { - fn new(children_slice: &BranchChildrenSlice, maybe_value: Option<&[u8]>) -> Self { + fn new( + children_slice: &BranchChildrenSlice, + maybe_value: Option<&[u8]>, + ) -> Self { let mut data: Vec = children_slice.1.into(); let data_ix = data.len(); let ubounds_ix = data_ix + maybe_value.map(|v|{ @@ -73,7 +74,7 @@ impl Branch { Branch { data, data_ix, ubounds_ix, child_head: N::ChildSliceIx::CONTENT_HEADER_SIZE } } - /// Get the node value, if any + /// Get the node value, if any. pub fn get_value(&self) -> Option<&[u8]> { if self.has_value() { Some(&self.data[self.data_ix..self.ubounds_ix]) @@ -82,7 +83,7 @@ impl Branch { } } - /// Test if the node has a value + /// Test if the node has a value. pub fn has_value(&self) -> bool { self.data_ix < self.ubounds_ix } @@ -99,6 +100,8 @@ impl Branch { self.data[s..e].try_into().ok().map(usize::from_ne_bytes) } } + + /// Get the children encoded value at index, if any. pub fn index(&self, index: usize) -> Option<&[u8]> { let b = (self.index_bound(index), self.index_bound(index + 1)); if let (Some(s), Some(e)) = b { @@ -121,10 +124,9 @@ pub enum OwnedNode { Leaf(NibbleVec, DBValue), /// Extension node: partial key and child node. Extension(NibbleVec, DBValue), - /// Branch node: 16 children and an optional value. + /// Branch node: children and an optional value. Branch(Branch), - /// Branch node: 16 children and an optional value. - /// TODO can put nibble vec into branch data vec + /// Branch node: children and an optional value. NibbledBranch(NibbleVec, Branch), } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 96143492..e3c2a4fd 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -39,7 +39,7 @@ pub trait Error {} impl Error for T {} /// Immutable representation of a nible slice (right aligned). -/// It contains a right aligned padded first byte (first pair element is the number of nibble +/// It contains a right aligned padded first byte (first pair element is the number of nibbles /// (0 to max nb nibble - 1), second pair element is the padded nibble), and a slice over /// the remaining bytes. pub type Partial<'a> = ((u8,u8), &'a[u8]); @@ -50,8 +50,9 @@ pub type Partial<'a> = ((u8,u8), &'a[u8]); pub trait NodeCodec: Sized { /// Codec error type type Error: Error; - /// child bitmap codec to use TODO EMCH nodecodec does not have to use a bitmap codec: this should be - /// only in codec implementation, having it as a subtype seems useless + /// child bitmap codec to use + /// TODO EMCH nodecodec does not have to use a bitmap codec: this should be + /// only in codec implementation, having it as a subtype seems useless type BM: ChildBitmap; /// Get the hashed null node. @@ -66,31 +67,48 @@ pub trait NodeCodec: Sized { /// Check if the provided bytes correspond to the codecs "empty" node. fn is_empty_node(data: &[u8]) -> bool; - /// Returns an empty node + /// Returns an encoded empty node. fn empty_node() -> &'static [u8]; /// Returns an encoded leaf node fn leaf_node(partial: Partial, value: &[u8]) -> Vec; /// Returns an encoded extension node - fn ext_node(partial: impl Iterator, nb_nibble: usize, child_ref: ChildReference) -> Vec; - - /// Returns an encoded branch node. Takes an iterator yielding `ChildReference` and an optional value - fn branch_node(children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; + /// Note that nb_nibble is the number of element of the iterator + /// it can possibly be obtain by `Iterator` `size_hint`, but + /// for simplicity it is used directly as a parameter. + fn ext_node( + partial: impl Iterator, + nb_nibble: usize, + child_ref: ChildReference, + ) -> Vec; + + /// Returns an encoded branch node. + /// Takes an iterator yielding `ChildReference` and an optional value. + fn branch_node( + children: impl Iterator>>>, + value: Option<&[u8]>, + ) -> Vec; /// Returns an encoded branch node with a possible partial path. - fn branch_node_nibbled(partial: impl Iterator, nb_nibble: usize, children: impl Iterator>>>, value: Option<&[u8]>) -> Vec; + /// `nb_nibble` is the partial path lenghth as in `ext_node`. + fn branch_node_nibbled( + partial: impl Iterator, + nb_nibble: usize, + children: impl Iterator>>>, + value: Option<&[u8]> + ) -> Vec; } -/// extract child bitmap encoding that is related to the trie number of children -/// This would be useless with https://github.com/rust-lang/rust/issues/43408 +/// Child bitmap encoder that is related to the trie number of children. +/// This would be useless with https://github.com/rust-lang/rust/issues/43408. pub trait ChildBitmap: Sized { /// length to encode the bitmap const ENCODED_LEN: usize; - /// Codec error type + /// Codec error type. type Error: Error; - /// Codec buf to use + /// Codec buffer to use. type Buff: AsRef<[u8]> + AsMut<[u8]> + Default; /// decode bitmap @@ -101,5 +119,4 @@ pub trait ChildBitmap: Sized { /// encode to dest of right len fn encode>(has_children: I , dest: &mut [u8]); - } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 7a1feed4..c2c1eb72 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -78,7 +78,10 @@ where { /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist - pub fn new(db: &'db HashDBRef, root: &'db TrieHash) -> Result, CError> { + pub fn new( + db: &'db HashDBRef, + root: &'db TrieHash + ) -> Result, CError> { if !db.contains(root, EMPTY_PREFIX) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { @@ -101,7 +104,10 @@ where /// may require a database lookup. If `is_root_data` then this is root-data and /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. - fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: Prefix) -> Result, TrieHash, CError> { + fn get_raw_or_lookup( + &'db self, node: &[u8], + partial_key: Prefix, + ) -> Result, TrieHash, CError> { match (partial_key.0.is_empty() && (partial_key.1).0 == 0, L::C::try_decode_hash(node)) { (false, Some(key)) => { self.db @@ -120,7 +126,11 @@ where { fn root(&self) -> &TrieHash { self.root } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> + fn get_with<'a, 'key, Q: Query>( + &'a self, + key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> where 'a: 'key, { Lookup:: { @@ -130,7 +140,11 @@ where }.look_up(NibbleSlice::new(key)) } - fn iter<'a>(&'a self) -> Result, CError>> + 'a>, TrieHash, CError> { + fn iter<'a>(&'a self)-> Result< + Box, CError>> + 'a>, + TrieHash, + CError, + > { TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) } } @@ -219,7 +233,7 @@ where Err(e) => f.debug_struct("BROKEN_NODE") .field("index", &self.index) - .field("key", &self.node_key) // [128, 225, 183, 218, 100, 173, 146, 231, 107, 158, 188, 21], + .field("key", &self.node_key) .field("error", &format!("ERROR decoding node branch Rlp: {}", e)) .finish() } @@ -300,7 +314,11 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { Ok(r) } - fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key, L::N>) -> Result<(), TrieHash, CError> { + fn seek<'key>( + &mut self, + node_data: &DBValue, + key: NibbleSlice<'key, L::N>, + ) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; let mut full_key_nibbles = 0; @@ -395,7 +413,6 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { return Ok(()) } } - }, Node::Empty => return Ok(()), } @@ -482,7 +499,9 @@ impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { return Some(Ok((self.key().right().1.into(), v.clone()))); }, (Status::At, &OwnedNode::Extension(_, ref d)) => { - IterStep::Descend::, CError>(self.db.get_raw_or_lookup(&*d, self.key_nibbles.as_prefix())) + IterStep::Descend::, CError>( + self.db.get_raw_or_lookup(&*d, self.key_nibbles.as_prefix()) + ) }, (Status::At, &OwnedNode::Branch(_)) | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, @@ -638,12 +657,21 @@ mod tests { let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); - assert_eq!(iter.next().unwrap().unwrap(), (hex!("0103000000000000000464").to_vec(), DBValue::from_slice(&hex!("fffffffffe")[..]))); + assert_eq!( + iter.next().unwrap().unwrap(), + (hex!("0103000000000000000464").to_vec(), DBValue::from_slice(&hex!("fffffffffe")[..])) + ); iter.seek(&hex!("00")[..]).unwrap(); - assert_eq!(pairs, iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()); + assert_eq!( + pairs, + iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>(), + ); let mut iter = t.iter().unwrap(); iter.seek(&hex!("0103000000000000000465")[..]).unwrap(); - assert_eq!(&pairs[1..], &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..]); + assert_eq!( + &pairs[1..], + &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..], + ); } @@ -667,7 +695,12 @@ mod tests { #[test] fn iterator_no_ext() { - let d = vec![DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B")]; + 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::, DBValue>::default(); let mut root = Default::default(); @@ -679,7 +712,10 @@ mod tests { } let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); + assert_eq!( + d.iter().map(|i| i.clone().into_vec()).collect::>(), + t.iter().unwrap().map(|x| x.unwrap().0).collect::>(), + ); assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } @@ -757,7 +793,6 @@ mod tests { assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); } - #[test] fn debug_output_supports_pretty_print() { let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 3a7fb2e8..18da52dd 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -116,8 +116,13 @@ where } // decode a node from encoded bytes without getting its children. - fn from_encoded<'a, 'b, C, H, N>(data: &'a[u8], db: &HashDB, storage: &'b mut NodeStorage) -> Self - where N: NibbleOps, C: NodeCodec, H: Hasher, + fn from_encoded<'a, 'b, C, H, N>( + data: &'a[u8], + db: &HashDB, + storage: &'b mut NodeStorage, + ) -> Self + where + N: NibbleOps, C: NodeCodec, H: Hasher, { let dec_children = |encoded_children: IterChildSliceIx, storage: &'b mut NodeStorage| { let mut res = Vec::with_capacity(N::ChildSliceIx::NIBBLE_LEN); @@ -369,7 +374,10 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'a mut HashDB, root: &'a mut TrieHash) -> Result, CError> { + pub fn from_existing( + db: &'a mut HashDB, + root: &'a mut TrieHash, + ) -> Result, CError> { if !db.contains(root, EMPTY_PREFIX) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } @@ -395,8 +403,13 @@ where } // cache a node by hash - fn cache(&mut self, hash: TrieHash, key: Prefix) -> Result, CError> { - let node_encoded = self.db.get(&hash, key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; + fn cache( + &mut self, + hash: TrieHash, + key: Prefix, + ) -> Result, CError> { + let node_encoded = self.db.get(&hash, key) + .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; let node = Node::from_encoded::( &node_encoded, &*self.db, @@ -407,8 +420,19 @@ where // inspect a node, choosing either to replace, restore, or delete it. // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored>, key: &mut NibbleFullKey, inspector: F) -> Result>, bool)>, TrieHash, CError> - where F: FnOnce(&mut Self, Node>, &mut NibbleFullKey) -> Result>, TrieHash, CError> { + fn inspect( + &mut self, + stored: Stored>, + key: &mut NibbleFullKey, + inspector: F, + ) -> Result>, bool)>, TrieHash, CError> + where + F: FnOnce( + &mut Self, + Node>, + &mut NibbleFullKey, + ) -> Result>, TrieHash, CError>, + { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -430,7 +454,11 @@ where } // walk the trie, attempting to find the key's node. - fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key, L::N>, handle: &NodeHandle>) -> Result, TrieHash, CError> + fn lookup<'x, 'key>( + &'x self, + mut partial: NibbleSlice<'key, L::N>, + handle: &NodeHandle>, + ) -> Result, TrieHash, CError> where 'x: 'key { let mut handle = handle; @@ -492,7 +520,13 @@ where } /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), TrieHash, CError> { + fn insert_at( + &mut self, + handle: NodeHandle>, + key: &mut NibbleFullKey, + value: DBValue, + old_val: &mut Option, + ) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h, key.left())?, @@ -506,7 +540,13 @@ where } /// the insertion inspector. - fn insert_inspector(&mut self, node: Node>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn insert_inspector( + &mut self, + node: Node>, + key: &mut NibbleFullKey, + value: DBValue, + old_val: &mut Option, + ) -> Result>, TrieHash, CError> { let partial = key.clone(); #[cfg(feature = "std")] @@ -571,8 +611,15 @@ where } else if cp < existing_key.len() { // insert a branch value in between #[cfg(feature = "std")] - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); - let low = Node::NibbledBranch(existing_key.mid(cp + 1).to_stored(), children, stored_value); + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", + existing_key.len(), + partial.len(), + cp, + ); + let nbranch_partial = existing_key.mid(cp + 1).to_stored(); + let low = Node::NibbledBranch(nbranch_partial, children, stored_value); let ix = existing_key.at(cp); let mut children = empty_children::<_, L::N>(); let alloc_storage = self.storage.alloc(Stored::New(low)); @@ -589,7 +636,8 @@ where ) } else { let ix = partial.at(cp); - let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.mid(cp + 1).to_stored(), value))); + let stored_leaf = Node::Leaf(partial.mid(cp + 1).to_stored(), value); + let leaf = self.storage.alloc(Stored::New(stored_leaf)); children[ix as usize] = Some(leaf.into()); InsertAction::Replace(Node::NibbledBranch( @@ -613,7 +661,8 @@ where children[idx] = Some(new_child.into()); if !changed { // the new node we composed didn't change. that means our branch is untouched too. - return Ok(InsertAction::Restore(Node::NibbledBranch(existing_key.to_stored(), children, stored_value))); + let n_branch = Node::NibbledBranch(existing_key.to_stored(), children, stored_value); + return Ok(InsertAction::Restore(n_branch)); } } else { // original had nothing there. compose a leaf. @@ -645,7 +694,12 @@ where } else if (L::USE_EXTENSION && cp == 0) || (!L::USE_EXTENSION && cp < existing_key.len()) { #[cfg(feature = "std")] - trace!(target: "trie", "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + trace!( + target: "trie", + "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", + existing_key.len(), + partial.len(), + ); // one of us isn't empty: transmute to branch here let mut children = empty_children::<_, L::N>(); @@ -673,7 +727,11 @@ where // fully-shared prefix for an extension. // make a stub branch - let branch = Node::NibbledBranch(existing_key.to_stored(), empty_children::<_, L::N>(), Some(stored_value)); + let branch = Node::NibbledBranch( + existing_key.to_stored(), + empty_children::<_, L::N>(), + Some(stored_value), + ); // augment the new branch. let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -793,7 +851,12 @@ where } /// the removal inspector - fn remove_inspector(&mut self, node: Node>, key: &mut NibbleFullKey, old_val: &mut Option) -> Result>, TrieHash, CError> { + fn remove_inspector( + &mut self, + node: Node>, + key: &mut NibbleFullKey, + old_val: &mut Option, + ) -> Result>, TrieHash, CError> { let partial = key.clone(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, @@ -901,7 +964,12 @@ where } else { // leaf the node alone. #[cfg(feature = "std")] - trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::::from_stored(&encoded)); + trace!( + target: "trie", + "restoring leaf wrong partial, partial={:?}, existing={:?}", + partial, + NibbleSlice::::from_stored(&encoded), + ); Action::Restore(Node::Leaf(encoded, value)) } }, @@ -947,7 +1015,11 @@ where /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node>, key: NibbleSlice) -> Result>, TrieHash, CError> { + fn fix( + &mut self, + node: Node>, + key: NibbleSlice, + ) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1197,7 +1269,11 @@ where /// case where we can fit the actual data in the `Hasher`s output type, we /// store the data inline. This function is used as the callback to the /// `into_encoded` method of `Node`. - fn commit_child(&mut self, handle: NodeHandle>, prefix: &mut NibbleVec) -> ChildReference> { + fn commit_child( + &mut self, + handle: NodeHandle>, + prefix: &mut NibbleVec, + ) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), NodeHandle::InMemory(storage_handle) => { @@ -1428,6 +1504,7 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie(&mut memtrie, &x); memtrie.commit(); + // TODO EMCH hashed null node helper let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); @@ -1484,7 +1561,6 @@ mod tests { } } - #[test] fn init() { let mut memdb = MemoryDB::, DBValue>::default(); From 8d1b2ae6d73e903fe7336149ed18534f2ccba13b Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jun 2019 16:45:51 +0200 Subject: [PATCH 083/120] some additional formatting --- test-support/reference-trie/src/lib.rs | 1 - trie-db/src/nibble/mod.rs | 25 ++++++++++++------------- trie-db/src/triedbmut.rs | 15 +++++++++------ 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 5ff0c098..906807d6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1213,7 +1213,6 @@ pub fn compare_no_ext_insert_remove( assert_eq!(*t.root(), calc_root_no_ext(data2)); } -// TODO EMCH to big is currently truncate, keep it that way?? wait for final spec #[test] fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 70e55bd9..713063d4 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -45,8 +45,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// The first element of each pair is a bit mask to apply, /// the second element is a right shift to apply in some case. /// const PADDING_BITMASK: &'static [(u8, usize)] = &[ - /// Similar to following const function TODO EMCH switch to two - /// const function (will make code simpler). + /// Similar to following const function. /// ```rust /// const BIT_PER_NIBBLE: usize = 4; /// const fn padding_bitmask(ix: usize) -> (u8, usize) { @@ -59,28 +58,28 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Last nibble index as u8, just a convenience constant for iteration on all nibble. const LAST_N_IX_U8: u8 = (Self::NIBBLE_PER_BYTE - 1) as u8; - /// Buffer type for slice index store (we do not include + /// Buffer type for slice index store (we do not include /// directly slice in it to avoid lifetime in /// trait type ChildSliceIx: ChildSliceIx; /// Mask a byte from a `ix` > 0 (ix being content). - /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. + /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. #[inline(always)] fn masked_left(ix: u8, b: u8) -> u8 { debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } /// Mask a byte from a ix > 0 (ix being content) - /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. + /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. #[inline(always)] fn masked_right(ix: u8, b: u8) -> u8 { if ix > 0 { - b & Self::PADDING_BITMASK[Self::NIBBLE_PER_BYTE - ix as usize].0 - } else { - b - } + b & Self::PADDING_BITMASK[Self::NIBBLE_PER_BYTE - ix as usize].0 + } else { + b + } } /// Get u8 nibble value at a given index of a byte. #[inline(always)] @@ -119,7 +118,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } /// Calculate the array nibble shifts needed - /// for alignment a given unaligned padding (pad != 0). + /// for alignment a given unaligned padding (pad != 0). #[inline(always)] fn split_shifts(pad: usize) -> (usize, usize) { debug_assert!(pad > 0); @@ -155,7 +154,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } /// Shifts right aligned key to add a given left offset. - /// Resulting in possibly padding at both left and right + /// Resulting in possibly padding at both left and right /// (example usage when combining two keys). fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { let old_offset = key.0; @@ -282,7 +281,7 @@ pub trait ChildSliceIx: AsRef<[usize]> /// Constant length for the number of children. const NIBBLE_LEN : usize; /// Constant size of header - /// Should only be use for inner implementation. + /// Should only be use for inner implementation. const CONTENT_HEADER_SIZE: usize; /// Access a children slice at a given index. @@ -321,7 +320,7 @@ impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { macro_rules! child_slice_ix { ($me: ident, $size: expr, $pre: expr) => { - #[cfg_attr(feature = "std", derive(Debug))] + #[cfg_attr(feature = "std", derive(Debug))] #[derive(Default, Eq, PartialEq, Clone)] /// Child slice indexes for radix $size. pub struct $me([usize; $size + 1]); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 18da52dd..670d67e1 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1426,7 +1426,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, LayoutNew, BitMap}; + LayoutOri, BitMap}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1470,6 +1470,10 @@ mod tests { } } + fn reference_hashed_null_node() -> ::Out { + > + as NodeCodec<_, ::N>>::hashed_null_node() + } #[test] fn playpen() { @@ -1504,8 +1508,7 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie(&mut memtrie, &x); memtrie.commit(); - // TODO EMCH hashed null node helper - let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); + let hashed_null_node = reference_hashed_null_node(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); @@ -1548,7 +1551,7 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie_no_ext(&mut memtrie, &x); memtrie.commit(); - let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); + let hashed_null_node = reference_hashed_null_node(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); @@ -1566,7 +1569,7 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); + let hashed_null_node = reference_hashed_null_node(); assert_eq!(*t.root(), hashed_null_node); } @@ -1842,7 +1845,7 @@ mod tests { } assert!(t.is_empty()); - let hashed_null_node = > as NodeCodec<_, ::N>>::hashed_null_node(); + let hashed_null_node = reference_hashed_null_node(); assert_eq!(*t.root(), hashed_null_node); } From a5a95bd565309ccdde953b5205b274ed6bdb2880 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jun 2019 16:49:55 +0200 Subject: [PATCH 084/120] Remove BM from NodeCodec (specific to some implementation only). --- test-support/reference-trie/src/lib.rs | 4 +--- trie-db/src/lib.rs | 2 -- trie-db/src/node_codec.rs | 4 ---- trie-db/src/triedbmut.rs | 6 +++--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 906807d6..81e3bcb3 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -41,7 +41,7 @@ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, ChildBitmap, - BitMap, ChildSliceIx}; + ChildSliceIx}; pub use trie_db::{Record, TrieLayOut, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; @@ -663,7 +663,6 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< // do `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. Perhaps one day soon? impl> NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; - type BM = BM; fn hashed_null_node() -> ::Out { KeccakHasher::hash(>::empty_node()) @@ -787,7 +786,6 @@ impl> NodeCodec> NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; - type BM = BM; fn hashed_null_node() -> ::Out { KeccakHasher::hash(>::empty_node()) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 886f2d9c..634087da 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -442,7 +442,5 @@ pub trait TrieOps: Sized + TrieLayOut { /// Alias accessor to hasher hash output type from a `TrieLayout`. pub type TrieHash = <::H as Hasher>::Out; -/// Alias accessor to bitmap codec from a `TrieLayout`. -pub type BitMap = <::C as NodeCodec<::H, ::N>>::BM; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = <::C as NodeCodec<::H, ::N>>::Error; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index e3c2a4fd..34e65506 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -50,10 +50,6 @@ pub type Partial<'a> = ((u8,u8), &'a[u8]); pub trait NodeCodec: Sized { /// Codec error type type Error: Error; - /// child bitmap codec to use - /// TODO EMCH nodecodec does not have to use a bitmap codec: this should be - /// only in codec implementation, having it as a subtype seems useless - type BM: ChildBitmap; /// Get the hashed null node. fn hashed_null_node() -> H::Out; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 670d67e1..73455477 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1425,8 +1425,8 @@ mod tests { use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, - ReferenceNodeCodec, ReferenceNodeCodecNoExt, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, BitMap}; + ReferenceNodeCodec, ref_trie_root, ref_trie_root_no_ext, + LayoutOri, BitMap16}; fn populate_trie<'db>( db: &'db mut HashDB, @@ -1471,7 +1471,7 @@ mod tests { } fn reference_hashed_null_node() -> ::Out { - > + as NodeCodec<_, ::N>>::hashed_null_node() } From be66ce2c17dae7f0101f269b2d79f215f5e10395 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 6 Jun 2019 15:24:39 +0200 Subject: [PATCH 085/120] Invalid asumption about prefix ordering of key, fix doc and implement an ordered prefix appending (non optimal in size). --- memory-db/src/lib.rs | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index b95fd4be..3fb6f92e 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -161,7 +161,10 @@ pub trait KeyFunction { /// Make database key from hash and prefix. -/// This is byte ordered similarilly to the original trie key. +/// +/// Prefix could be ordered, but key hash appending will break this property. +/// If true ordering is needed we would need a fix length encoding for prefix +/// (so a maximum length and additional byte (or chunk of prefix encoded)). pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); prefixed_key.extend_from_slice(prefix.0); @@ -173,6 +176,109 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { prefixed_key } +#[test] +fn test_ordered_prefixed_key() { + use keccak_hasher::KeccakHasher; + struct Opt; + impl IntoIterator for Opt { + type Item = usize; + type IntoIter = std::iter::Repeat; + fn into_iter(self) -> Self::IntoIter { + std::iter::repeat(1) + } + } + impl OrderedPrefixedKeyChunk for Opt { } + let prefixes = vec![ + (vec![],(0u8, 0x00u8), [1u8;32]), + (vec![],(1u8, 0x00u8), [1u8;32]), + (vec![0u8],(0,0), [0;32]), + (vec![],(1u8, 0x10u8), [1u8;32]), + (vec![0x10u8],(0,0), [0;32]), + (vec![0x11u8],(0,0), [0;32]), + ]; + let oc: std::collections::BTreeSet<_> = prefixes.iter().map(|(p1, p2, k)| { + ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone()), Opt) + }).collect(); + + for ((p1, p2, k),b) in prefixes.iter().zip(oc.iter()) { + assert_eq!( + &ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone()), Opt), + b, + ); + } +} +/// Parameterize the size of successive chunks when using +/// `ordered_prefixed_key`. +/// Length of each chunk is minimum 1, maximum 254. +pub trait OrderedPrefixedKeyChunk: IntoIterator { + /// Estimate a final length given a input length. + fn estimate_enc_len(input: usize) -> usize { input } +} + +/// Make database key from hash and prefix. +/// +/// The scheme allows byte ordering of key depending on prefix (can iterate +/// trie with it). +/// Every chunk we add a 2 byte (could be reduce to one if storing directly a +/// total number of chunks), the number of written bytes (last prefix padded one included), and +/// the number of bytes in the prefix padding byte (note that for aligned prefix we add a +/// theorically useless 0 to keep a simple trait). +pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix, chunksiter: C) -> Vec + where + H: KeyHasher, + C: OrderedPrefixedKeyChunk, + { + const REM_CHUNKS: u8 = 255; + const FULL_NIBBLES: u8 = 255; + let mut prefixed_key = Vec::with_capacity( + key.as_ref().len() + C::estimate_enc_len(prefix.0.len() + 2) + 1 + ); + let mut to_write = &prefix.0[..]; + let mut rem = 0; + for chunk_len in chunksiter { + if chunk_len >= to_write.len() { + prefixed_key.extend_from_slice(&to_write[..]); + match chunk_len - to_write.len() { + 0 => { + rem = chunk_len; + prefixed_key.push(REM_CHUNKS); + prefixed_key.push(FULL_NIBBLES); + }, + 1 => { + prefixed_key.push((prefix.1).1); + prefixed_key.push(to_write.len() as u8 + 1); + prefixed_key.push(REM_CHUNKS); + prefixed_key.push((prefix.1).0); + }, + a => { + let pl = prefixed_key.len(); + prefixed_key.push((prefix.1).1); + prefixed_key.resize(pl + a, 0); + prefixed_key.push(to_write.len() as u8 + 1); + prefixed_key.push((prefix.1).0); + }, + } + break; + } + + prefixed_key.extend_from_slice(&to_write[..chunk_len]); + to_write = &to_write[chunk_len..]; + prefixed_key.push(REM_CHUNKS); + prefixed_key.push(FULL_NIBBLES); + } + if rem > 0 { + let pl = prefixed_key.len(); + prefixed_key.push((prefix.1).1); + prefixed_key.resize(pl + rem, 0); + prefixed_key.push(1); + prefixed_key.push((prefix.1).0); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + + + /// Legacy method for db using previous version of prefix encoding. /// Only for trie radix 16. pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { From ddd24d2b589b2fb060e2f46daf794acdeb238717 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 7 Jun 2019 21:44:45 +0200 Subject: [PATCH 086/120] OrderedPrefix instance. --- memory-db/src/lib.rs | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 3fb6f92e..f291551d 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -179,6 +179,7 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { #[test] fn test_ordered_prefixed_key() { use keccak_hasher::KeccakHasher; + #[derive(Debug, Clone, Default)] struct Opt; impl IntoIterator for Opt { type Item = usize; @@ -197,12 +198,12 @@ fn test_ordered_prefixed_key() { (vec![0x11u8],(0,0), [0;32]), ]; let oc: std::collections::BTreeSet<_> = prefixes.iter().map(|(p1, p2, k)| { - ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone()), Opt) + ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone())) }).collect(); for ((p1, p2, k),b) in prefixes.iter().zip(oc.iter()) { assert_eq!( - &ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone()), Opt), + &ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone())), b, ); } @@ -210,7 +211,7 @@ fn test_ordered_prefixed_key() { /// Parameterize the size of successive chunks when using /// `ordered_prefixed_key`. /// Length of each chunk is minimum 1, maximum 254. -pub trait OrderedPrefixedKeyChunk: IntoIterator { +pub trait OrderedPrefixedKeyChunk: IntoIterator + Clone + MaybeDebug + Default { /// Estimate a final length given a input length. fn estimate_enc_len(input: usize) -> usize { input } } @@ -223,7 +224,7 @@ pub trait OrderedPrefixedKeyChunk: IntoIterator { /// total number of chunks), the number of written bytes (last prefix padded one included), and /// the number of bytes in the prefix padding byte (note that for aligned prefix we add a /// theorically useless 0 to keep a simple trait). -pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix, chunksiter: C) -> Vec +pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec where H: KeyHasher, C: OrderedPrefixedKeyChunk, @@ -235,7 +236,7 @@ pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix, chunksiter: C) - ); let mut to_write = &prefix.0[..]; let mut rem = 0; - for chunk_len in chunksiter { + for chunk_len in C::default() { if chunk_len >= to_write.len() { prefixed_key.extend_from_slice(&to_write[..]); match chunk_len - to_write.len() { @@ -327,6 +328,29 @@ impl KeyFunction for PrefixedKey { } } +#[derive(Clone,Debug)] +/// Key function that concatenates prefix and hash. +/// The resulting key byte is ordered by prefix byte value +/// (in case of a trie prefix iteration over the prefixed +/// key should give the same order as the trie). +/// Also note that this scheme allows to retrieve the prefix +/// original value. +pub struct OrderedPrefixedKey(C, PhantomData); + +impl KeyFunction for OrderedPrefixedKey + where + H: KeyHasher, + C: OrderedPrefixedKeyChunk, +{ + type Key = Vec; + + fn key(hash: &H::Out, prefix: Prefix) -> Vec { + ordered_prefixed_key::(hash, prefix) + } +} + + + #[derive(Clone,Debug)] /// Key function that concatenates prefix and hash. /// This is doing useless computation and should only be From fec4a79196f4fdad622053637c2b399db29fb444 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sun, 9 Jun 2019 13:44:51 +0200 Subject: [PATCH 087/120] inprogress --- trie-db/src/iter_build.rs | 125 ++++++++++++++++++++++++++++---------- trie-db/src/nibble/mod.rs | 2 +- trie-db/src/triedb.rs | 33 +++++----- 3 files changed, 112 insertions(+), 48 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index fffe27c3..a20dfbb3 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -77,7 +77,7 @@ type ArrayNode = <::CB as CacheBuilder>>::AN; /// to node partial). /// Three field are used, a cache over the children, a boolean to indicate a change occured, /// and an optional associated value. -struct CacheAccum (Vec<(ArrayNode, bool, Option)>,PhantomData); +struct CacheAccum (Vec<(ArrayNode, bool, Option, usize)>,PhantomData); /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; @@ -90,50 +90,91 @@ where fn new() -> Self { let mut v = Vec::with_capacity(INITIAL_DEPTH); - (0..INITIAL_DEPTH).for_each(|_| - v.push((T::CB::new_vec_slice_buff(), false, None))); + v.push((T::CB::new_vec_slice_buff(), false, None, 0)); +/* (0..INITIAL_DEPTH).for_each(|_| + v.push((T::CB::new_vec_slice_buff(), false, None)));*/ CacheAccum(v, PhantomData) } #[inline(always)] fn set_elt(&mut self, depth:usize, sl: Option) { - if depth >= self.0.len() { - for _i in self.0.len()..depth + 1 { - self.0.push((T::CB::new_vec_slice_buff(), false, None)); - } - } - self.0[depth].2 = sl; - self.0[depth].1 = true; + let mut last = self.0.len() - 1; + if self.0[last].3 < depth { + self.0.push((T::CB::new_vec_slice_buff(), false, None, depth)); + last += 1; + } + if self.0[last].3 > depth { + assert!(false); + } + self.0[last].2 = sl; + self.0[last].1 = true; } #[inline(always)] fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode>) { - if depth >= self.0.len() { - for _i in self.0.len()..depth + 1 { - self.0.push((T::CB::new_vec_slice_buff(), false, None)); - } - } - self.0[depth].0.as_mut()[nibble_ix] = node; - self.0[depth].1 = true; + let mut last = self.0.len() - 1; + if self.0[last].3 < depth { + self.0.push((T::CB::new_vec_slice_buff(), false, None, depth)); + last += 1; + } + assert_eq!(self.0[last].3, depth); + + self.0[last].0.as_mut()[nibble_ix] = node; + self.0[last].1 = true; + } + + #[inline(always)] + fn last_depth(&self) -> usize { + let ix = self.0.len(); + if ix > 0 { + let last = ix - 1; + self.0[last].3 + } else { + 0 + } } + #[inline(always)] + fn last_last_depth(&self) -> usize { + let ix = self.0.len(); + if ix > 1 { + let last = ix - 2; + self.0[last].3 + } else { + 0 + } + } + #[inline(always)] fn touched(&self, depth:usize) -> bool { - self.0[depth].1 + let mut res = false; + let mut ix = self.0.len(); + while ix > 0 { + + + let last = ix - 1; +// TODO rec does not make sense : rewrite the flush to access directly parent ix instead +// of minus 1 loop... + res = self.0[last].1 && self.0[last].3 == depth; + if res { return res } + ix = last; + } + res } #[inline(always)] fn reset_depth(&mut self, depth:usize) { - self.0[depth].1 = false; - for i in 0..T::N::NIBBLE_LEN { - self.0[depth].0.as_mut()[i] = None; - } + + let last = self.0.len() - 1; + assert_eq!(self.0[last].3, depth); + + self.0.pop(); } fn flush_val ( &mut self, cb_ext: &mut impl ProcessEncodedNode>, - target_depth: usize, + target_depth: usize, (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), ) { let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); @@ -152,13 +193,22 @@ where no_ext: bool, cb_ext: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, - new_depth: usize, + new_depth: usize, old_depth: usize, is_last: bool, ) { let mut last_branch_ix = None; for d in (new_depth..=old_depth).rev() { +/* let lix = self.last_depth(); + if lix == d { + let llix = self.last_last_depth(); + let is_last = llix == lix; + // root + } else { + } + }*/ + let touched = self.touched(d); if touched || d == new_depth { @@ -222,10 +272,13 @@ where is_root: bool, nkey: Option<(usize, usize)>, ) -> ChildReference> { + let last = self.0.len() - 1; + assert_eq!(self.0[last].3, branch_d); // enc branch - let v = self.0[branch_d].2.take(); - let encoded = T::C::branch_node(self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); + let v = self.0[last].2.take(); + let encoded = T::C::branch_node(self.0[last].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); + // TODO branch_d == 0 means is _root ??!! self.reset_depth(branch_d); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); @@ -250,15 +303,19 @@ where is_root: bool, nkey: Option<(usize, usize)>, ) -> ChildReference> { + let last = self.0.len() - 1; + if self.0[last].3 != branch_d { + assert!(false); + } // enc branch - let v = self.0[branch_d].2.take(); + let v = self.0[last].2.take(); let nkeyix = nkey.unwrap_or((0,0)); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); let encoded = T::C::branch_node_nibbled( // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); pr.right_range_iter(nkeyix.1), nkeyix.1, - self.0[branch_d].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); + self.0[last].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); self.reset_depth(branch_d); let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); @@ -517,7 +574,7 @@ mod test { compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); compare_impl_no_ext_pk(data.clone()); - compare_impl_no_ext_q(data.clone()); + //compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { @@ -686,11 +743,17 @@ mod test { #[test] fn fuzz_noext3 () { - compare_impl_no_ext_unordered(vec![ + compare_impl(vec![ + (vec![0],vec![0, 0]), + (vec![11,0],vec![0, 0]), + (vec![11,252],vec![11, 0]), + ]); + +/* compare_impl_no_ext_unordered(vec![ (vec![11,252],vec![11, 0]), (vec![11,0],vec![0, 0]), (vec![0],vec![0, 0]), - ]); + ]);*/ } #[test] fn fuzz_noext4 () { diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 713063d4..dfb0d7d6 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -139,7 +139,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy return v1.len() * Self::NIBBLE_PER_BYTE; } - /// Calculate the number of common nibble between two left pad byte. + /// Calculate the number of common nibble between two left aligned bytes. #[inline(always)] fn left_common(a: u8, b: u8) -> usize { let mut i = 0; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index c2c1eb72..3818f3d8 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -826,39 +826,40 @@ mod tests { slice: , value: [ 65, - 65 - ] + 65, + ], }, Node::Leaf { index: 2, slice: , value: [ 65, - 66 - ] - } + 66, + ], + }, ], - value: None - } + value: None, + }, ], value: Some( [ - 65 - ] - ) + 65, + ], + ), }, Node::Leaf { index: 2, slice: , value: [ - 66 - ] - } + 66, + ], + }, ], - value: None - } - } + value: None, + }, + }, }"); + } #[test] From b45762d4baab71e2869b6336a2bf28c3e93c8423 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sun, 9 Jun 2019 20:43:52 +0200 Subject: [PATCH 088/120] No exessive buffer len, simplify flush_branch. --- trie-db/src/iter_build.rs | 261 +++++++++++++++++--------------------- 1 file changed, 117 insertions(+), 144 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index a20dfbb3..c5383189 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -16,6 +16,7 @@ use hash_db::{Hasher, HashDB, Prefix}; use core_::marker::PhantomData; +use core_::cmp::max; use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; @@ -75,9 +76,8 @@ type ArrayNode = <::CB as CacheBuilder>>::AN; /// /// Note that it is not memory optimal (all depth are allocated even if some are empty due /// to node partial). -/// Three field are used, a cache over the children, a boolean to indicate a change occured, -/// and an optional associated value. -struct CacheAccum (Vec<(ArrayNode, bool, Option, usize)>,PhantomData); +/// Three field are used, a cache over the children, an optional associated value and the depth. +struct CacheAccum (Vec<(ArrayNode, Option, usize)>,PhantomData); /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; @@ -89,85 +89,67 @@ where { fn new() -> Self { - let mut v = Vec::with_capacity(INITIAL_DEPTH); - v.push((T::CB::new_vec_slice_buff(), false, None, 0)); -/* (0..INITIAL_DEPTH).for_each(|_| - v.push((T::CB::new_vec_slice_buff(), false, None)));*/ + let v = Vec::with_capacity(INITIAL_DEPTH); CacheAccum(v, PhantomData) } #[inline(always)] fn set_elt(&mut self, depth:usize, sl: Option) { - let mut last = self.0.len() - 1; - if self.0[last].3 < depth { - self.0.push((T::CB::new_vec_slice_buff(), false, None, depth)); - last += 1; - } - if self.0[last].3 > depth { - assert!(false); - } - self.0[last].2 = sl; - self.0[last].1 = true; + if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { + self.0.push((T::CB::new_vec_slice_buff(), None, depth)); + } + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 <= depth); + self.0[last].1 = sl; } #[inline(always)] fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode>) { - let mut last = self.0.len() - 1; - if self.0[last].3 < depth { - self.0.push((T::CB::new_vec_slice_buff(), false, None, depth)); - last += 1; - } - assert_eq!(self.0[last].3, depth); + if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { + self.0.push((T::CB::new_vec_slice_buff(), None, depth)); + } + + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 == depth); self.0[last].0.as_mut()[nibble_ix] = node; - self.0[last].1 = true; } #[inline(always)] fn last_depth(&self) -> usize { let ix = self.0.len(); - if ix > 0 { - let last = ix - 1; - self.0[last].3 - } else { - 0 - } + if ix > 0 { + let last = ix - 1; + self.0[last].2 + } else { + 0 + } } #[inline(always)] fn last_last_depth(&self) -> usize { let ix = self.0.len(); - if ix > 1 { - let last = ix - 2; - self.0[last].3 - } else { - 0 - } - } + if ix > 1 { + let last = ix - 2; + self.0[last].2 + } else { + 0 + } + } #[inline(always)] - fn touched(&self, depth:usize) -> bool { - let mut res = false; - let mut ix = self.0.len(); - while ix > 0 { - - - let last = ix - 1; -// TODO rec does not make sense : rewrite the flush to access directly parent ix instead -// of minus 1 loop... - res = self.0[last].1 && self.0[last].3 == depth; - if res { return res } - ix = last; - } - res + fn is_empty(&self) -> bool { + self.0.is_empty() + } + #[inline(always)] + fn is_one(&self) -> bool { + self.0.len() == 1 } #[inline(always)] fn reset_depth(&mut self, depth:usize) { - let last = self.0.len() - 1; - assert_eq!(self.0[last].3, depth); - + debug_assert!(self.0[self.0.len() - 1].2 == depth); self.0.pop(); } @@ -179,9 +161,12 @@ where ) { let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth+1); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth + 1); let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len()); + let pr = NibbleSlice::::new_offset( + &k2.as_ref()[..], + k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len(), + ); let hash = cb_ext.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) @@ -194,71 +179,37 @@ where cb_ext: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, new_depth: usize, - old_depth: usize, is_last: bool, ) { - let mut last_branch_ix = None; - for d in (new_depth..=old_depth).rev() { - -/* let lix = self.last_depth(); - if lix == d { - let llix = self.last_last_depth(); - let is_last = llix == lix; - // root - } else { - } - }*/ - - let touched = self.touched(d); - - if touched || d == new_depth { - if let Some(branch_d) = last_branch_ix.take() { - - let last_root = d == 0 && is_last; - // reduce slice for branch - let parent_branch = touched; - let (slice_size, offset) = if parent_branch && last_root { - // corner branch last - (branch_d - d - 1, d + 1) - } else if last_root { - // corner case non branch last - (branch_d - d, d) - } else { - (branch_d - d - 1, d + 1) - }; - - let nkey = if slice_size > 0 { - Some((offset, slice_size)) - } else { - None - }; - - let is_root = d == 0 && is_last && !parent_branch; - let h = if no_ext { - // enc branch - self.alt_no_ext(&ref_branch.as_ref()[..], cb_ext, branch_d, is_root, nkey) - } else { - self.standard_ext(&ref_branch.as_ref()[..], cb_ext, branch_d, is_root, nkey) - }; - // put hash in parent - let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..],d); - self.set_node(d, nibble as usize, Some(h)); - } - } - - if d > new_depth || is_last { - if touched { - last_branch_ix = Some(d); - } - } + while self.last_depth() > new_depth || is_last && !self.is_empty() { - } - if let Some(d) = last_branch_ix { - if no_ext { - self.alt_no_ext(&ref_branch.as_ref()[..], cb_ext, d, true, None); + let lix = self.last_depth(); + let llix = max(self.last_last_depth(), new_depth); + + let (offset, slice_size, is_root) = + if llix == 0 && is_last && self.is_one() { + // branch root + (llix, lix - llix, true) + } else { + (llix + 1, lix - llix - 1, false) + }; + let nkey = if slice_size > 0 { + Some((offset, slice_size)) + } else { + None + }; + + let h = if no_ext { + // enc branch + self.alt_no_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) } else { - self.standard_ext(&ref_branch.as_ref()[..], cb_ext, d, true, None); + self.standard_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + }; + if !is_root { + // put hash in parent + let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..],llix); + self.set_node(llix, nibble as usize, Some(h)); } } } @@ -272,13 +223,15 @@ where is_root: bool, nkey: Option<(usize, usize)>, ) -> ChildReference> { - let last = self.0.len() - 1; - assert_eq!(self.0[last].3, branch_d); + let last = self.0.len() - 1; + assert_eq!(self.0[last].2, branch_d); // enc branch - let v = self.0[last].2.take(); - let encoded = T::C::branch_node(self.0[last].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); - // TODO branch_d == 0 means is _root ??!! + let v = self.0[last].1.take(); + let encoded = T::C::branch_node( + self.0[last].0.as_ref().iter(), + v.as_ref().map(|v| v.as_ref()), + ); self.reset_depth(branch_d); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); @@ -303,22 +256,22 @@ where is_root: bool, nkey: Option<(usize, usize)>, ) -> ChildReference> { - let last = self.0.len() - 1; - if self.0[last].3 != branch_d { - assert!(false); - } + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 == branch_d); // enc branch - let v = self.0[last].2.take(); + let v = self.0[last].1.take(); let nkeyix = nkey.unwrap_or((0,0)); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); let encoded = T::C::branch_node_nibbled( - // warn direct use of default empty nible encoded: NibbleSlice::new_offset(&[],0).encoded(false); pr.right_range_iter(nkeyix.1), nkeyix.1, - self.0[last].0.as_ref().iter(), v.as_ref().map(|v|v.as_ref())); + self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref())); self.reset_depth(branch_d); - let ext_len = nkey.as_ref().map(|nkeyix|nkeyix.0).unwrap_or(0); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d - ext_len); + let ext_len = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); + let pr = NibbleSlice::::new_offset( + &key_branch.as_ref()[..], + branch_d - ext_len, + ); cb_ext.process(pr.left(), encoded, is_root) } @@ -343,7 +296,9 @@ pub fn trie_visit(input: I, cb_ext: &mut F) // depth of last item let mut last_depth = 0; + let mut single = true; for (k, v) in iter_input { + single = false; let common_depth = T::N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; @@ -358,25 +313,27 @@ pub fn trie_visit(input: I, cb_ext: &mut F) // do not put with next, previous is last of a branch depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, last_depth, false); + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, false); } prev_val = (k, v); last_depth = depth_item; } // last pendings - if last_depth == 0 - && !depth_queue.touched(0) { + if single { // one single element corner case let (k2, v2) = prev_val; let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],last_depth); let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset(&k2.as_ref()[..], k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len()); + let pr = NibbleSlice::::new_offset( + &k2.as_ref()[..], + k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len(), + ); cb_ext.process(pr.left(), encoded, true); } else { depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, last_depth, true); + depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, true); } } else { // nothing null root corner case @@ -410,8 +367,14 @@ impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { } } -impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { - fn process(&mut self, encoded_prefix: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { +impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> + for TrieBuilder<'a, H, ::Out, V, DB> { + fn process( + &mut self, + encoded_prefix: Prefix, + enc_ext: Vec, + is_root: bool, + ) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); @@ -441,7 +404,12 @@ impl Default for TrieRoot { } impl ProcessEncodedNode<::Out> for TrieRoot::Out> { - fn process(&mut self, _: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + fn process( + &mut self, + _: Prefix, + enc_ext: Vec, + is_root: bool, + ) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); @@ -471,7 +439,12 @@ impl Default for TrieRootUnhashed { } impl ProcessEncodedNode<::Out> for TrieRootUnhashed { - fn process(&mut self, _: Prefix, enc_ext: Vec, is_root: bool) -> ChildReference<::Out> { + fn process( + &mut self, + _: Prefix, + enc_ext: Vec, + is_root: bool, + ) -> ChildReference<::Out> { let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); @@ -574,7 +547,7 @@ mod test { compare_impl_pk(data.clone()); compare_impl_no_ext(data.clone()); compare_impl_no_ext_pk(data.clone()); - //compare_impl_no_ext_q(data.clone()); + compare_impl_no_ext_q(data.clone()); } fn compare_impl_pk(data: Vec<(Vec,Vec)>) { @@ -743,17 +716,17 @@ mod test { #[test] fn fuzz_noext3 () { - compare_impl(vec![ + /*compare_impl(vec![ (vec![0],vec![0, 0]), (vec![11,0],vec![0, 0]), (vec![11,252],vec![11, 0]), - ]); + ]);*/ -/* compare_impl_no_ext_unordered(vec![ + compare_impl_no_ext_unordered(vec![ (vec![11,252],vec![11, 0]), (vec![11,0],vec![0, 0]), (vec![0],vec![0, 0]), - ]);*/ + ]); } #[test] fn fuzz_noext4 () { From 0218331734ce5d45456555c20949293eabc77afa Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 10 Jun 2019 19:44:44 +0200 Subject: [PATCH 089/120] fix quarter error --- test-support/reference-trie/src/lib.rs | 4 ++-- trie-db/src/iter_build.rs | 4 ++-- trie-db/src/nibble/nibbleslice.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 81e3bcb3..d4b636e4 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1102,7 +1102,7 @@ pub fn compare_impl_no_ext_q( { let db : &dyn hash_db::HashDB<_,_> = &hashdb; let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); - println!("{:?}", t); + println!("it: {:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); } @@ -1111,7 +1111,7 @@ pub fn compare_impl_no_ext_q( { let db : &dyn hash_db::HashDB<_,_> = &memdb; let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); - println!("{:?}", t); + println!("fu: {:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index c5383189..8095ab83 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -716,11 +716,11 @@ mod test { #[test] fn fuzz_noext3 () { - /*compare_impl(vec![ + compare_impl(vec![ (vec![0],vec![0, 0]), (vec![11,0],vec![0, 0]), (vec![11,252],vec![11, 0]), - ]);*/ + ]); compare_impl_no_ext_unordered(vec![ (vec![11,252],vec![11, 0]), diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 224dedab..751febb5 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -76,7 +76,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { // aligned let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; - (nb % N::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end])) + (self.offset % N::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end])) } else { // unaligned let start = self.offset / N::NIBBLE_PER_BYTE; From a2f31c50df21d141f757cd02893246f5f2317fb7 Mon Sep 17 00:00:00 2001 From: cheme Date: Sat, 22 Jun 2019 09:21:17 +0200 Subject: [PATCH 090/120] iter_build instance with print trace. --- trie-db/src/iter_build.rs | 42 +++++++++++++++++++++++++++++++++++++++ trie-db/src/lib.rs | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 8095ab83..a3d8a5a3 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -438,6 +438,48 @@ impl Default for TrieRootUnhashed { } } +/// Calculate the trie root of the trie. +/// Print a debug trace. +pub struct TrieRootPrint { + /// The resulting root. + pub root: Option, + _ph: PhantomData<(H)>, +} + +impl Default for TrieRootPrint { + fn default() -> Self { + TrieRootPrint { root: None, _ph: PhantomData } + } +} + +impl ProcessEncodedNode<::Out> for TrieRootPrint::Out> { + fn process( + &mut self, + p: Prefix, + enc_ext: Vec, + is_root: bool, + ) -> ChildReference<::Out> { + println!("Encoded node: {:x?}", &enc_ext); + println!(" with prefix: {:x?}", &p); + let len = enc_ext.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + + println!(" inline len {}", len); + return ChildReference::Inline(h, len); + } + let hash = ::hash(&enc_ext[..]); + if is_root { + self.root = Some(hash.clone()); + }; + println!(" hashed to {:x?}", hash.as_ref()); + ChildReference::Hash(hash) + } +} + + + impl ProcessEncodedNode<::Out> for TrieRootUnhashed { fn process( &mut self, diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 63b900a2..95191d48 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,7 @@ pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, ChildSliceIx}; pub use node_codec::{NodeCodec, Partial, ChildBitmap}; -pub use iter_build::{trie_visit, ProcessEncodedNode, +pub use iter_build::{trie_visit, ProcessEncodedNode, TrieRootPrint, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; pub type DBValue = elastic_array::ElasticArray128; From 65b748a532fd8a7c653177b41409955f75392b14 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 22 Jun 2019 17:34:02 +0200 Subject: [PATCH 091/120] gen impl over H. --- hash-db/src/lib.rs | 6 +-- test-support/reference-trie/src/lib.rs | 65 +++++++++++++++++--------- trie-db/src/iter_build.rs | 2 - trie-db/src/triedbmut.rs | 2 +- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index c9e5a484..bf13ef3d 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -171,7 +171,7 @@ impl<'a, H: Hasher, T> AsHashDB for &'a mut dyn HashDB { } #[cfg(feature = "std")] -impl<'a, K, V> AsPlainDB for &'a mut PlainDB { - fn as_plain_db(&self) -> &PlainDB { &**self } - fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (PlainDB + 'b) { &mut **self } +impl<'a, K, V> AsPlainDB for &'a mut dyn PlainDB { + fn as_plain_db(&self) -> &dyn PlainDB { &**self } + fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn PlainDB + 'b) { &mut **self } } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 5df03a67..beaabe55 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -63,7 +63,18 @@ impl TrieLayOut for LayoutNew { type CB = Cache16; } -impl TrieOps for LayoutNew { } +/// trie layout similar to substrate one +pub struct LayoutNewH(PhantomData); + +impl TrieLayOut for LayoutNewH { + const USE_EXTENSION: bool = false; + type H = H; + type C = ReferenceNodeCodecNoExt; + type N = NibbleHalf; + type CB = Cache16; +} + +impl TrieOps for LayoutNewH { } /// Test quarter nibble pub struct LayoutNewQuarter; @@ -655,11 +666,15 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< // `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` // but due to the current limitations of Rust const evaluation we can't // do `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. Perhaps one day soon? -impl> NodeCodec for ReferenceNodeCodec { +impl< + H: Hasher, + N: NibbleOps, + BM: ChildBitmap +> NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; - fn hashed_null_node() -> ::Out { - KeccakHasher::hash(>::empty_node()) + fn hashed_null_node() -> ::Out { + H::hash(>::empty_node()) } fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { @@ -709,9 +724,9 @@ impl> NodeCodec Option<::Out> { - if data.len() == KeccakHasher::LENGTH { - let mut r = ::Out::default(); + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + if data.len() == H::LENGTH { + let mut r = ::Out::default(); r.as_mut().copy_from_slice(data); Some(r) } else { @@ -720,7 +735,7 @@ impl> NodeCodec bool { - data == >::empty_node() + data == >::empty_node() } fn empty_node() -> &'static[u8] { @@ -733,7 +748,7 @@ impl> NodeCodec, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { + fn ext_node(partial: impl Iterator, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { let mut output = partial_to_key_it::(partial, nb_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), @@ -743,7 +758,7 @@ impl> NodeCodec::Out>>>>, + children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { let mut output = vec![0; BM::ENCODED_LEN + 1]; let mut prefix: BM::Buff = Default::default(); @@ -771,18 +786,22 @@ impl> NodeCodec, _nb_nibble: usize, - _children: impl Iterator::Out>>>>, + _children: impl Iterator::Out>>>>, _maybe_value: Option<&[u8]>) -> Vec { unreachable!() } } -impl> NodeCodec for ReferenceNodeCodecNoExt { +impl< + H: Hasher, + N: NibbleOps, + BM: ChildBitmap +> NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; - fn hashed_null_node() -> ::Out { - KeccakHasher::hash(>::empty_node()) + fn hashed_null_node() -> ::Out { + H::hash(>::empty_node()) } fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { @@ -836,12 +855,12 @@ impl> NodeCodec Option<::Out> { - as NodeCodec<_, N>>::try_decode_hash(data) + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + as NodeCodec>::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { - data == >::empty_node() + data == >::empty_node() } fn empty_node() -> &'static [u8] { @@ -857,13 +876,13 @@ impl> NodeCodec, _nbnibble: usize, - _child: ChildReference<::Out>, + _child: ChildReference<::Out>, ) -> Vec { unreachable!() } fn branch_node( - _children: impl Iterator::Out>>>>, + _children: impl Iterator::Out>>>>, _maybe_value: Option<&[u8]>, ) -> Vec { unreachable!() @@ -872,7 +891,7 @@ impl> NodeCodec, nb_nibble: usize, - children: impl Iterator::Out>>>>, + children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { @@ -892,7 +911,7 @@ impl> NodeCodec { - inline_data[..len].encode_to(&mut output); + inline_data.as_ref()[..len].encode_to(&mut output); true } None => false, @@ -1209,8 +1228,8 @@ pub fn compare_no_ext_insert_remove( fn too_big_nibble_len () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; - let enc = as NodeCodec<_, NibbleHalf>>::leaf_node(((0,0),&input), &[1]); - let dec = as NodeCodec<_, NibbleHalf>>::decode(&enc).unwrap(); + let enc = as NodeCodec>::leaf_node(((0,0),&input), &[1]); + let dec = as NodeCodec>::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) } else { None }; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index a3d8a5a3..9a058b6e 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -478,8 +478,6 @@ impl ProcessEncodedNode<::Out> for TrieRootPrint ProcessEncodedNode<::Out> for TrieRootUnhashed { fn process( &mut self, diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 058311a6..eebb593f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1472,7 +1472,7 @@ mod tests { fn reference_hashed_null_node() -> ::Out { - as NodeCodec<_, ::N>>::hashed_null_node() + as NodeCodec::N>>::hashed_null_node() } #[test] From 56b13987a60d125dfe2927ac375a41d7c7b47475 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 25 Jun 2019 12:42:02 +0200 Subject: [PATCH 092/120] no_std --- trie-db/src/iter_build.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index a3d8a5a3..78f567d4 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -438,6 +438,7 @@ impl Default for TrieRootUnhashed { } } +#[cfg(feature = "std")] /// Calculate the trie root of the trie. /// Print a debug trace. pub struct TrieRootPrint { @@ -446,12 +447,14 @@ pub struct TrieRootPrint { _ph: PhantomData<(H)>, } +#[cfg(feature = "std")] impl Default for TrieRootPrint { fn default() -> Self { TrieRootPrint { root: None, _ph: PhantomData } } } +#[cfg(feature = "std")] impl ProcessEncodedNode<::Out> for TrieRootPrint::Out> { fn process( &mut self, @@ -825,6 +828,15 @@ mod test { ]); } + #[test] + fn polka_re_test () { + compare_impl(vec![ + (vec![77, 111, 111, 55, 111, 104, 121, 97], vec![68, 97, 105, 55, 105, 101, 116, 111]), + (vec![101, 105, 67, 104, 111, 111, 66, 56], vec![97, 56, 97, 113, 117, 53, 97]), + (vec![105, 97, 48, 77, 101, 105, 121, 101], vec![69, 109, 111, 111, 82, 49, 97, 105]), + ]); + } + /* #[test] From 2efca9b31e3f06b449cd8d9f335d5ede2452ccde Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 25 Jun 2019 12:43:43 +0200 Subject: [PATCH 093/120] fix no_std bis --- trie-db/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 95191d48..124b7afe 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -100,6 +100,9 @@ pub use node_codec::{NodeCodec, Partial, ChildBitmap}; pub use iter_build::{trie_visit, ProcessEncodedNode, TrieRootPrint, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; +#[cfg(feature = "std")] +pub use iter_build::TrieRootPrint; + pub type DBValue = elastic_array::ElasticArray128; /// Trie Errors. From fcb70e20f2a47e8bb13b50882666882fb0d87db0 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 25 Jun 2019 12:44:26 +0200 Subject: [PATCH 094/120] no_std tierce --- trie-db/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 124b7afe..cc9098cd 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,7 @@ pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, ChildSliceIx}; pub use node_codec::{NodeCodec, Partial, ChildBitmap}; -pub use iter_build::{trie_visit, ProcessEncodedNode, TrieRootPrint, +pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; #[cfg(feature = "std")] From 116978f336ae7c1293337b570b6d2ed96591c46f Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 Jul 2019 12:27:20 +0200 Subject: [PATCH 095/120] quick bench trie mut against trie root. --- trie-db/benches/bench.rs | 151 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index e0a24c39..97494c92 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -16,18 +16,24 @@ extern crate criterion; use criterion::{Criterion, black_box, Bencher}; criterion_group!(benches, - nibble_common_prefix, root_old, root_new, root_a_big_v, root_b_big_v, root_a_small_v, root_b_small_v, + trie_mut_ref_root_a, + trie_mut_ref_root_b, + trie_mut_root_a, + trie_mut_root_b, + trie_mut_a, + trie_mut_b, ); criterion_main!(benches); extern crate trie_standardmap; extern crate trie_db; +extern crate memory_db; extern crate rand; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; use trie_db::{NibbleSlice, NibbleHalf}; @@ -254,3 +260,146 @@ fn input2(seed: u64, len: usize, vl: usize) -> Vec<(Vec,Vec)> { let data = data_sorted_unique(fuzz_to_data2(data,vl)); data } + +fn input_unsorted(seed: u64, len: usize, vl: usize) -> Vec<(Vec,Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + fuzz_to_data(data) +} + +fn trie_mut_root_a(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_root_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(|| { + let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + +fn trie_mut_root_b(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }) + ,data); +} + +fn trie_mut_ref_root_a(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_ref_root_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(|| { + let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation + + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::ref_trie_root(inputc); + }) + ,data); +} + +fn trie_mut_ref_root_b(c: &mut Criterion) { + let data : Vec,Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_ref_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::ref_trie_root(inputc); + }) + ,data); +} + + + +fn trie_mut_a(c: &mut Criterion) { + use trie_db::TrieMut; + use memory_db::HashKey; + let data : Vec,Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(|| { + let datac:Vec<(Vec,Vec)> = data.clone(); + + let mut root = Default::default(); + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + let mut trie = reference_trie::RefTrieDBMut::new(&mut mdb, &mut root); + for (key, value) in datac { + trie.insert(&key, &value) + .expect("changes trie: insertion to trie is not allowed to fail within runtime"); + } + + }) + ,data); +} + +fn trie_mut_b(c: &mut Criterion) { + use trie_db::TrieMut; + use memory_db::HashKey; + let data : Vec,Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data.clone(); + + let mut root = Default::default(); + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + let mut trie = reference_trie::RefTrieDBMut::new(&mut mdb, &mut root); + for (key, value) in datac { + trie.insert(&key, &value) + .expect("changes trie: insertion to trie is not allowed to fail within runtime"); + } + + }) + ,data); +} + + From af7f2bb18b7360220731e3ac0a9329ad9b77c45b Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 Jul 2019 12:51:01 +0200 Subject: [PATCH 096/120] memory db building iter_build benches --- test-support/reference-trie/src/lib.rs | 32 ++++++++++++++++++ trie-db/benches/bench.rs | 46 ++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index beaabe55..a3a636f5 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1041,6 +1041,38 @@ pub fn calc_root_no_ext( cb.root.unwrap_or(Default::default()) } +pub fn calc_root_build( + data: I, + hashdb: &mut DB +) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, + DB: hash_db::HashDB +{ + let mut cb = TrieBuilder::new(hashdb); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) +} + +pub fn calc_root_build_no_ext( + data: I, + hashdb: &mut DB, +) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, + DB: hash_db::HashDB +{ + let mut cb = TrieBuilder::new(hashdb); + trie_db::trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) +} + + + pub fn compare_impl_no_ext( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 97494c92..89241715 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -28,6 +28,8 @@ criterion_group!(benches, trie_mut_root_b, trie_mut_a, trie_mut_b, + trie_mut_build_a, + trie_mut_build_b, ); criterion_main!(benches); @@ -402,4 +404,48 @@ fn trie_mut_b(c: &mut Criterion) { ,data); } +fn trie_mut_build_a(c: &mut Criterion) { + use memory_db::HashKey; + let data : Vec,Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_build_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(|| { + let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + reference_trie::calc_root_build(inputc, &mut mdb); + }) + ,data); +} + +fn trie_mut_build_b(c: &mut Criterion) { + use memory_db::HashKey; + let data : Vec,Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_build_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + b.iter(||{ + let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + // this is in `ref_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + reference_trie::calc_root_build(inputc, &mut mdb); + }) + ,data); +} + From 5eb9b99d4e2f5bccef64277fdb0896ac888d04dc Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 8 Jul 2019 10:25:15 +0200 Subject: [PATCH 097/120] update versioning --- hash-db/Cargo.toml | 2 +- hash256-std-hasher/Cargo.toml | 2 +- memory-db/Cargo.toml | 6 +++--- test-support/keccak-hasher/Cargo.toml | 6 +++--- test-support/reference-trie/Cargo.toml | 14 +++++++------- test-support/trie-bench/Cargo.toml | 14 +++++++------- test-support/trie-standardmap/Cargo.toml | 6 +++--- trie-db/Cargo.toml | 14 +++++++------- trie-root/Cargo.toml | 10 +++++----- 9 files changed, 37 insertions(+), 37 deletions(-) diff --git a/hash-db/Cargo.toml b/hash-db/Cargo.toml index 6bf38a1b..d453445a 100644 --- a/hash-db/Cargo.toml +++ b/hash-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hash-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Trait for hash-keyed databases." license = "Apache-2.0" diff --git a/hash256-std-hasher/Cargo.toml b/hash256-std-hasher/Cargo.toml index b3e0b030..6ee6ea29 100644 --- a/hash256-std-hasher/Cargo.toml +++ b/hash256-std-hasher/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hash256-std-hasher" description = "Standard library hasher for 256-bit prehashed keys." -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] license = "Apache-2.0" homepage = "https://github.com/paritytech/trie" diff --git a/memory-db/Cargo.toml b/memory-db/Cargo.toml index a5be306f..5c19421f 100644 --- a/memory-db/Cargo.toml +++ b/memory-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memory-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "In-memory implementation of hash-db, useful for tests" repository = "https://github.com/paritytech/trie" @@ -9,11 +9,11 @@ license = "Apache-2.0" [dependencies] heapsize = { version = "0.4", optional = true } parity-util-mem = { version = "0.2", default-features = false } -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} hashmap_core = { version = "0.1" } [dev-dependencies] -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0"} +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0"} criterion = "0.2.8" [features] diff --git a/test-support/keccak-hasher/Cargo.toml b/test-support/keccak-hasher/Cargo.toml index 587bb7af..7b2cfea4 100644 --- a/test-support/keccak-hasher/Cargo.toml +++ b/test-support/keccak-hasher/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "keccak-hasher" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Keccak-256 implementation of the Hasher trait" repository = "https://github.com/paritytech/parity/" @@ -8,8 +8,8 @@ license = "Apache-2.0" [dependencies] tiny-keccak = "1.4.2" -hash-db = { path = "../../hash-db", default-features = false, version = "0.14.0" } -hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.14.0" } +hash-db = { path = "../../hash-db", default-features = false, version = "0.15.0" } +hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.0" } [features] default = ["std"] diff --git a/test-support/reference-trie/Cargo.toml b/test-support/reference-trie/Cargo.toml index d63b43aa..907076c2 100644 --- a/test-support/reference-trie/Cargo.toml +++ b/test-support/reference-trie/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reference-trie" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Simple reference trie format" repository = "https://github.com/paritytech/trie/" @@ -8,15 +8,15 @@ license = "Apache-2.0" edition = "2018" [dependencies] -hash-db = { path = "../../hash-db" , version = "0.14.0"} -hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.14.0" } -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0" } -trie-db = { path = "../../trie-db", default-features = false, version = "0.14.0"} -trie-root = { path = "../../trie-root", default-features = false, version = "0.14.0" } +hash-db = { path = "../../hash-db" , version = "0.15.0"} +hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.0" } +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0" } +trie-db = { path = "../../trie-db", default-features = false, version = "0.15.0"} +trie-root = { path = "../../trie-root", default-features = false, version = "0.15.0" } parity-codec = { version = "4.0", features = ["derive"] } [dev-dependencies] -trie-bench = { path = "../trie-bench", version = "0.14.0" } +trie-bench = { path = "../trie-bench", version = "0.15.0" } criterion = "0.2.8" [[bench]] diff --git a/test-support/trie-bench/Cargo.toml b/test-support/trie-bench/Cargo.toml index b063a46a..370a0fdc 100644 --- a/test-support/trie-bench/Cargo.toml +++ b/test-support/trie-bench/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "trie-bench" description = "Standard benchmarking suite for tries" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] license = "Apache-2.0" edition = "2018" [dependencies] -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0" } -trie-standardmap = { path = "../trie-standardmap", version = "0.14.0" } -hash-db = { path = "../../hash-db" , version = "0.14.0"} -memory-db = { path = "../../memory-db", version = "0.14.0" } -trie-root = { path = "../../trie-root", version = "0.14.0" } -trie-db = { path = "../../trie-db", version = "0.14.0" } +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0" } +trie-standardmap = { path = "../trie-standardmap", version = "0.15.0" } +hash-db = { path = "../../hash-db" , version = "0.15.0"} +memory-db = { path = "../../memory-db", version = "0.15.0" } +trie-root = { path = "../../trie-root", version = "0.15.0" } +trie-db = { path = "../../trie-db", version = "0.15.0" } criterion = "0.2.8" parity-codec = "4.0" diff --git a/test-support/trie-standardmap/Cargo.toml b/test-support/trie-standardmap/Cargo.toml index 2cca564b..e07efe2a 100644 --- a/test-support/trie-standardmap/Cargo.toml +++ b/test-support/trie-standardmap/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "trie-standardmap" description = "Standard test map for profiling tries" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] license = "Apache-2.0" [dependencies] -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0"} -hash-db = { path = "../../hash-db" , version = "0.14.0"} +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0"} +hash-db = { path = "../../hash-db" , version = "0.15.0"} diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index ce5c9bc1..8969e88d 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Merkle-Patricia Trie generic over key hasher and node encoding" repository = "https://github.com/paritytech/trie" @@ -10,17 +10,17 @@ license = "Apache-2.0" log = "0.4" rand = { version = "0.6", default-features = false } elastic-array = { version = "0.10", default-features = false } -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} hashmap_core = { version = "0.1" } [dev-dependencies] env_logger = "0.6" -memory-db = { path = "../memory-db", version = "0.14.0" } -trie-root = { path = "../trie-root", version = "0.14.0"} -trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.14.0" } -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0" } +memory-db = { path = "../memory-db", version = "0.15.0" } +trie-root = { path = "../trie-root", version = "0.15.0"} +trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.15.0" } +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0" } # DISABLE the following line when publishing until cyclic dependencies are resolved https://github.com/rust-lang/cargo/issues/4242 -reference-trie = { path = "../test-support/reference-trie", version = "0.14.0" } +reference-trie = { path = "../test-support/reference-trie", version = "0.15.0" } hex-literal = "0.1" criterion = "0.2.8" parity-codec = "3.0" diff --git a/trie-root/Cargo.toml b/trie-root/Cargo.toml index 069a2342..517d942e 100644 --- a/trie-root/Cargo.toml +++ b/trie-root/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-root" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "In-memory patricia trie operations" repository = "https://github.com/paritytech/trie" @@ -8,14 +8,14 @@ license = "Apache-2.0" categories = [ "no-std" ] [dependencies] -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} [dev-dependencies] hex-literal = "0.1" -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0" } -trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.14.0" } +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0" } +trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.15.0" } # DISABLE the following line when publishing until cyclic dependencies are resolved https://github.com/rust-lang/cargo/issues/4242 -reference-trie = { path = "../test-support/reference-trie", version = "0.14.0" } +reference-trie = { path = "../test-support/reference-trie", version = "0.15.0" } [features] default = ["std"] From 0a2938977240160ad0e2a3665c70b5b2b772ef3f Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Jul 2019 10:24:39 +0200 Subject: [PATCH 098/120] Small doc changes and formatting fixes. --- hash-db/src/lib.rs | 14 ++-- memory-db/src/lib.rs | 31 ++++----- test-support/reference-trie/src/lib.rs | 64 +++++++------------ test-support/trie-bench/Cargo.toml | 2 +- trie-db/benches/bench.rs | 6 +- trie-db/fuzz/fuzz_targets/trie_root.rs | 2 +- .../fuzz/fuzz_targets/trie_root_fix_len.rs | 2 +- .../fuzz/hongfuzz_targets/trie_root_new.rs | 12 ++-- trie-db/fuzz/src/lib.rs | 2 +- trie-db/src/fatdb.rs | 3 +- trie-db/src/iter_build.rs | 64 +++++++------------ trie-db/src/lib.rs | 26 +++++--- trie-db/src/nibble/mod.rs | 13 ++-- trie-db/src/nibble/nibbleslice.rs | 6 +- trie-db/src/nibble/nibblevec.rs | 14 ++-- trie-db/src/node.rs | 2 +- 16 files changed, 121 insertions(+), 142 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index bf13ef3d..b8300215 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -37,14 +37,14 @@ impl MaybeDebug for T {} /// and for root nodes. pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0,0)); -/// The Prefix of a trie node. Prefix is nibble path up to +/// The prefix of a trie node, a prefix is the nibble path up to /// the node in the trie. -/// For a value node it is the value key without the Partial -/// bytes (encoded in the node). -/// Being the leftmost portion of a byte, its internal representation -/// is byte slice and and a padded byte. -/// The padded byte is represented as a pair with number of nibble first, -/// and left aligned padded value second. +/// For a value node, it is the node key without the partial +/// bytes (part of the key encoded in the node). +/// As the leftmost portion of a byte array, its internal representation +/// is a byte slice followed by a padded byte. +/// The padded byte is a pair of u8 containing the number of nibble first, +/// and the left aligned padded value second. pub type Prefix<'a> = (&'a[u8], (u8, u8)); /// Trait describing an object that can hash a slice of bytes. Used to abstract diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 5eb3ce7e..bd315394 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -164,11 +164,11 @@ pub trait KeyFunction { } -/// Make database key from hash and prefix. +/// Derive a database key from hash and prefix. /// -/// Prefix could be ordered, but key hash appending will break this property. -/// If true ordering is needed we would need a fix length encoding for prefix -/// (so a maximum length and additional byte (or chunk of prefix encoded)). +/// This method does not preserve ordering of prefix. +/// +/// If ordering is needed, a length encoded prefix would be needed. pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); prefixed_key.extend_from_slice(prefix.0); @@ -212,6 +212,7 @@ fn test_ordered_prefixed_key() { ); } } + /// Parameterize the size of successive chunks when using /// `ordered_prefixed_key`. /// Length of each chunk is minimum 1, maximum 254. @@ -220,15 +221,15 @@ pub trait OrderedPrefixedKeyChunk: IntoIterator + Clone + MaybeDeb fn estimate_enc_len(input: usize) -> usize { input } } -/// Make database key from hash and prefix. +/// Derive a database key from hash and prefix. +/// +/// Key are byte ordered depending on prefix (follows trie iteration). /// -/// The scheme allows byte ordering of key depending on prefix (can iterate -/// trie with it). -/// Every chunk we add a 2 byte (could be reduce to one if storing directly a -/// total number of chunks), the number of written bytes (last prefix padded one included), and -/// the number of bytes in the prefix padding byte (note that for aligned prefix we add a -/// theorically useless 0 to keep a simple trait). -pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec +/// Size encoding use fix sized chunks with 2 additional encoding bytes (not optimal +/// for multiple chunks): the number of written bytes , and +/// the number of bytes in the prefix padding byte. +/// Padding is 0 values, and the 2 additional bytes allows ordering for 0 keys. +pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec where H: KeyHasher, C: OrderedPrefixedKeyChunk, @@ -336,12 +337,12 @@ impl KeyFunction for PrefixedKey { /// Key function that concatenates prefix and hash. /// The resulting key byte is ordered by prefix byte value /// (in case of a trie prefix iteration over the prefixed -/// key should give the same order as the trie). -/// Also note that this scheme allows to retrieve the prefix +/// nodes are ordered the same way as in the trie). +/// Also note that this scheme allow retrieval of the prefix /// original value. pub struct OrderedPrefixedKey(C, PhantomData); -impl KeyFunction for OrderedPrefixedKey +impl KeyFunction for OrderedPrefixedKey where H: KeyHasher, C: OrderedPrefixedKeyChunk, diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index a3a636f5..b0605e07 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -405,8 +405,6 @@ impl TrieStream for ReferenceTrieStreamNoExt { fn out(self) -> Vec { self.buffer } } - - /// A node header. #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum NodeHeader { @@ -462,7 +460,6 @@ fn s_size_and_prefix_iter(size: usize, prefix: u8) -> impl Iterator { first_byte.chain(::std::iter::from_fn(next_bytes)) } -/// bounding size to storage in a u16 variable to avoid dos fn s_encode_size_and_prefix(size: usize, prefix: u8, out: &mut impl Output) { for b in s_size_and_prefix_iter(size, prefix) { out.push_byte(b) @@ -506,28 +503,6 @@ fn test_encoding_simple_trie() { } } -/* -#[test] -fn test_mal() { - let mut unique = std::collections::BTreeMap::new(); - // test over 32 bit only is still 4 byte & this bruteforce takes way to long... - for i in (0..u32::max_value() / 4) { - let enc = i.to_be_bytes(); - assert!(enc[0] >> 6 == 0); - let input = &mut &enc[..]; - let first = input.read_byte().unwrap(); - if let Some(v) = s_decode_size(first, input) { - let mut rem = 0; - while let Some(_) = input.read_byte() { rem += 1 } - if let Some((prev,prem)) = unique.insert(v, (enc.to_vec(),rem)) { - assert_eq!(&enc[..4 - rem], &prev[..4 - prem], - "duplicated key val {} {} {:x?} {:x?}", v, i, prev, enc); - } - } - } -} -*/ - impl Encode for NodeHeaderNoExt { fn encode_to(&self, output: &mut T) { match self { @@ -575,7 +550,8 @@ impl Decode for NodeHeaderNoExt { pub struct ReferenceNodeCodec(PhantomData); /// Simple reference implementation of a `NodeCodec`. -/// This is following https://github.com/w3f/polkadot-re-spec/issues/8 +/// Implementation follows https://github.com/w3f/polkadot-re-spec/issues/8. +/// It is mainly testing trie without extension node. #[derive(Default, Clone)] pub struct ReferenceNodeCodecNoExt(PhantomData); @@ -922,7 +898,7 @@ impl< } -// TODO fuse with other layout +/// Compare trie builder and in memory trie. pub fn compare_impl + Eq> ( data: Vec<(Vec,Vec)>, mut memdb: X, @@ -966,6 +942,7 @@ pub fn compare_impl + Eq> ( assert!(memdb == hashdb); } +/// Compare trie builder and trie root implementations. pub fn compare_root( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, @@ -987,6 +964,7 @@ pub fn compare_root( assert_eq!(root, root_new); } +/// Compare trie builder and trie root unhashed implementations. pub fn compare_unhashed( data: Vec<(Vec,Vec)>, ) { @@ -1000,6 +978,8 @@ pub fn compare_unhashed( assert_eq!(root, root_new); } +/// Compare trie builder and trie root unhashed implementations. +/// This uses the variant without extension nodes. pub fn compare_unhashed_no_ext( data: Vec<(Vec,Vec)>, ) { @@ -1013,8 +993,7 @@ pub fn compare_unhashed_no_ext( assert_eq!(root, root_new); } - - +/// Trie builder root calculation utility. pub fn calc_root( data: I, ) -> ::Out @@ -1028,6 +1007,8 @@ pub fn calc_root( cb.root.unwrap_or(Default::default()) } +/// Trie builder root calculation utility. +/// This uses the variant without extension nodes. pub fn calc_root_no_ext( data: I, ) -> ::Out @@ -1041,6 +1022,7 @@ pub fn calc_root_no_ext( cb.root.unwrap_or(Default::default()) } +/// Trie builder trie building utility. pub fn calc_root_build( data: I, hashdb: &mut DB @@ -1049,13 +1031,15 @@ pub fn calc_root_build( I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } +/// Trie builder trie building utility. +/// This uses the variant without extension nodes. pub fn calc_root_build_no_ext( data: I, hashdb: &mut DB, @@ -1064,15 +1048,15 @@ pub fn calc_root_build_no_ext( I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); trie_db::trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - - +/// Compare trie builder and in memory trie. +/// This uses the variant without extension nodes. pub fn compare_impl_no_ext( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, @@ -1091,12 +1075,7 @@ pub fn compare_impl_no_ext( } t.root().clone() }; -/* { - let db : &dyn hash_db::HashDB<_,_> = &memdb; - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); - println!("{:?}", t); - }*/ - + if root != root_new { { let db : &dyn hash_db::HashDB<_,_> = &memdb; @@ -1119,6 +1098,9 @@ pub fn compare_impl_no_ext( assert_eq!(root, root_new); } +/// Compare trie builder and in memory trie. +/// This uses the variant without extension nodes. +/// This uses a radix 4 trie. pub fn compare_impl_no_ext_q( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, @@ -1166,7 +1148,7 @@ pub fn compare_impl_no_ext_q( assert_eq!(root, root_new); } - +/// `compare_impl_no_ext` for unordered input. pub fn compare_impl_no_ext_unordered( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, @@ -1210,6 +1192,8 @@ pub fn compare_impl_no_ext_unordered( assert_eq!(root, root_new); } +/// Testing utility that uses some periodic removal over +/// its input test data. pub fn compare_no_ext_insert_remove( data: Vec<(bool, Vec,Vec)>, mut memdb: impl hash_db::HashDB, diff --git a/test-support/trie-bench/Cargo.toml b/test-support/trie-bench/Cargo.toml index 370a0fdc..45e20c26 100644 --- a/test-support/trie-bench/Cargo.toml +++ b/test-support/trie-bench/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] keccak-hasher = { path = "../keccak-hasher", version = "0.15.0" } -trie-standardmap = { path = "../trie-standardmap", version = "0.15.0" } +trie-standardmap = { path = "../trie-standardmap", version = "0.15.0" } hash-db = { path = "../../hash-db" , version = "0.15.0"} memory-db = { path = "../../memory-db", version = "0.15.0" } trie-root = { path = "../../trie-root", version = "0.15.0" } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 89241715..d3dd4d54 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -375,7 +375,7 @@ fn trie_mut_a(c: &mut Criterion) { trie.insert(&key, &value) .expect("changes trie: insertion to trie is not allowed to fail within runtime"); } - + }) ,data); } @@ -399,7 +399,7 @@ fn trie_mut_b(c: &mut Criterion) { trie.insert(&key, &value) .expect("changes trie: insertion to trie is not allowed to fail within runtime"); } - + }) ,data); } @@ -447,5 +447,3 @@ fn trie_mut_build_b(c: &mut Criterion) { }) ,data); } - - diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs index bb698909..e9c76df6 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -5,5 +5,5 @@ use trie_db_fuzz::fuzz_that_ref_trie_root; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_ref_trie_root(data); + fuzz_that_ref_trie_root(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs index 4a75f81a..ac75c605 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -5,5 +5,5 @@ use trie_db_fuzz::fuzz_that_ref_trie_root_fix_len; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_ref_trie_root_fix_len(data); + fuzz_that_ref_trie_root_fix_len(data); }); diff --git a/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs index 78e327db..9e954954 100644 --- a/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs +++ b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs @@ -3,10 +3,10 @@ use honggfuzz::fuzz; use trie_db_fuzz::fuzz_that_compare_impl; fn main() { - loop { - fuzz!(|data: &[u8]| { - // fuzzed code goes here - fuzz_that_compare_impl(data); - }); - } + loop { + fuzz!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_compare_impl(data); + }); + } } diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 96902b79..c26a33d8 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -58,7 +58,7 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { let mut res = Vec::new(); let mut torem = None; - let mut a = 0; + let mut a = 0; for (a, d) in data.into_iter().enumerate() { if a % 7 == 6 { // a random removal some time diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 6c2086d4..32a85162 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -13,7 +13,8 @@ // limitations under the License. use hash_db::{HashDBRef, Hasher}; -use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, TrieLayOut, CError, TrieHash}; +use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, + TrieLayOut, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 5da2aeb1..aad96a9d 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -13,6 +13,9 @@ // limitations under the License. //! Alternative tools for working with key value ordered iterator without recursion. +//! This is iterative implementation of `trie_root` algorithm, using `NodeCodec` +//! implementation. +//! See `trie_visit` function. use hash_db::{Hasher, HashDB, Prefix}; use core_::marker::PhantomData; @@ -41,7 +44,7 @@ pub trait CacheBuilder { /// The type of the cache. type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; /// Create a new cache. - fn new_vec_slice_buff() -> Self::AN; + fn new_vec_slice_buff() -> Self::AN; } /// Cache builder for radix 16 trie. @@ -72,7 +75,7 @@ type ArrayNode = <::CB as CacheBuilder>>::AN; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index // second str is in branch value -/// Struct containing cache while iterating, can be at most the length of the lowest nibble. +/// Struct containing iteration cache, can be at most the length of the lowest nibble. /// /// Note that it is not memory optimal (all depth are allocated even if some are empty due /// to node partial). @@ -108,7 +111,7 @@ where if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { self.0.push((T::CB::new_vec_slice_buff(), None, depth)); } - + let last = self.0.len() - 1; debug_assert!(self.0[last].2 == depth); @@ -157,7 +160,7 @@ where &mut self, cb_ext: &mut impl ProcessEncodedNode>, target_depth: usize, - (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), + (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), ) { let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) @@ -187,7 +190,7 @@ where let lix = self.last_depth(); let llix = max(self.last_last_depth(), new_depth); - let (offset, slice_size, is_root) = + let (offset, slice_size, is_root) = if llix == 0 && is_last && self.is_one() { // branch root (llix, lix - llix, true) @@ -199,7 +202,7 @@ where } else { None }; - + let h = if no_ext { // enc branch self.alt_no_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) @@ -277,10 +280,10 @@ where } -/// Function visiting trie from key value with a `ProccessEncodedNode`. +/// Function visiting trie from key/value inputs with a `ProccessEncodedNode` callback. /// Calls to each node occurs ordered but with longest depth first (from node to /// branch to root), this differs form key ordering a bit. -pub fn trie_visit(input: I, cb_ext: &mut F) +pub fn trie_visit(input: I, cb_ext: &mut F) where T: TrieLayOut, I: IntoIterator, @@ -363,7 +366,7 @@ pub struct TrieBuilder<'a, H, HO, V, DB> { impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { pub fn new(db: &'a mut DB) -> Self { - TrieBuilder { db, root: None, _ph: PhantomData } + TrieBuilder { db, root: None, _ph: PhantomData } } } @@ -399,7 +402,7 @@ pub struct TrieRoot { impl Default for TrieRoot { fn default() -> Self { - TrieRoot { root: None, _ph: PhantomData } + TrieRoot { root: None, _ph: PhantomData } } } @@ -434,7 +437,7 @@ pub struct TrieRootUnhashed { impl Default for TrieRootUnhashed { fn default() -> Self { - TrieRootUnhashed { root: None, _ph: PhantomData } + TrieRootUnhashed { root: None, _ph: PhantomData } } } @@ -450,7 +453,7 @@ pub struct TrieRootPrint { #[cfg(feature = "std")] impl Default for TrieRootPrint { fn default() -> Self { - TrieRootPrint { root: None, _ph: PhantomData } + TrieRootPrint { root: None, _ph: PhantomData } } } @@ -462,21 +465,21 @@ impl ProcessEncodedNode<::Out> for TrieRootPrint, is_root: bool, ) -> ChildReference<::Out> { - println!("Encoded node: {:x?}", &enc_ext); - println!(" with prefix: {:x?}", &p); + println!("Encoded node: {:x?}", &enc_ext); + println!(" with prefix: {:x?}", &p); let len = enc_ext.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); - println!(" inline len {}", len); + println!(" inline len {}", len); return ChildReference::Inline(h, len); } let hash = ::hash(&enc_ext[..]); if is_root { self.root = Some(hash.clone()); }; - println!(" hashed to {:x?}", hash.as_ref()); + println!(" hashed to {:x?}", hash.as_ref()); ChildReference::Hash(hash) } } @@ -503,8 +506,6 @@ impl ProcessEncodedNode<::Out> for TrieRootUnhashed { } } - - #[cfg(test)] mod test { use DBValue; @@ -679,7 +680,6 @@ mod test { compare_unhashed(d.clone()); compare_unhashed_no_ext(d); } - #[test] fn root_extension_tierce_big () { // on more content unhashed would hash @@ -691,8 +691,6 @@ mod test { (vec![6u8,2u8,3u8,13u8],vec![8u8;32]), ]); } - - #[test] fn trie_middle_node2x () { compare_impl(vec![ @@ -756,7 +754,6 @@ mod test { (vec![0xbb, 0xcc], vec![0xbc]), ]); } - #[test] fn fuzz_noext3 () { compare_impl(vec![ @@ -789,7 +786,6 @@ mod test { ]; compare_no_ext_insert_remove(data); } - #[test] fn fuzz_noext_ins_rem_2 () { let data = vec![ @@ -800,7 +796,6 @@ mod test { ]; compare_no_ext_insert_remove(data); } - #[test] fn two_bytes_nibble_len () { let data = vec![ @@ -829,23 +824,10 @@ mod test { #[test] fn polka_re_test () { compare_impl(vec![ - (vec![77, 111, 111, 55, 111, 104, 121, 97], vec![68, 97, 105, 55, 105, 101, 116, 111]), - (vec![101, 105, 67, 104, 111, 111, 66, 56], vec![97, 56, 97, 113, 117, 53, 97]), - (vec![105, 97, 48, 77, 101, 105, 121, 101], vec![69, 109, 111, 111, 82, 49, 97, 105]), + (vec![77, 111, 111, 55, 111, 104, 121, 97], vec![68, 97, 105, 55, 105, 101, 116, 111]), + (vec![101, 105, 67, 104, 111, 111, 66, 56], vec![97, 56, 97, 113, 117, 53, 97]), + (vec![105, 97, 48, 77, 101, 105, 121, 101], vec![69, 109, 111, 111, 82, 49, 97, 105]), ]); - } - - - -/* #[test] - fn fdispc () { - let data = vec![ - (vec![0], vec![251;32]), - (vec![0,1], vec![251; 32]), - (vec![0,1,2], vec![251; 32]), - ]; - compare_impl_no_ext_pk(data); - panic!("dd"); } - */ + } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index cc9098cd..e42ae4ab 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -202,7 +202,10 @@ pub trait Trie { } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { + fn get<'a, 'key>( + &'a self, + key: &'key [u8], + ) -> Result, TrieHash, CError> where 'a: 'key { self.get_with(key, DBValue::from_slice) } @@ -236,7 +239,10 @@ pub trait TrieMut { } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key; + fn get<'a, 'key>( + &'a self, + key: &'key [u8], + ) -> Result, TrieHash, CError> where 'a: 'key; /// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing /// `key` from the trie. Returns the old value associated with this key, if it existed. @@ -313,7 +319,10 @@ impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { wrapper!(self, contains, key) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> + fn get_with<'a, 'key, Q: Query>( + &'a self, key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> where 'a: 'key { wrapper!(self, get_with, key, query) @@ -381,8 +390,8 @@ where } /// Trait with definition of trie layout. -/// It contains all associated trait needed for -/// a trie definition. +/// Contains all associated trait needed for +/// a trie definition or implementation. pub trait TrieLayOut { /// If true, the trie will use extension nodes and /// no partial in branch, if false the trie will only @@ -390,9 +399,9 @@ pub trait TrieLayOut { const USE_EXTENSION: bool; /// Hasher to use for this trie. type H: Hasher; - /// Codec to use (need to match hasher and nibble ops) + /// Codec to use (needs to match hasher and nibble ops). type C: NodeCodec; - /// Trie nibble constants. + /// Trie nibble constants. It defines trie radix. type N: NibbleOps; /// Technical trait for cache, it should match the radix /// of `NibbleOps`. @@ -400,6 +409,7 @@ pub trait TrieLayOut { } /// Trait with operation on key value iterator. +/// It associates trie definition with chosen methods. /// This trait contains its own default implementations /// and exists only to allow alternate algorithm usage. pub trait TrieOps: Sized + TrieLayOut { @@ -434,7 +444,7 @@ pub trait TrieOps: Sized + TrieLayOut { trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - /// Encoding of index as a key (when reusing general trie for + /// Encoding of index as a key (when reusing general trie for /// indexed trie). fn encode_index(input: u32) -> Vec { // be for byte ordering diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index dfb0d7d6..d7d6a423 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -31,9 +31,9 @@ const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// Generic methods should not need redefinition except for optimization /// purpose. pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + MaybeDebug { - /// See [`ByteLayout`]. + /// See [`ByteLayout`]. const REPR : ByteLayout; - /// Single nibble length in bit. + /// Single nibble length in bit. const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); /// Number of nibble per byte. const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; @@ -71,6 +71,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } + /// Mask a byte from a ix > 0 (ix being content) /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. #[inline(always)] @@ -81,6 +82,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy b } } + /// Get u8 nibble value at a given index of a byte. #[inline(always)] fn at_left(ix: u8, b: u8) -> u8 { @@ -201,7 +203,7 @@ pub enum ByteLayout { } impl NibbleOps for NibbleHalf { - const REPR: ByteLayout = ByteLayout::Half; + const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; type ChildSliceIx = ChildSliceIx16; } @@ -213,7 +215,7 @@ pub struct NibbleQuarter; // new_padded_end merged impl NibbleOps for NibbleQuarter { - const REPR: ByteLayout = ByteLayout::Quarter; + const REPR: ByteLayout = ByteLayout::Quarter; const PADDING_BITMASK: &'static [(u8, usize)] = &[ (0b1111_1111, 6), (0b0011_1111, 4), @@ -223,7 +225,6 @@ impl NibbleOps for NibbleQuarter { type ChildSliceIx = ChildSliceIx4; } - /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. /// Nibbles are always left aligned, so making a `NibbleVec` from /// a `NibbleSlice` can get costy. @@ -304,7 +305,7 @@ pub trait ChildSliceIx: AsRef<[usize]> } } -/// Iterator over `ChildSliceIx` trait. +/// Iterator over `ChildSliceIx` trait. pub struct IterChildSliceIx<'a, CS>(&'a CS, usize, &'a[u8]); impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 751febb5..4b5c2672 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -55,10 +55,12 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { NibbleSliceIterator { p: self, i: 0 } } + /// Get nibble slice from a `NodeKey`. pub fn from_stored(i: &NodeKey) -> NibbleSlice { NibbleSlice::::new_offset(&i.1[..], i.0) } + /// Helper function to create a owned `NodeKey` from this `NibbleSlice`. pub fn to_stored(&self) -> NodeKey { let split = self.offset / N::NIBBLE_PER_BYTE; @@ -129,9 +131,9 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// Do we start with the same nibbles as the whole of `them`? - pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } + pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } - /// How many of the same nibbles at the beginning do we match with `them`? + /// How many of the same nibbles at the beginning do we match with `them`? pub fn common_prefix(&self, them: &Self) -> usize { let s = min(self.len(), them.len()); let mut i = 0usize; diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 8d18f90e..6b57cccc 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -64,7 +64,7 @@ impl NibbleVec { } self.len += 1; } - + /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. pub fn pop(&mut self) -> Option { if self.is_empty() { @@ -153,26 +153,26 @@ impl NibbleVec { } /// Utility function for chaining two optional appending - /// of `NibbleSlice` and/or a byte. - /// Can be slow. + /// of `NibbleSlice` and/or a byte. + /// Can be slow. pub(crate) fn append_slice_nibble( &mut self, o_sl: Option<&NibbleSlice>, o_ix: Option, ) -> usize { let mut res = 0; - if let Some(sl) = o_sl { + if let Some(sl) = o_sl { self.append_partial(sl.right()); res += sl.len(); } - if let Some(ix) = o_ix { + if let Some(ix) = o_ix { self.push(ix); res += 1; } res } /// Utility function for `append_slice_nibble` after a clone. - /// Can be slow. + /// Can be slow. pub(crate) fn clone_append_slice_nibble( &self, o_sl: Option<&NibbleSlice>, @@ -269,7 +269,7 @@ mod tests { #[test] fn drop_lasts_test() { - let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { + let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { let mut k = NibbleVec::::new(); for v in a { k.push(*v); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index db1e9764..7f1cf190 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -27,7 +27,7 @@ pub type NodeKey = (usize, ElasticArray36); /// Alias to branch children slice, it is equivalent to '&[&[u8]]'. /// Reason for using it is https://github.com/rust-lang/rust/issues/43408. -pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); +pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Clone)] From 5e1126a1d93a1d3dedd5dab6b55327e8f6480798 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Jul 2019 11:54:58 +0200 Subject: [PATCH 099/120] ChildBitmap renamed to more generic BitMap. --- test-support/reference-trie/src/lib.rs | 12 ++++++------ trie-db/src/lib.rs | 2 +- trie-db/src/node_codec.rs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index b0605e07..d7158887 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -34,7 +34,7 @@ use trie_db::{ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, ChildBitmap, +pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, ChildSliceIx}; pub use trie_db::{Record, TrieLayOut, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; @@ -92,7 +92,7 @@ impl TrieOps for LayoutNewQuarter { } /// bitmap codec for radix 16 pub struct BitMap16(u16); -impl ChildBitmap for BitMap16 { +impl BitMap for BitMap16 { const ENCODED_LEN: usize = 2; type Error = ReferenceError; type Buff = [u8;3]; // need a byte for header @@ -122,7 +122,7 @@ impl ChildBitmap for BitMap16 { /// bitmap codec for radix 4 pub struct BitMap4(u8); -impl ChildBitmap for BitMap4 { +impl BitMap for BitMap4 { const ENCODED_LEN: usize = 1; type Error = ReferenceError; type Buff = [u8;2]; // need a byte for header @@ -277,7 +277,7 @@ fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8 res } -fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { +fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { @@ -645,7 +645,7 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< impl< H: Hasher, N: NibbleOps, - BM: ChildBitmap + BM: BitMap > NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; @@ -772,7 +772,7 @@ impl< impl< H: Hasher, N: NibbleOps, - BM: ChildBitmap + BM: BitMap > NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index e42ae4ab..1310ae59 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -96,7 +96,7 @@ pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, ChildSliceIx}; -pub use node_codec::{NodeCodec, Partial, ChildBitmap}; +pub use node_codec::{NodeCodec, Partial, BitMap}; pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 34e65506..8adc4af4 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -96,9 +96,9 @@ pub trait NodeCodec: Sized { ) -> Vec; } -/// Child bitmap encoder that is related to the trie number of children. +/// Bitmap encoder for the number of children nodes. /// This would be useless with https://github.com/rust-lang/rust/issues/43408. -pub trait ChildBitmap: Sized { +pub trait BitMap: Sized { /// length to encode the bitmap const ENCODED_LEN: usize; /// Codec error type. From c8e66cde1c52904fb3a9bad2a6548aa634858d17 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Jul 2019 12:03:52 +0200 Subject: [PATCH 100/120] Renaming of LayOut to Layout. --- test-support/reference-trie/src/lib.rs | 10 +++++----- test-support/trie-bench/src/lib.rs | 6 +++--- trie-db/src/fatdb.rs | 16 ++++++++-------- trie-db/src/fatdbmut.rs | 8 ++++---- trie-db/src/iter_build.rs | 10 +++++----- trie-db/src/lib.rs | 22 +++++++++++----------- trie-db/src/lookup.rs | 6 +++--- trie-db/src/sectriedb.rs | 8 ++++---- trie-db/src/sectriedbmut.rs | 8 ++++---- trie-db/src/triedb.rs | 22 +++++++++++----------- trie-db/src/triedbmut.rs | 14 +++++++------- 11 files changed, 65 insertions(+), 65 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index d7158887..a669f905 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -36,13 +36,13 @@ use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, ChildSliceIx}; -pub use trie_db::{Record, TrieLayOut, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; +pub use trie_db::{Record, TrieLayout, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; /// trie layout similar to parity-ethereum pub struct LayoutOri; -impl TrieLayOut for LayoutOri { +impl TrieLayout for LayoutOri { const USE_EXTENSION: bool = true; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodec; @@ -55,7 +55,7 @@ impl TrieOps for LayoutOri { } /// trie layout similar to substrate one pub struct LayoutNew; -impl TrieLayOut for LayoutNew { +impl TrieLayout for LayoutNew { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; @@ -66,7 +66,7 @@ impl TrieLayOut for LayoutNew { /// trie layout similar to substrate one pub struct LayoutNewH(PhantomData); -impl TrieLayOut for LayoutNewH { +impl TrieLayout for LayoutNewH { const USE_EXTENSION: bool = false; type H = H; type C = ReferenceNodeCodecNoExt; @@ -79,7 +79,7 @@ impl TrieOps for LayoutNewH { } /// Test quarter nibble pub struct LayoutNewQuarter; -impl TrieLayOut for LayoutNewQuarter { +impl TrieLayout for LayoutNewQuarter { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index e28cf662..b1813989 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -19,7 +19,7 @@ use criterion::{Criterion, black_box, Fun}; use keccak_hasher::KeccakHasher; use hash_db::Hasher; use memory_db::{MemoryDB, HashKey}; -use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut, TrieLayOut, TrieHash}; +use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut, TrieLayout, TrieHash}; use std::default::Default; use trie_root::{TrieStream, trie_root}; use trie_standardmap::*; @@ -37,7 +37,7 @@ impl ::std::fmt::Debug for TrieInsertionList { } } -fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) +fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) where ::Out: 'static { @@ -100,7 +100,7 @@ fn random_value(seed: &mut ::Out) -> Vec { } } -pub fn standard_benchmark(b: &mut Criterion, name: &str) { +pub fn standard_benchmark(b: &mut Criterion, name: &str) { // Typical ethereum transaction payload passing through `verify_block_integrity()` close to block #6317032; // 140 iteams, avg length 157bytes, total 22033bytes payload (expected root: 0xc1382bbef81d10a41d325e2873894b61162fb1e6167cafc663589283194acfda) diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 32a85162..231bae31 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -14,7 +14,7 @@ use hash_db::{HashDBRef, Hasher}; use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, - TrieLayOut, CError, TrieHash}; + TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -25,14 +25,14 @@ use alloc::boxed::Box; /// Use it as a `Trie` or `TrieMut` trait object. pub struct FatDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { raw: TrieDB<'db, L>, } impl<'db, L> FatDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. @@ -50,7 +50,7 @@ where impl<'db, L> Trie for FatDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&self) -> &TrieHash { self.raw.root() } @@ -76,7 +76,7 @@ where /// Itarator over inserted pairs of key values. pub struct FatDBIterator<'db, L> where - L: TrieLayOut, + L: TrieLayout, { trie_iterator: TrieDBIterator<'db, L>, trie: &'db TrieDB<'db, L>, @@ -84,7 +84,7 @@ where impl<'db, L> FatDBIterator<'db, L> where - L: TrieLayOut, + L: TrieLayout, { /// Creates new iterator. pub fn new(trie: &'db TrieDB) -> Result, CError> { @@ -97,7 +97,7 @@ where impl<'db, L> TrieIterator for FatDBIterator<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { let hashed_key = L::H::hash(key); @@ -107,7 +107,7 @@ where impl<'db, L> Iterator for FatDBIterator<'db, L> where - L: TrieLayOut, + L: TrieLayout, { type Item = TrieItem<'db, TrieHash, CError>; diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index e8fa4513..ab902431 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayOut, TrieHash, CError}; +use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. @@ -21,14 +21,14 @@ use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayOut, TrieHash, CError}; /// Use it as a `Trie` or `TrieMut` trait object. pub struct FatDBMut<'db, L> where - L: TrieLayOut, + L: TrieLayout, { raw: TrieDBMut<'db, L>, } impl<'db, L> FatDBMut<'db, L> where - L: TrieLayOut, + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. @@ -60,7 +60,7 @@ where impl<'db, L> TrieMut for FatDBMut<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&mut self) -> &TrieHash { self.raw.root() } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index aad96a9d..2be7acd7 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -24,7 +24,7 @@ use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::NibbleOps; use node_codec::NodeCodec; -use crate::{TrieLayOut, TrieHash}; +use crate::{TrieLayout, TrieHash}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; @@ -70,7 +70,7 @@ impl CacheBuilder for Cache4 { } } -type ArrayNode = <::CB as CacheBuilder>>::AN; +type ArrayNode = <::CB as CacheBuilder>>::AN; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index @@ -80,14 +80,14 @@ type ArrayNode = <::CB as CacheBuilder>>::AN; /// Note that it is not memory optimal (all depth are allocated even if some are empty due /// to node partial). /// Three field are used, a cache over the children, an optional associated value and the depth. -struct CacheAccum (Vec<(ArrayNode, Option, usize)>,PhantomData); +struct CacheAccum (Vec<(ArrayNode, Option, usize)>,PhantomData); /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; impl CacheAccum where - T: TrieLayOut, + T: TrieLayout, V: AsRef<[u8]>, { @@ -285,7 +285,7 @@ where /// branch to root), this differs form key ordering a bit. pub fn trie_visit(input: I, cb_ext: &mut F) where - T: TrieLayOut, + T: TrieLayout, I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 1310ae59..7a7a8f9c 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -189,7 +189,7 @@ impl<'a, F, T, H: Hasher> Query for (&'a mut Recorder, F) where F: Fn } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait Trie { +pub trait Trie { /// Return the root of the trie. fn root(&self) -> &TrieHash; @@ -226,7 +226,7 @@ pub trait Trie { } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait TrieMut { +pub trait TrieMut { /// Return the root of the trie. fn root(&mut self) -> &TrieHash; @@ -254,7 +254,7 @@ pub trait TrieMut { } /// A trie iterator that also supports random access (`seek()`). -pub trait TrieIterator: Iterator { +pub trait TrieIterator: Iterator { /// Position the iterator on the first element with key >= `key` fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError>; } @@ -279,14 +279,14 @@ impl Default for TrieSpec { /// Trie factory. #[derive(Default, Clone)] -pub struct TrieFactory { +pub struct TrieFactory { spec: TrieSpec, layout: L, } /// All different kinds of tries. /// This is used to prevent a heap allocation for every created trie. -pub enum TrieKinds<'db, L: TrieLayOut> { +pub enum TrieKinds<'db, L: TrieLayout> { /// A generic trie db. Generic(TrieDB<'db, L>), /// A secure trie db. @@ -306,7 +306,7 @@ macro_rules! wrapper { } } -impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { +impl<'db, L: TrieLayout> Trie for TrieKinds<'db, L> { fn root(&self) -> &TrieHash { wrapper!(self, root,) } @@ -339,7 +339,7 @@ impl<'db, L: TrieLayOut> Trie for TrieKinds<'db, L> { impl<'db, L> TrieFactory where - L: TrieLayOut + 'db, + L: TrieLayout + 'db, { /// Creates new factory. pub fn new(spec: TrieSpec, layout: L) -> Self { @@ -392,7 +392,7 @@ where /// Trait with definition of trie layout. /// Contains all associated trait needed for /// a trie definition or implementation. -pub trait TrieLayOut { +pub trait TrieLayout { /// If true, the trie will use extension nodes and /// no partial in branch, if false the trie will only /// use branch and node with partials in both. @@ -412,7 +412,7 @@ pub trait TrieLayOut { /// It associates trie definition with chosen methods. /// This trait contains its own default implementations /// and exists only to allow alternate algorithm usage. -pub trait TrieOps: Sized + TrieLayOut { +pub trait TrieOps: Sized + TrieLayout { /// Operation to build a trie db from its ordered iterator over its key/values. fn trie_build(db: &mut DB, input: I) -> ::Out where DB: HashDB, @@ -466,6 +466,6 @@ pub trait TrieOps: Sized + TrieLayOut { } /// Alias accessor to hasher hash output type from a `TrieLayout`. -pub type TrieHash = <::H as Hasher>::Out; +pub type TrieHash = <::H as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. -pub type CError = <::C as NodeCodec<::H, ::N>>::Error; +pub type CError = <::C as NodeCodec<::H, ::N>>::Error; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index b99ca2f7..8d5a8d9f 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -18,13 +18,13 @@ use hash_db::HashDBRef; use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query, TrieLayOut, CError, TrieHash, ChildSliceIx}; +use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash, ChildSliceIx}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; /// Trie lookup helper object. -pub struct Lookup<'a, L: TrieLayOut, Q: Query> { +pub struct Lookup<'a, L: TrieLayout, Q: Query> { /// database to query from. pub db: &'a dyn HashDBRef, /// Query object to record nodes and transform data. @@ -35,7 +35,7 @@ pub struct Lookup<'a, L: TrieLayOut, Q: Query> { impl<'a, L, Q> Lookup<'a, L, Q> where - L: TrieLayOut, + L: TrieLayout, Q: Query, { /// Look up the given key. If the value is found, it will be passed to the given diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 7a76c355..5b300482 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -14,7 +14,7 @@ use hash_db::{HashDBRef, Hasher}; use super::triedb::TrieDB; -use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayOut, CError, TrieHash}; +use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -24,14 +24,14 @@ use alloc::boxed::Box; /// Use it as a `Trie` trait object. You can use `raw()` to get the backing `TrieDB` object. pub struct SecTrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { raw: TrieDB<'db, L> } impl<'db, L> SecTrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// @@ -58,7 +58,7 @@ where impl<'db, L> Trie for SecTrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&self) -> &TrieHash { self.raw.root() } diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index 40607206..8cbc6c9c 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -13,21 +13,21 @@ // limitations under the License. use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayOut, TrieHash, CError}; +use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// /// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object. pub struct SecTrieDBMut<'db, L> where - L: TrieLayOut + L: TrieLayout { raw: TrieDBMut<'db, L> } impl<'db, L> SecTrieDBMut<'db, L> where - L: TrieLayOut + L: TrieLayout { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. @@ -55,7 +55,7 @@ where impl<'db, L> TrieMut for SecTrieDBMut<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&mut self) -> &TrieHash { self.raw.root() diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 6bc1604e..76b4c013 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -17,7 +17,7 @@ use nibble::{NibbleSlice, NibbleOps, ChildSliceIx}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; -use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayOut, CError, TrieHash}; +use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayout, CError, TrieHash}; use super::nibble::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; @@ -64,7 +64,7 @@ use alloc::vec::Vec; /// ``` pub struct TrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { db: &'db dyn HashDBRef, root: &'db TrieHash, @@ -74,7 +74,7 @@ where impl<'db, L> TrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist @@ -122,7 +122,7 @@ where impl<'db, L> Trie for TrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&self) -> &TrieHash { self.root } @@ -154,7 +154,7 @@ where // This is for pretty debug output only struct TrieAwareDebugNode<'db, 'a, L> where - L: TrieLayOut, + L: TrieLayout, { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], @@ -165,7 +165,7 @@ where #[cfg(feature="std")] impl<'db, 'a, L> fmt::Debug for TrieAwareDebugNode<'db, 'a, L> where - L: TrieLayOut, + L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { @@ -250,7 +250,7 @@ where #[cfg(feature="std")] impl<'db, L> fmt::Debug for TrieDB<'db, L> where - L: TrieLayOut, + L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let root_rlp = self.root_data().unwrap(); @@ -300,13 +300,13 @@ impl Crumb { } /// Iterator for going through all values in the trie. -pub struct TrieDBIterator<'a, L: TrieLayOut> { +pub struct TrieDBIterator<'a, L: TrieLayout> { db: &'a TrieDB<'a, L>, trail: Vec>, key_nibbles: NibbleVec, } -impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { +impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: NibbleVec::new() }; @@ -452,7 +452,7 @@ impl<'a, L: TrieLayOut> TrieDBIterator<'a, L> { } -impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { +impl<'a, L: TrieLayout> TrieIterator for TrieDBIterator<'a, L> { /// Position the iterator on the first element with key >= `key` fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { self.trail.clear(); @@ -462,7 +462,7 @@ impl<'a, L: TrieLayOut> TrieIterator for TrieDBIterator<'a, L> { } } -impl<'a, L: TrieLayOut> Iterator for TrieDBIterator<'a, L> { +impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { type Item = TrieItem<'a, TrieHash, CError>; fn next(&mut self) -> Option { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index eebb593f..1f43fa83 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -14,7 +14,7 @@ //! In-memory trie representation. -use super::{Result, TrieError, TrieMut, TrieLayOut, TrieHash, CError}; +use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; use super::lookup::Lookup; use super::node::Node as EncodedNode; use node_codec::NodeCodec; @@ -341,7 +341,7 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// ``` pub struct TrieDBMut<'a, L> where - L: TrieLayOut, + L: TrieLayout, { storage: NodeStorage>, db: &'a mut dyn HashDB, @@ -355,7 +355,7 @@ where impl<'a, L> TrieDBMut<'a, L> where - L: TrieLayOut, + L: TrieLayout, { /// Create a new trie with backing database `db` and empty `root`. pub fn new(db: &'a mut dyn HashDB, root: &'a mut TrieHash) -> Self { @@ -1318,7 +1318,7 @@ where impl<'a, L> TrieMut for TrieDBMut<'a, L> where - L: TrieLayOut, + L: TrieLayout, { fn root(&mut self) -> &TrieHash { self.commit(); @@ -1392,7 +1392,7 @@ where impl<'a, L> Drop for TrieDBMut<'a, L> where - L: TrieLayOut, + L: TrieLayout, { fn drop(&mut self) { self.commit(); @@ -1424,7 +1424,7 @@ mod tests { use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayOut, NodeCodec, + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayout, NodeCodec, ReferenceNodeCodec, ref_trie_root, ref_trie_root_no_ext, LayoutOri, BitMap16}; @@ -1472,7 +1472,7 @@ mod tests { fn reference_hashed_null_node() -> ::Out { - as NodeCodec::N>>::hashed_null_node() + as NodeCodec::N>>::hashed_null_node() } #[test] From bb2588ee3d2f1d81abe39082e3996793c1e37c24 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 22 Jul 2019 14:55:08 +0200 Subject: [PATCH 101/120] replace _ix with _index, bm with bitmap, and Ix with Index. --- test-support/reference-trie/src/lib.rs | 102 +++++++++++++------------ trie-db/src/iter_build.rs | 4 +- trie-db/src/lib.rs | 2 +- trie-db/src/lookup.rs | 2 +- trie-db/src/nibble/mod.rs | 28 +++---- trie-db/src/nibble/nibbleslice.rs | 2 +- trie-db/src/nibble/nibblevec.rs | 18 ++--- trie-db/src/node.rs | 20 ++--- trie-db/src/node_codec.rs | 8 +- trie-db/src/triedb.rs | 2 +- trie-db/src/triedbmut.rs | 19 +++-- 11 files changed, 106 insertions(+), 101 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index a669f905..283074cf 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -35,7 +35,7 @@ use std::borrow::Borrow; use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, - ChildSliceIx}; + ChildSliceIndex}; pub use trie_db::{Record, TrieLayout, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; @@ -277,14 +277,14 @@ fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8 res } -fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { +fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE }; dest[0] = first; - BM::encode(has_children, &mut dest[1..]); + BITMAP::encode(has_children, &mut dest[1..]); } fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { @@ -379,8 +379,8 @@ impl TrieStream for ReferenceTrieStreamNoExt { } else { self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchNoValue)); } - let bm = branch_node_bit_mask(has_children); - self.buffer.extend([bm.0,bm.1].iter()); + let bitmap = branch_node_bit_mask(has_children); + self.buffer.extend([bitmap.0, bitmap.1].iter()); } else { // should not happen self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); @@ -584,12 +584,12 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { } fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { - let nb_nibble_hpe = (partial.0).0 as usize; - let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; + let number_nibble_encoded = (partial.0).0 as usize; + let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if nb_nibble_hpe > 0 { - output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); + if number_nibble_encoded > 0 { + output.push(N::masked_right(number_nibble_encoded as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -620,8 +620,8 @@ fn partial_enc_it>(partial: I, nibble_count fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec { - let nb_nibble_hpe = (partial.0).0 as usize; - let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + nb_nibble_hpe; + let number_nibble_encoded = (partial.0).0 as usize; + let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); @@ -631,8 +631,8 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; - if nb_nibble_hpe > 0 { - output.push(N::masked_right(nb_nibble_hpe as u8, (partial.0).1)); + if number_nibble_encoded > 0 { + output.push(N::masked_right(number_nibble_encoded as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -645,8 +645,8 @@ fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec< impl< H: Hasher, N: NibbleOps, - BM: BitMap -> NodeCodec for ReferenceNodeCodec { + BITMAP: BitMap +> NodeCodec for ReferenceNodeCodec { type Error = ReferenceError; fn hashed_null_node() -> ::Out { @@ -658,8 +658,8 @@ impl< match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - let bm_slice = take(input, BM::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; - let bitmap = BM::decode(&bm_slice[..])?; + let bitmap_slice = take(input, BITMAP::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap = BITMAP::decode(&bitmap_slice[..])?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; @@ -667,7 +667,7 @@ impl< } else { None }; - let mut children: N::ChildSliceIx = Default::default(); + let mut children: N::ChildSliceIndex = Default::default(); let child_val = &**input; let mut ix = 0; children.as_mut()[0] = ix; @@ -675,7 +675,7 @@ impl< if bitmap.value_at(i) { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; let _ = take(input, count); - ix += count + N::ChildSliceIx::CONTENT_HEADER_SIZE; + ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; } children.as_mut()[i + 1] = ix; } @@ -685,7 +685,7 @@ impl< let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::nb_padding(nibble_count)); + N::number_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -693,7 +693,7 @@ impl< let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::nb_padding(nibble_count)); + N::number_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -724,8 +724,8 @@ impl< output } - fn ext_node(partial: impl Iterator, nb_nibble: usize, child: ChildReference<::Out>) -> Vec { - let mut output = partial_to_key_it::(partial, nb_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + fn ext_node(partial: impl Iterator, number_nibble: usize, child: ChildReference<::Out>) -> Vec { + let mut output = partial_to_key_it::(partial, number_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -736,15 +736,15 @@ impl< fn branch_node( children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { - let mut output = vec![0; BM::ENCODED_LEN + 1]; - let mut prefix: BM::Buff = Default::default(); + let mut output = vec![0; BITMAP::ENCODED_LEN + 1]; + let mut prefix: BITMAP::Buff = Default::default(); let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); true } else { false }; - branch_node_buf::(have_value, children.map(|maybe_child| match maybe_child.borrow() { + branch_node_buf::(have_value, children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -755,13 +755,13 @@ impl< } None => false, }), prefix.as_mut()); - output[0..BM::ENCODED_LEN + 1].copy_from_slice(prefix.as_ref()); + output[0..BITMAP::ENCODED_LEN + 1].copy_from_slice(prefix.as_ref()); output } fn branch_node_nibbled( _partial: impl Iterator, - _nb_nibble: usize, + _number_nibble: usize, _children: impl Iterator::Out>>>>, _maybe_value: Option<&[u8]>) -> Vec { unreachable!() @@ -772,8 +772,8 @@ impl< impl< H: Hasher, N: NibbleOps, - BM: BitMap -> NodeCodec for ReferenceNodeCodecNoExt { + BITMAP: BitMap +> NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; fn hashed_null_node() -> ::Out { @@ -786,23 +786,24 @@ impl< match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { - let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && N::masked_left((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, input[0]) != 0 { + let number_nibble_encoded = nibble_count % N::NIBBLE_PER_BYTE; + if number_nibble_encoded > 0 + && N::masked_left((N::NIBBLE_PER_BYTE - number_nibble_encoded) as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::nb_padding(nibble_count)); - let bm_slice = take(input, BM::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; - let bitmap = BM::decode(&bm_slice[..])?; + N::number_padding(nibble_count)); + let bitmap_slice = take(input, BITMAP::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap = BITMAP::decode(&bitmap_slice[..])?; let value = if has_value { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) } else { None }; - let mut children: N::ChildSliceIx = Default::default(); + let mut children: N::ChildSliceIndex = Default::default(); let child_val = &**input; let mut ix = 0; children.as_mut()[0] = ix; @@ -810,21 +811,21 @@ impl< if bitmap.value_at(i) { let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; let _ = take(input, count); - ix += count + N::ChildSliceIx::CONTENT_HEADER_SIZE; + ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; } children.as_mut()[i + 1] = ix; } Ok(Node::NibbledBranch(nibble_slice, (children, child_val), value)) } NodeHeaderNoExt::Leaf(nibble_count) => { - let nb_nibble_hpe = nibble_count % N::NIBBLE_PER_BYTE; - if nb_nibble_hpe > 0 && N::masked_left((N::NIBBLE_PER_BYTE - nb_nibble_hpe) as u8, input[0]) != 0 { + let number_nibble_encoded = nibble_count % N::NIBBLE_PER_BYTE; + if number_nibble_encoded > 0 && N::masked_left((N::NIBBLE_PER_BYTE - number_nibble_encoded) as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) .ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::nb_padding(nibble_count)); + N::number_padding(nibble_count)); let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } @@ -832,7 +833,7 @@ impl< } fn try_decode_hash(data: &[u8]) -> Option<::Out> { - as NodeCodec>::try_decode_hash(data) + as NodeCodec>::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { @@ -866,22 +867,22 @@ impl< fn branch_node_nibbled( partial: impl Iterator, - nb_nibble: usize, + number_nibble: usize, children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { - partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchWithValue) + partial_enc_it::(partial, number_nibble, NodeKindNoExt::BranchWithValue) } else { - partial_enc_it::(partial, nb_nibble, NodeKindNoExt::BranchNoValue) + partial_enc_it::(partial, number_nibble, NodeKindNoExt::BranchNoValue) }; - let bm_ix = output.len(); - let mut bm: BM::Buff = Default::default(); - (0..BM::ENCODED_LEN).for_each(|_|output.push(0)); + let bitmap_index = output.len(); + let mut bitmap: BITMAP::Buff = Default::default(); + (0..BITMAP::ENCODED_LEN).for_each(|_|output.push(0)); if let Some(value) = maybe_value { value.encode_to(&mut output); }; - BM::encode(children.map(|maybe_child| match maybe_child.borrow() { + BITMAP::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -891,8 +892,9 @@ impl< true } None => false, - }), bm.as_mut()); - output[bm_ix..bm_ix + BM::ENCODED_LEN].copy_from_slice(&bm.as_ref()[..BM::ENCODED_LEN]); + }), bitmap.as_mut()); + output[bitmap_index..bitmap_index + BITMAP::ENCODED_LEN] + .copy_from_slice(&bitmap.as_ref()[..BITMAP::ENCODED_LEN]); output } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 2be7acd7..055862d5 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -107,7 +107,7 @@ where } #[inline(always)] - fn set_node(&mut self, depth:usize, nibble_ix:usize, node: CacheNode>) { + fn set_node(&mut self, depth:usize, nibble_index:usize, node: CacheNode>) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { self.0.push((T::CB::new_vec_slice_buff(), None, depth)); } @@ -115,7 +115,7 @@ where let last = self.0.len() - 1; debug_assert!(self.0[last].2 == depth); - self.0[last].0.as_mut()[nibble_ix] = node; + self.0[last].0.as_mut()[nibble_index] = node; } #[inline(always)] diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 7a7a8f9c..7939d173 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -95,7 +95,7 @@ pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, - ChildSliceIx}; + ChildSliceIndex}; pub use node_codec::{NodeCodec, Partial, BitMap}; pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 8d5a8d9f..59bc2e3b 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -18,7 +18,7 @@ use hash_db::HashDBRef; use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash, ChildSliceIx}; +use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash, ChildSliceIndex}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index d7d6a423..d6b37f02 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -61,7 +61,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Buffer type for slice index store (we do not include /// directly slice in it to avoid lifetime in /// trait - type ChildSliceIx: ChildSliceIx; + type ChildSliceIndex: ChildSliceIndex; /// Mask a byte from a `ix` > 0 (ix being content). @@ -115,7 +115,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy #[inline] /// Calculate the number of needed padding a array of nibble length `i`. - fn nb_padding(i: usize) -> usize { + fn number_padding(i: usize) -> usize { (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE } @@ -205,7 +205,7 @@ pub enum ByteLayout { impl NibbleOps for NibbleHalf { const REPR: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; - type ChildSliceIx = ChildSliceIx16; + type ChildSliceIndex = ChildSliceIndex16; } /// Radix 4 `NibbleOps` definition. @@ -222,7 +222,7 @@ impl NibbleOps for NibbleQuarter { (0b0000_1111, 2), (0b0000_0011, 0), ]; - type ChildSliceIx = ChildSliceIx4; + type ChildSliceIndex = ChildSliceIndex4; } /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. @@ -275,7 +275,7 @@ pub struct NibbleSliceIterator<'a, N: NibbleOps> { /// representation of a branch. /// This is use instead of `&[&[u8]]` to allow associated type /// with a constant lenght. -pub trait ChildSliceIx: AsRef<[usize]> +pub trait ChildSliceIndex: AsRef<[usize]> + AsMut<[usize]> + Default + Eq + PartialEq + crate::MaybeDebug + Clone { @@ -300,15 +300,15 @@ pub trait ChildSliceIx: AsRef<[usize]> } } /// Iterator over the children slice. - fn iter<'a>(&'a self, data: &'a [u8]) -> IterChildSliceIx<'a, Self> { - IterChildSliceIx(self, 0, data) + fn iter<'a>(&'a self, data: &'a [u8]) -> IterChildSliceIndex<'a, Self> { + IterChildSliceIndex(self, 0, data) } } -/// Iterator over `ChildSliceIx` trait. -pub struct IterChildSliceIx<'a, CS>(&'a CS, usize, &'a[u8]); +/// Iterator over `ChildSliceIndex` trait. +pub struct IterChildSliceIndex<'a, CS>(&'a CS, usize, &'a[u8]); -impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { +impl<'a, CS: ChildSliceIndex> Iterator for IterChildSliceIndex<'a, CS> { type Item = Option<&'a[u8]>; fn next(&mut self) -> Option { if self.1 == CS::NIBBLE_LEN { @@ -319,7 +319,7 @@ impl<'a, CS: ChildSliceIx> Iterator for IterChildSliceIx<'a, CS> { } } -macro_rules! child_slice_ix { +macro_rules! child_slice_index { ($me: ident, $size: expr, $pre: expr) => { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Default, Eq, PartialEq, Clone)] @@ -338,11 +338,11 @@ macro_rules! child_slice_ix { } } - impl ChildSliceIx for $me { + impl ChildSliceIndex for $me { const CONTENT_HEADER_SIZE: usize = $pre; const NIBBLE_LEN: usize = $size; } } } -child_slice_ix!(ChildSliceIx16, 16, 1); -child_slice_ix!(ChildSliceIx4, 4, 1); +child_slice_index!(ChildSliceIndex16, 16, 1); +child_slice_index!(ChildSliceIndex4, 4, 1); diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 4b5c2672..77e8cf9a 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -85,7 +85,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); let ea_offset = self.offset % N::NIBBLE_PER_BYTE; - let n_offset = N::nb_padding(nb); + let n_offset = N::number_padding(nb); let mut result = (ea_offset, ea); N::shift_key(&mut result, n_offset); result.1.pop(); diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 6b57cccc..2b9b9ecc 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -87,9 +87,9 @@ impl NibbleVec { return; } let end = self.len - n; - let end_ix = end / N::NIBBLE_PER_BYTE + let end_index = end / N::NIBBLE_PER_BYTE + if end % N::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; - (end_ix..self.inner.len()).for_each(|_|{ self.inner.pop(); }); + (end_index..self.inner.len()).for_each(|_|{ self.inner.pop(); }); self.len = end; let pos = self.len % N::NIBBLE_PER_BYTE; if pos != 0 { @@ -116,10 +116,10 @@ impl NibbleVec { let final_len = self.len + v.len; let offset = self.len % N::NIBBLE_PER_BYTE; let final_offset = final_len % N::NIBBLE_PER_BYTE; - let last_ix = self.len / N::NIBBLE_PER_BYTE; + let last_index = self.len / N::NIBBLE_PER_BYTE; if offset > 0 { let (s1, s2) = N::split_shifts(offset); - self.inner[last_ix] = N::masked_left(offset as u8, self.inner[last_ix]) | (v.inner[0] >> s2); + self.inner[last_index] = N::masked_left(offset as u8, self.inner[last_index]) | (v.inner[0] >> s2); (0..v.inner.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); if final_offset > 0 { self.inner.push(v.inner[v.inner.len() - 1] << s1); @@ -158,14 +158,14 @@ impl NibbleVec { pub(crate) fn append_slice_nibble( &mut self, o_sl: Option<&NibbleSlice>, - o_ix: Option, + o_index: Option, ) -> usize { let mut res = 0; if let Some(sl) = o_sl { self.append_partial(sl.right()); res += sl.len(); } - if let Some(ix) = o_ix { + if let Some(ix) = o_index { self.push(ix); res += 1; } @@ -175,11 +175,11 @@ impl NibbleVec { /// Can be slow. pub(crate) fn clone_append_slice_nibble( &self, - o_sl: Option<&NibbleSlice>, - o_ix: Option, + o_slice: Option<&NibbleSlice>, + o_index: Option, ) -> Self { let mut p = self.clone(); - p.append_slice_nibble(o_sl, o_ix); + p.append_slice_nibble(o_slice, o_index); p } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 7f1cf190..9bb1647d 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -14,7 +14,7 @@ use elastic_array::ElasticArray36; use nibble::NibbleSlice; -use nibble::{NibbleOps, ChildSliceIx}; +use nibble::{NibbleOps, ChildSliceIndex}; use nibble::NibbleVec; use super::DBValue; @@ -27,7 +27,7 @@ pub type NodeKey = (usize, ElasticArray36); /// Alias to branch children slice, it is equivalent to '&[&[u8]]'. /// Reason for using it is https://github.com/rust-lang/rust/issues/43408. -pub type BranchChildrenSlice<'a, N> = (::ChildSliceIx, &'a[u8]); +pub type BranchChildrenSlice<'a, N> = (::ChildSliceIndex, &'a[u8]); /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Clone)] @@ -50,8 +50,8 @@ pub enum Node<'a, N: NibbleOps> { #[derive(Eq, PartialEq, Clone)] pub struct Branch { data: Vec, - data_ix: usize, - ubounds_ix: usize, + data_index: usize, + ubounds_index: usize, child_head: usize, } @@ -61,8 +61,8 @@ impl Branch { maybe_value: Option<&[u8]>, ) -> Self { let mut data: Vec = children_slice.1.into(); - let data_ix = data.len(); - let ubounds_ix = data_ix + maybe_value.map(|v|{ + let data_index = data.len(); + let ubounds_index = data_index + maybe_value.map(|v|{ data.extend_from_slice(v); v.len() }).unwrap_or(0); @@ -71,13 +71,13 @@ impl Branch { i += 1; data.extend_from_slice(&ix.to_ne_bytes()[..]); } - Branch { data, data_ix, ubounds_ix, child_head: N::ChildSliceIx::CONTENT_HEADER_SIZE } + Branch { data, data_index, ubounds_index, child_head: N::ChildSliceIndex::CONTENT_HEADER_SIZE } } /// Get the node value, if any. pub fn get_value(&self) -> Option<&[u8]> { if self.has_value() { - Some(&self.data[self.data_ix..self.ubounds_ix]) + Some(&self.data[self.data_index..self.ubounds_index]) } else { None } @@ -85,14 +85,14 @@ impl Branch { /// Test if the node has a value. pub fn has_value(&self) -> bool { - self.data_ix < self.ubounds_ix + self.data_index < self.ubounds_index } fn index_bound(&self, index: usize) -> Option { use core_::convert::TryInto; use core_::mem; let usize_len = mem::size_of::(); - let s = self.ubounds_ix + index * usize_len; + let s = self.ubounds_index + index * usize_len; let e = s + usize_len; if self.data.len() < e { None diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 8adc4af4..f94df32c 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -70,12 +70,12 @@ pub trait NodeCodec: Sized { fn leaf_node(partial: Partial, value: &[u8]) -> Vec; /// Returns an encoded extension node - /// Note that nb_nibble is the number of element of the iterator + /// Note that number_nibble is the number of element of the iterator /// it can possibly be obtain by `Iterator` `size_hint`, but /// for simplicity it is used directly as a parameter. fn ext_node( partial: impl Iterator, - nb_nibble: usize, + number_nibble: usize, child_ref: ChildReference, ) -> Vec; @@ -87,10 +87,10 @@ pub trait NodeCodec: Sized { ) -> Vec; /// Returns an encoded branch node with a possible partial path. - /// `nb_nibble` is the partial path lenghth as in `ext_node`. + /// `number_nibble` is the partial path lenghth as in `ext_node`. fn branch_node_nibbled( partial: impl Iterator, - nb_nibble: usize, + number_nibble: usize, children: impl Iterator>>>, value: Option<&[u8]> ) -> Vec; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 76b4c013..eb73c603 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; -use nibble::{NibbleSlice, NibbleOps, ChildSliceIx}; +use nibble::{NibbleSlice, NibbleOps, ChildSliceIndex}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 1f43fa83..e60bb012 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -21,7 +21,7 @@ use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; -use nibble::{NibbleVec, NibbleSlice, NibbleOps, ChildSliceIx, IterChildSliceIx}; +use nibble::{NibbleVec, NibbleSlice, NibbleOps, ChildSliceIndex, IterChildSliceIndex}; use elastic_array::ElasticArray36; use ::core_::mem; use ::core_::ops::Index; @@ -124,8 +124,11 @@ where where N: NibbleOps, C: NodeCodec, H: Hasher, { - let dec_children = |encoded_children: IterChildSliceIx, storage: &'b mut NodeStorage| { - let mut res = Vec::with_capacity(N::ChildSliceIx::NIBBLE_LEN); + let dec_children = | + encoded_children: IterChildSliceIndex, + storage: &'b mut NodeStorage, + | { + let mut res = Vec::with_capacity(N::ChildSliceIndex::NIBBLE_LEN); encoded_children.for_each(|o_data|{ let v = o_data.map(|data|Self::inline_or_hash::(data, db, storage)); res.push(v) @@ -199,7 +202,7 @@ where .map(Option::take) .enumerate() .map(|(i, maybe_child)|{ - //let branch_ix = [i as u8]; + //let branch_index = [i as u8]; maybe_child.map(|child| { let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); child_cb(child, Some(&pr), Some(i as u8)) @@ -1243,8 +1246,8 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_sl, o_ix| { - let mov = k.append_slice_nibble(o_sl, o_ix); + let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_slice, o_index| { + let mov = k.append_slice_nibble(o_slice, o_index); let cr = self.commit_child(child, &mut k); k.drop_lasts(mov); cr @@ -1281,8 +1284,8 @@ where Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { let encoded = { - let commit_child = |node_handle, o_sl: Option<&NibbleSlice>, o_ix: Option| { - let mov = prefix.append_slice_nibble(o_sl, o_ix); + let commit_child = |node_handle, o_slice: Option<&NibbleSlice>, o_index: Option| { + let mov = prefix.append_slice_nibble(o_slice, o_index); let cr = self.commit_child(node_handle, prefix); prefix.drop_lasts(mov); cr From 202bc74fdd7eb6d6f016d702a0a11e079c75b9c4 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 14:45:30 +0200 Subject: [PATCH 102/120] Removing padding length from prefix. Comment changes. --- hash-db/src/lib.rs | 6 +++--- memory-db/src/lib.rs | 3 ++- trie-db/src/iter_build.rs | 5 ----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index b8300215..70ca2acd 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -39,9 +39,9 @@ pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0,0)); /// The prefix of a trie node, a prefix is the nibble path up to /// the node in the trie. -/// For a value node, it is the node key without the partial -/// bytes (part of the key encoded in the node). -/// As the leftmost portion of a byte array, its internal representation +/// For a value node, it is the node key with its node partial +/// bytes removed (the node key can be split into prefix and node partial). +/// As the leftmost portion of the node key, its internal representation /// is a byte slice followed by a padded byte. /// The padded byte is a pair of u8 containing the number of nibble first, /// and the left aligned padded value second. diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index bd315394..426fe9c7 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -174,7 +174,6 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { prefixed_key.extend_from_slice(prefix.0); if (prefix.1).0 > 0 { prefixed_key.push((prefix.1).1); - prefixed_key.push((prefix.1).0); // put size to avoid any possible collision } prefixed_key.extend_from_slice(key.as_ref()); prefixed_key @@ -215,6 +214,8 @@ fn test_ordered_prefixed_key() { /// Parameterize the size of successive chunks when using /// `ordered_prefixed_key`. +/// Chunks are successive fix length byte arrays containing the key. +/// Last chunk is padded with 0 values. /// Length of each chunk is minimum 1, maximum 254. pub trait OrderedPrefixedKeyChunk: IntoIterator + Clone + MaybeDebug + Default { /// Estimate a final length given a input length. diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 055862d5..92519030 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -626,11 +626,6 @@ mod test { let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_impl_no_ext_unordered(data, memdb, hashdb); } -/* fn compare_impl_no_ext_unordered_rem(data: Vec<(Vec,Vec)>, rem: &[(usize,usize)]) { - let memdb = MemoryDB::default(); - let hashdb = MemoryDB::::default(); - reference_trie::compare_impl_no_ext_unordered_rem(data, rem, memdb, hashdb); - }*/ fn compare_no_ext_insert_remove(data: Vec<(bool, Vec,Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); reference_trie::compare_no_ext_insert_remove(data, memdb); From 0de2f851b4da2c0857720388eab20cf915527634 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 14:54:59 +0200 Subject: [PATCH 103/120] Renamings. --- test-support/reference-trie/benches/bench.rs | 2 +- test-support/reference-trie/src/lib.rs | 80 ++++++++++---------- trie-db/src/node_codec.rs | 2 +- trie-db/src/triedbmut.rs | 4 +- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/test-support/reference-trie/benches/bench.rs b/test-support/reference-trie/benches/bench.rs index e8868c88..943f2eee 100644 --- a/test-support/reference-trie/benches/bench.rs +++ b/test-support/reference-trie/benches/bench.rs @@ -24,7 +24,7 @@ extern crate trie_bench; fn benchmark(c: &mut Criterion) { trie_bench::standard_benchmark::< - reference_trie::LayoutOri, + reference_trie::ExtensionLayout, reference_trie::ReferenceTrieStream, >(c, "ref"); } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 283074cf..79c6e240 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -39,10 +39,10 @@ pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, pub use trie_db::{Record, TrieLayout, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; -/// trie layout similar to parity-ethereum -pub struct LayoutOri; +/// trie layout using extension nodes. +pub struct ExtensionLayout; -impl TrieLayout for LayoutOri { +impl TrieLayout for ExtensionLayout { const USE_EXTENSION: bool = true; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodec; @@ -50,12 +50,12 @@ impl TrieLayout for LayoutOri { type CB = Cache16; } -impl TrieOps for LayoutOri { } +impl TrieOps for ExtensionLayout { } /// trie layout similar to substrate one -pub struct LayoutNew; +pub struct NoExtensionLayout; -impl TrieLayout for LayoutNew { +impl TrieLayout for NoExtensionLayout { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; @@ -64,9 +64,9 @@ impl TrieLayout for LayoutNew { } /// trie layout similar to substrate one -pub struct LayoutNewH(PhantomData); +pub struct SimpleNoExtensionLayout(PhantomData); -impl TrieLayout for LayoutNewH { +impl TrieLayout for SimpleNoExtensionLayout { const USE_EXTENSION: bool = false; type H = H; type C = ReferenceNodeCodecNoExt; @@ -74,12 +74,12 @@ impl TrieLayout for LayoutNewH { type CB = Cache16; } -impl TrieOps for LayoutNewH { } +impl TrieOps for SimpleNoExtensionLayout { } /// Test quarter nibble -pub struct LayoutNewQuarter; +pub struct NoExtensionLayoutQuarter; -impl TrieLayout for LayoutNewQuarter { +impl TrieLayout for NoExtensionLayoutQuarter { const USE_EXTENSION: bool = false; type H = keccak_hasher::KeccakHasher; type C = ReferenceNodeCodecNoExt; @@ -87,7 +87,7 @@ impl TrieLayout for LayoutNewQuarter { type CB = Cache4; } -impl TrieOps for LayoutNewQuarter { } +impl TrieOps for NoExtensionLayoutQuarter { } /// bitmap codec for radix 16 pub struct BitMap16(u16); @@ -95,7 +95,7 @@ pub struct BitMap16(u16); impl BitMap for BitMap16 { const ENCODED_LEN: usize = 2; type Error = ReferenceError; - type Buff = [u8;3]; // need a byte for header + type Buffer = [u8;3]; // need a byte for header fn decode(data: &[u8]) -> Result { u16::decode(&mut &data[..]) @@ -125,7 +125,7 @@ pub struct BitMap4(u8); impl BitMap for BitMap4 { const ENCODED_LEN: usize = 1; type Error = ReferenceError; - type Buff = [u8;2]; // need a byte for header + type Buffer = [u8;2]; // need a byte for header fn decode(data: &[u8]) -> Result { if data.len() == 0 || data[0] & 0xf0 != 0 { @@ -151,19 +151,19 @@ impl BitMap for BitMap4 { } -pub type RefTrieDB<'a> = trie_db::TrieDB<'a, LayoutOri>; -pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, LayoutNew>; -pub type RefTrieDBNoExtQ<'a> = trie_db::TrieDB<'a, LayoutNewQuarter>; -pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, LayoutOri>; -pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, LayoutNew>; -pub type RefTrieDBMutNoExtQ<'a> = trie_db::TrieDBMut<'a, LayoutNewQuarter>; -pub type RefFatDB<'a> = trie_db::FatDB<'a, LayoutOri>; -pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, LayoutOri>; -pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, LayoutOri>; -pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, LayoutOri>; -pub type RefLookup<'a, Q> = trie_db::Lookup<'a, LayoutOri, Q>; -pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, LayoutNew, Q>; -pub type RefLookupNoExtQ<'a, Q> = trie_db::Lookup<'a, LayoutNewQuarter, Q>; +pub type RefTrieDB<'a> = trie_db::TrieDB<'a, ExtensionLayout>; +pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, NoExtensionLayout>; +pub type RefTrieDBNoExtQ<'a> = trie_db::TrieDB<'a, NoExtensionLayoutQuarter>; +pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, ExtensionLayout>; +pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, NoExtensionLayout>; +pub type RefTrieDBMutNoExtQ<'a> = trie_db::TrieDBMut<'a, NoExtensionLayoutQuarter>; +pub type RefFatDB<'a> = trie_db::FatDB<'a, ExtensionLayout>; +pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, ExtensionLayout>; +pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, ExtensionLayout>; +pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, ExtensionLayout>; +pub type RefLookup<'a, Q> = trie_db::Lookup<'a, ExtensionLayout, Q>; +pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; +pub type RefLookupNoExtQ<'a, Q> = trie_db::Lookup<'a, NoExtensionLayoutQuarter, Q>; pub fn ref_trie_root(input: I) -> ::Out where I: IntoIterator, @@ -737,7 +737,7 @@ impl< children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>) -> Vec { let mut output = vec![0; BITMAP::ENCODED_LEN + 1]; - let mut prefix: BITMAP::Buff = Default::default(); + let mut prefix: BITMAP::Buffer = Default::default(); let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); true @@ -877,7 +877,7 @@ impl< partial_enc_it::(partial, number_nibble, NodeKindNoExt::BranchNoValue) }; let bitmap_index = output.len(); - let mut bitmap: BITMAP::Buff = Default::default(); + let mut bitmap: BITMAP::Buffer = Default::default(); (0..BITMAP::ENCODED_LEN).for_each(|_|output.push(0)); if let Some(value) = maybe_value { value.encode_to(&mut output); @@ -908,7 +908,7 @@ pub fn compare_impl + Eq> ( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -951,7 +951,7 @@ pub fn compare_root( ) { let root_new = { let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -972,7 +972,7 @@ pub fn compare_unhashed( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed(data); @@ -987,7 +987,7 @@ pub fn compare_unhashed_no_ext( ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = ref_trie_root_unhashed_no_ext(data); @@ -1005,7 +1005,7 @@ pub fn calc_root( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -1020,7 +1020,7 @@ pub fn calc_root_no_ext( B: AsRef<[u8]> + fmt::Debug, { let mut cb = TrieRoot::::default(); - trie_db::trie_visit::(data.into_iter(), &mut cb); + trie_db::trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -1036,7 +1036,7 @@ pub fn calc_root_build( DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); - trie_visit::(data.into_iter(), &mut cb); + trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -1053,7 +1053,7 @@ pub fn calc_root_build_no_ext( DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); - trie_db::trie_visit::(data.into_iter(), &mut cb); + trie_db::trie_visit::(data.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -1066,7 +1066,7 @@ pub fn compare_impl_no_ext( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -1110,7 +1110,7 @@ pub fn compare_impl_no_ext_q( ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); + trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; let root = { @@ -1168,7 +1168,7 @@ pub fn compare_impl_no_ext_unordered( }; let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(b_map.into_iter(), &mut cb); + trie_visit::(b_map.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index f94df32c..31e43404 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -105,7 +105,7 @@ pub trait BitMap: Sized { type Error: Error; /// Codec buffer to use. - type Buff: AsRef<[u8]> + AsMut<[u8]> + Default; + type Buffer: AsRef<[u8]> + AsMut<[u8]> + Default; /// decode bitmap fn decode(data: &[u8]) -> Result; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index e60bb012..ca7d801f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1429,7 +1429,7 @@ mod tests { use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayout, NodeCodec, ReferenceNodeCodec, ref_trie_root, ref_trie_root_no_ext, - LayoutOri, BitMap16}; + ExtensionLayout, BitMap16}; fn populate_trie<'db>( db: &'db mut dyn HashDB, @@ -1475,7 +1475,7 @@ mod tests { fn reference_hashed_null_node() -> ::Out { - as NodeCodec::N>>::hashed_null_node() + as NodeCodec::N>>::hashed_null_node() } #[test] From 93e9c13c23ece7e934ffaa07832df9727297cdc2 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 20:33:25 +0200 Subject: [PATCH 104/120] Remove substrate from comment. --- test-support/reference-trie/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 79c6e240..04e5c0cc 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -39,7 +39,7 @@ pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, pub use trie_db::{Record, TrieLayout, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; -/// trie layout using extension nodes. +/// Trie layout using extension nodes. pub struct ExtensionLayout; impl TrieLayout for ExtensionLayout { @@ -52,7 +52,7 @@ impl TrieLayout for ExtensionLayout { impl TrieOps for ExtensionLayout { } -/// trie layout similar to substrate one +/// Trie layout without extension nodes. pub struct NoExtensionLayout; impl TrieLayout for NoExtensionLayout { @@ -550,8 +550,9 @@ impl Decode for NodeHeaderNoExt { pub struct ReferenceNodeCodec(PhantomData); /// Simple reference implementation of a `NodeCodec`. -/// Implementation follows https://github.com/w3f/polkadot-re-spec/issues/8. -/// It is mainly testing trie without extension node. +/// Implementation follows initial specification of +/// https://github.com/w3f/polkadot-re-spec/issues/8. +/// But it is mainly the testing codec without extension node. #[derive(Default, Clone)] pub struct ReferenceNodeCodecNoExt(PhantomData); From 699cdc07b8bec33c24877db2b478c18344248fd4 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 26 Jul 2019 10:09:18 +0200 Subject: [PATCH 105/120] First pass of renamaings. --- memory-db/src/lib.rs | 133 ------------- test-support/reference-trie/src/lib.rs | 177 ++++++++---------- trie-db/benches/bench.rs | 78 ++++---- trie-db/fuzz/Cargo.toml | 10 +- trie-db/fuzz/fuzz_targets/no_ext_insert.rs | 4 +- .../fuzz/fuzz_targets/no_ext_insert_rem.rs | 4 +- trie-db/fuzz/fuzz_targets/trie_root.rs | 4 +- .../fuzz/fuzz_targets/trie_root_fix_len.rs | 4 +- trie-db/fuzz/fuzz_targets/trie_root_new.rs | 4 +- .../fuzz/fuzz_targets/trie_unhashed_no_ext.rs | 4 +- trie-db/fuzz/src/lib.rs | 34 ++-- trie-db/src/iter_build.rs | 154 +++++++-------- trie-db/src/node_codec.rs | 36 ++-- trie-db/src/triedbmut.rs | 56 +++--- trie-root/src/lib.rs | 28 +-- 15 files changed, 283 insertions(+), 447 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 426fe9c7..909e9046 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -163,7 +163,6 @@ pub trait KeyFunction { fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; } - /// Derive a database key from hash and prefix. /// /// This method does not preserve ordering of prefix. @@ -179,113 +178,6 @@ pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { prefixed_key } -#[test] -fn test_ordered_prefixed_key() { - use keccak_hasher::KeccakHasher; - #[derive(Debug, Clone, Default)] - struct Opt; - impl IntoIterator for Opt { - type Item = usize; - type IntoIter = std::iter::Repeat; - fn into_iter(self) -> Self::IntoIter { - std::iter::repeat(1) - } - } - impl OrderedPrefixedKeyChunk for Opt { } - let prefixes = vec![ - (vec![],(0u8, 0x00u8), [1u8;32]), - (vec![],(1u8, 0x00u8), [1u8;32]), - (vec![0u8],(0,0), [0;32]), - (vec![],(1u8, 0x10u8), [1u8;32]), - (vec![0x10u8],(0,0), [0;32]), - (vec![0x11u8],(0,0), [0;32]), - ]; - let oc: std::collections::BTreeSet<_> = prefixes.iter().map(|(p1, p2, k)| { - ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone())) - }).collect(); - - for ((p1, p2, k),b) in prefixes.iter().zip(oc.iter()) { - assert_eq!( - &ordered_prefixed_key::(&k.clone().into(), (&p1[..], p2.clone())), - b, - ); - } -} - -/// Parameterize the size of successive chunks when using -/// `ordered_prefixed_key`. -/// Chunks are successive fix length byte arrays containing the key. -/// Last chunk is padded with 0 values. -/// Length of each chunk is minimum 1, maximum 254. -pub trait OrderedPrefixedKeyChunk: IntoIterator + Clone + MaybeDebug + Default { - /// Estimate a final length given a input length. - fn estimate_enc_len(input: usize) -> usize { input } -} - -/// Derive a database key from hash and prefix. -/// -/// Key are byte ordered depending on prefix (follows trie iteration). -/// -/// Size encoding use fix sized chunks with 2 additional encoding bytes (not optimal -/// for multiple chunks): the number of written bytes , and -/// the number of bytes in the prefix padding byte. -/// Padding is 0 values, and the 2 additional bytes allows ordering for 0 keys. -pub fn ordered_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec - where - H: KeyHasher, - C: OrderedPrefixedKeyChunk, - { - const REM_CHUNKS: u8 = 255; - const FULL_NIBBLES: u8 = 255; - let mut prefixed_key = Vec::with_capacity( - key.as_ref().len() + C::estimate_enc_len(prefix.0.len() + 2) + 1 - ); - let mut to_write = &prefix.0[..]; - let mut rem = 0; - for chunk_len in C::default() { - if chunk_len >= to_write.len() { - prefixed_key.extend_from_slice(&to_write[..]); - match chunk_len - to_write.len() { - 0 => { - rem = chunk_len; - prefixed_key.push(REM_CHUNKS); - prefixed_key.push(FULL_NIBBLES); - }, - 1 => { - prefixed_key.push((prefix.1).1); - prefixed_key.push(to_write.len() as u8 + 1); - prefixed_key.push(REM_CHUNKS); - prefixed_key.push((prefix.1).0); - }, - a => { - let pl = prefixed_key.len(); - prefixed_key.push((prefix.1).1); - prefixed_key.resize(pl + a, 0); - prefixed_key.push(to_write.len() as u8 + 1); - prefixed_key.push((prefix.1).0); - }, - } - break; - } - - prefixed_key.extend_from_slice(&to_write[..chunk_len]); - to_write = &to_write[chunk_len..]; - prefixed_key.push(REM_CHUNKS); - prefixed_key.push(FULL_NIBBLES); - } - if rem > 0 { - let pl = prefixed_key.len(); - prefixed_key.push((prefix.1).1); - prefixed_key.resize(pl + rem, 0); - prefixed_key.push(1); - prefixed_key.push((prefix.1).0); - } - prefixed_key.extend_from_slice(key.as_ref()); - prefixed_key -} - - - /// Legacy method for db using previous version of prefix encoding. /// Only for trie radix 16. pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { @@ -334,29 +226,6 @@ impl KeyFunction for PrefixedKey { } } -#[derive(Clone,Debug)] -/// Key function that concatenates prefix and hash. -/// The resulting key byte is ordered by prefix byte value -/// (in case of a trie prefix iteration over the prefixed -/// nodes are ordered the same way as in the trie). -/// Also note that this scheme allow retrieval of the prefix -/// original value. -pub struct OrderedPrefixedKey(C, PhantomData); - -impl KeyFunction for OrderedPrefixedKey - where - H: KeyHasher, - C: OrderedPrefixedKeyChunk, -{ - type Key = Vec; - - fn key(hash: &H::Out, prefix: Prefix) -> Vec { - ordered_prefixed_key::(hash, prefix) - } -} - - - #[derive(Clone,Debug)] /// Key function that concatenates prefix and hash. /// This is doing useless computation and should only be @@ -566,8 +435,6 @@ where } } - - impl PlainDB for MemoryDB where H: KeyHasher, diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 04e5c0cc..dbfe72b4 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -165,7 +165,7 @@ pub type RefLookup<'a, Q> = trie_db::Lookup<'a, ExtensionLayout, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; pub type RefLookupNoExtQ<'a, Q> = trie_db::Lookup<'a, NoExtensionLayoutQuarter, Q>; -pub fn ref_trie_root(input: I) -> ::Out where +pub fn reference_trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, @@ -173,7 +173,7 @@ pub fn ref_trie_root(input: I) -> ::Out where trie_root::trie_root::(input) } -fn ref_trie_root_unhashed(input: I) -> Vec where +fn reference_trie_root_unhashed(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, @@ -181,20 +181,20 @@ fn ref_trie_root_unhashed(input: I) -> Vec where trie_root::unhashed_trie::(input) } -pub fn ref_trie_root_no_ext(input: I) -> ::Out where +pub fn reference_trie_root_no_extension(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::trie_root_no_ext::(input) + trie_root::trie_root_no_extension::(input) } -fn ref_trie_root_unhashed_no_ext(input: I) -> Vec where +fn reference_trie_root_unhashed_no_extension(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::unhashed_trie_no_ext::(input) + trie_root::unhashed_trie_no_extension::(input) } const EMPTY_TRIE: u8 = 0; @@ -207,31 +207,12 @@ const EXTENSION_NODE_OVER: u8 = BRANCH_NODE_NO_VALUE - EXTENSION_NODE_OFFSET; const LEAF_NODE_LAST: u8 = EXTENSION_NODE_OFFSET - 1; const EXTENSION_NODE_LAST: u8 = BRANCH_NODE_NO_VALUE - 1; -/*mod noext_cst { - pub const EMPTY_TRIE: u8 = 0; - pub const LEAF_NODE_OFFSET: u8 = 1; - pub const LEAF_NODE_BIG: u8 = 85; - pub const BRANCH_NODE_NO_VALUE: u8 = 86; - pub const BRANCH_NODE_NO_VALUE_BIG: u8 = 170; - pub const BRANCH_NODE_WITH_VALUE: u8 = 171; - pub const BRANCH_NODE_WITH_VALUE_BIG: u8 = 255; - pub const LEAF_NODE_OVER: u8 = LEAF_NODE_BIG - LEAF_NODE_OFFSET; - pub const BRANCH_NODE_WITH_VALUE_OVER: u8 = BRANCH_NODE_WITH_VALUE_BIG - BRANCH_NODE_WITH_VALUE; - pub const BRANCH_NODE_NO_VALUE_OVER: u8 = BRANCH_NODE_NO_VALUE_BIG - BRANCH_NODE_NO_VALUE; - pub const LEAF_NODE_LAST: u8 = LEAF_NODE_BIG - 1; - pub const BRANCH_NODE_WITH_VALUE_LAST: u8 = BRANCH_NODE_WITH_VALUE_BIG - 1; - pub const BRANCH_NODE_NO_VALUE_LAST: u8 = BRANCH_NODE_NO_VALUE_BIG - 1; -}*/ - -/// constant use with trie simplification codec -mod s_cst { - pub const EMPTY_TRIE: u8 = 0; - pub const NIBBLE_SIZE_BOUND: usize = u16::max_value() as usize; - pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6; - pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6; - pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; -} - +// Constant use with no extensino trie codec. +const EMPTY_TRIE_NO_EXT: u8 = 0; +const NIBBLE_SIZE_BOUND_NO_EXT: usize = u16::max_value() as usize; +const LEAF_PREFIX_MASK_NO_EXT: u8 = 0b_01 << 6; +const BRANCH_WITHOUT_MASK_NO_EXT: u8 = 0b_10 << 6; +const BRANCH_WITH_MASK_NO_EXT: u8 = 0b_11 << 6; /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 125 and if @@ -256,28 +237,26 @@ enum NodeKindNoExt { } /// Create a leaf/branch node, encoding a number of nibbles. -fn fuse_nibbles_node_noext<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl Iterator + 'a { - let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibbles.len()); +fn fuse_nibbles_node_no_extension<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl Iterator + 'a { + let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibbles.len()); let iter_start = match kind { - NodeKindNoExt::Leaf => s_size_and_prefix_iter(size, s_cst::LEAF_PREFIX_MASK), - NodeKindNoExt::BranchNoValue => s_size_and_prefix_iter(size, s_cst::BRANCH_WITHOUT_MASK), - NodeKindNoExt::BranchWithValue => s_size_and_prefix_iter(size, s_cst::BRANCH_WITH_MASK), + NodeKindNoExt::Leaf => size_and_prefix_iterator(size, LEAF_PREFIX_MASK_NO_EXT), + NodeKindNoExt::BranchNoValue => size_and_prefix_iterator(size, BRANCH_WITHOUT_MASK_NO_EXT), + NodeKindNoExt::BranchWithValue => size_and_prefix_iterator(size, BRANCH_WITH_MASK_NO_EXT), }; iter_start .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) - //.chain(nibbles[..nibbles.len() - (nibbles.len() % 2)].chunks(2).map(|ch| ch[0] << 4 | ch[1])) - //.chain(if nibbles.len() % 2 == 1 { Some(nibbles[nibbles.len() - 1] << 4) } else { None }) } fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { - let mut res = [0, 0, 0]; - branch_node_buf::(has_value, has_children, &mut res[..]); - res + let mut result = [0, 0, 0]; + branch_node_buffered::(has_value, has_children, &mut result[..]); + result } -fn branch_node_buf>(has_value: bool, has_children: I, dest: &mut[u8]) { +fn branch_node_buffered>(has_value: bool, has_children: I, dest: &mut[u8]) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { @@ -359,11 +338,11 @@ impl TrieStream for ReferenceTrieStreamNoExt { } fn append_empty_data(&mut self) { - self.buffer.push(s_cst::EMPTY_TRIE); + self.buffer.push(EMPTY_TRIE_NO_EXT); } fn append_leaf(&mut self, key: &[u8], value: &[u8]) { - self.buffer.extend(fuse_nibbles_node_noext(key, NodeKindNoExt::Leaf)); + self.buffer.extend(fuse_nibbles_node_no_extension(key, NodeKindNoExt::Leaf)); value.encode_to(&mut self.buffer); } @@ -375,9 +354,9 @@ impl TrieStream for ReferenceTrieStreamNoExt { ) { if let Some(partial) = maybe_key { if maybe_value.is_some() { - self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchWithValue)); + self.buffer.extend(fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchWithValue)); } else { - self.buffer.extend(fuse_nibbles_node_noext(partial, NodeKindNoExt::BranchNoValue)); + self.buffer.extend(fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchNoValue)); } let bitmap = branch_node_bit_mask(has_children); self.buffer.extend([bitmap.0, bitmap.1].iter()); @@ -434,8 +413,10 @@ impl Encode for NodeHeader { } } -fn s_size_and_prefix_iter(size: usize, prefix: u8) -> impl Iterator { - let size = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, size); +/// Encode and allocate node type header (type and size), and partial value. +/// It uses an iterator over encoded partial bytes as input. +fn size_and_prefix_iterator(size: usize, prefix: u8) -> impl Iterator { + let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, size); let l1 = std::cmp::min(62, size); let (first_byte, mut rem) = if size == l1 { @@ -446,9 +427,9 @@ fn s_size_and_prefix_iter(size: usize, prefix: u8) -> impl Iterator { let next_bytes = move || { if rem > 0 { if rem < 256 { - let res = rem - 1; + let result = rem - 1; rem = 0; - Some(res as u8) + Some(result as u8) } else { rem = rem.saturating_sub(255); Some(255) @@ -460,19 +441,19 @@ fn s_size_and_prefix_iter(size: usize, prefix: u8) -> impl Iterator { first_byte.chain(::std::iter::from_fn(next_bytes)) } -fn s_encode_size_and_prefix(size: usize, prefix: u8, out: &mut impl Output) { - for b in s_size_and_prefix_iter(size, prefix) { +fn encode_size_and_prefix(size: usize, prefix: u8, out: &mut impl Output) { + for b in size_and_prefix_iterator(size, prefix) { out.push_byte(b) } } -fn s_decode_size(first: u8, input: &mut I) -> Option { +fn decode_size(first: u8, input: &mut I) -> Option { let mut result = (first & 255u8 >> 2) as usize; if result < 63 { return Some(result); } result -= 1; - while result <= s_cst::NIBBLE_SIZE_BOUND { + while result <= NIBBLE_SIZE_BOUND_NO_EXT { let n = input.read_byte()? as usize; if n < 255 { return Some(result + n + 1); @@ -485,19 +466,19 @@ fn s_decode_size(first: u8, input: &mut I) -> Option { #[test] fn test_encoding_simple_trie() { for prefix in [ - s_cst::LEAF_PREFIX_MASK, - s_cst::BRANCH_WITHOUT_MASK, - s_cst::BRANCH_WITH_MASK, + LEAF_PREFIX_MASK_NO_EXT, + BRANCH_WITHOUT_MASK_NO_EXT, + BRANCH_WITH_MASK_NO_EXT, ].iter() { for i in (0..1000) - .chain(s_cst::NIBBLE_SIZE_BOUND - 2..s_cst::NIBBLE_SIZE_BOUND + 2) { + .chain(NIBBLE_SIZE_BOUND_NO_EXT - 2..NIBBLE_SIZE_BOUND_NO_EXT + 2) { let mut output = Vec::new(); - s_encode_size_and_prefix(i, *prefix, &mut output); + encode_size_and_prefix(i, *prefix, &mut output); let input = &mut &output[..]; let first = input.read_byte().unwrap(); assert_eq!(first & (0b11 << 6), *prefix); - let v = s_decode_size(first, input); - assert_eq!(Some(std::cmp::min(i, s_cst::NIBBLE_SIZE_BOUND)), v); + let v = decode_size(first, input); + assert_eq!(Some(std::cmp::min(i, NIBBLE_SIZE_BOUND_NO_EXT)), v); } } @@ -506,13 +487,13 @@ fn test_encoding_simple_trie() { impl Encode for NodeHeaderNoExt { fn encode_to(&self, output: &mut T) { match self { - NodeHeaderNoExt::Null => output.push_byte(s_cst::EMPTY_TRIE), + NodeHeaderNoExt::Null => output.push_byte(EMPTY_TRIE_NO_EXT), NodeHeaderNoExt::Branch(true, nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITH_MASK, output), + encode_size_and_prefix(*nibble_count, BRANCH_WITH_MASK_NO_EXT, output), NodeHeaderNoExt::Branch(false, nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::BRANCH_WITHOUT_MASK, output), + encode_size_and_prefix(*nibble_count, BRANCH_WITHOUT_MASK_NO_EXT, output), NodeHeaderNoExt::Leaf(nibble_count) => - s_encode_size_and_prefix(*nibble_count, s_cst::LEAF_PREFIX_MASK, output), + encode_size_and_prefix(*nibble_count, LEAF_PREFIX_MASK_NO_EXT, output), } } } @@ -532,13 +513,13 @@ impl Decode for NodeHeader { impl Decode for NodeHeaderNoExt { fn decode(input: &mut I) -> Option { let i = input.read_byte()?; - if i == s_cst::EMPTY_TRIE { + if i == EMPTY_TRIE_NO_EXT { return Some(NodeHeaderNoExt::Null); } match i & (0b11 << 6) { - s_cst::LEAF_PREFIX_MASK => Some(NodeHeaderNoExt::Leaf(s_decode_size(i, input)?)), - s_cst::BRANCH_WITHOUT_MASK => Some(NodeHeaderNoExt::Branch(false, s_decode_size(i, input)?)), - s_cst::BRANCH_WITH_MASK => Some(NodeHeaderNoExt::Branch(true, s_decode_size(i, input)?)), + LEAF_PREFIX_MASK_NO_EXT => Some(NodeHeaderNoExt::Leaf(decode_size(i, input)?)), + BRANCH_WITHOUT_MASK_NO_EXT => Some(NodeHeaderNoExt::Branch(false, decode_size(i, input)?)), + BRANCH_WITH_MASK_NO_EXT => Some(NodeHeaderNoExt::Branch(true, decode_size(i, input)?)), // do not allow any special encoding _ => None, } @@ -597,7 +578,7 @@ fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec>(partial: I, nibble_count: usize, offset: u8, over: u8) -> Vec { +fn partial_from_iterator_to_key>(partial: I, nibble_count: usize, offset: u8, over: u8) -> Vec { assert!(nibble_count < over as usize); let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); output.push(offset + nibble_count as u8); @@ -605,8 +586,8 @@ fn partial_to_key_it>(partial: I, nibble_co output } -fn partial_enc_it>(partial: I, nibble_count: usize, node_kind: NodeKindNoExt) -> Vec { - let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); +fn partial_from_iterator_encode>(partial: I, nibble_count: usize, node_kind: NodeKindNoExt) -> Vec { + let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); let mut output = Vec::with_capacity(3 + (nibble_count / N::NIBBLE_PER_BYTE)); match node_kind { @@ -620,11 +601,11 @@ fn partial_enc_it>(partial: I, nibble_count -fn partial_enc(partial: Partial, node_kind: NodeKindNoExt) -> Vec { +fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { let number_nibble_encoded = (partial.0).0 as usize; let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; - let nibble_count = ::std::cmp::min(s_cst::NIBBLE_SIZE_BOUND, nibble_count); + let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); let mut output = Vec::with_capacity(3 + partial.1.len()); match node_kind { @@ -725,8 +706,8 @@ impl< output } - fn ext_node(partial: impl Iterator, number_nibble: usize, child: ChildReference<::Out>) -> Vec { - let mut output = partial_to_key_it::(partial, number_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + fn extension_node(partial: impl Iterator, number_nibble: usize, child: ChildReference<::Out>) -> Vec { + let mut output = partial_from_iterator_to_key::(partial, number_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), @@ -745,7 +726,7 @@ impl< } else { false }; - branch_node_buf::(have_value, children.map(|maybe_child| match maybe_child.borrow() { + branch_node_buffered::(have_value, children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -842,16 +823,16 @@ impl< } fn empty_node() -> &'static [u8] { - &[s_cst::EMPTY_TRIE] + &[EMPTY_TRIE_NO_EXT] } fn leaf_node(partial: Partial, value: &[u8]) -> Vec { - let mut output = partial_enc::(partial, NodeKindNoExt::Leaf); + let mut output = partial_encode::(partial, NodeKindNoExt::Leaf); value.encode_to(&mut output); output } - fn ext_node( + fn extension_node( _partial: impl Iterator, _nbnibble: usize, _child: ChildReference<::Out>, @@ -873,9 +854,9 @@ impl< maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { - partial_enc_it::(partial, number_nibble, NodeKindNoExt::BranchWithValue) + partial_from_iterator_encode::(partial, number_nibble, NodeKindNoExt::BranchWithValue) } else { - partial_enc_it::(partial, number_nibble, NodeKindNoExt::BranchNoValue) + partial_from_iterator_encode::(partial, number_nibble, NodeKindNoExt::BranchNoValue) }; let bitmap_index = output.len(); let mut bitmap: BITMAP::Buffer = Default::default(); @@ -902,7 +883,7 @@ impl< } /// Compare trie builder and in memory trie. -pub fn compare_impl + Eq> ( +pub fn compare_implementations + Eq> ( data: Vec<(Vec,Vec)>, mut memdb: X, mut hashdb: X, @@ -976,14 +957,14 @@ pub fn compare_unhashed( trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; - let root = ref_trie_root_unhashed(data); + let root = reference_trie_root_unhashed(data); assert_eq!(root, root_new); } /// Compare trie builder and trie root unhashed implementations. /// This uses the variant without extension nodes. -pub fn compare_unhashed_no_ext( +pub fn compare_unhashed_no_extension( data: Vec<(Vec,Vec)>, ) { let root_new = { @@ -991,7 +972,7 @@ pub fn compare_unhashed_no_ext( trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; - let root = ref_trie_root_unhashed_no_ext(data); + let root = reference_trie_root_unhashed_no_extension(data); assert_eq!(root, root_new); } @@ -1012,7 +993,7 @@ pub fn calc_root( /// Trie builder root calculation utility. /// This uses the variant without extension nodes. -pub fn calc_root_no_ext( +pub fn calc_root_no_extension( data: I, ) -> ::Out where @@ -1043,7 +1024,7 @@ pub fn calc_root_build( /// Trie builder trie building utility. /// This uses the variant without extension nodes. -pub fn calc_root_build_no_ext( +pub fn calc_root_build_no_extension( data: I, hashdb: &mut DB, ) -> ::Out @@ -1060,7 +1041,7 @@ pub fn calc_root_build_no_ext( /// Compare trie builder and in memory trie. /// This uses the variant without extension nodes. -pub fn compare_impl_no_ext( +pub fn compare_implementations_no_extension( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, mut hashdb: impl hash_db::HashDB, @@ -1104,7 +1085,7 @@ pub fn compare_impl_no_ext( /// Compare trie builder and in memory trie. /// This uses the variant without extension nodes. /// This uses a radix 4 trie. -pub fn compare_impl_no_ext_q( +pub fn compare_implementations_no_extension_q( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, mut hashdb: impl hash_db::HashDB, @@ -1151,8 +1132,8 @@ pub fn compare_impl_no_ext_q( assert_eq!(root, root_new); } -/// `compare_impl_no_ext` for unordered input. -pub fn compare_impl_no_ext_unordered( +/// `compare_implementations_no_extension` for unordered input. +pub fn compare_implementations_no_extension_unordered( data: Vec<(Vec,Vec)>, mut memdb: impl hash_db::HashDB, mut hashdb: impl hash_db::HashDB, @@ -1197,7 +1178,7 @@ pub fn compare_impl_no_ext_unordered( /// Testing utility that uses some periodic removal over /// its input test data. -pub fn compare_no_ext_insert_remove( +pub fn compare_no_extension_insert_remove( data: Vec<(bool, Vec,Vec)>, mut memdb: impl hash_db::HashDB, ) { @@ -1240,13 +1221,13 @@ pub fn compare_no_ext_insert_remove( let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. - assert_eq!(*t.root(), calc_root_no_ext(data2)); + assert_eq!(*t.root(), calc_root_no_extension(data2)); } #[test] -fn too_big_nibble_len () { +fn too_big_nibble_length () { // + 1 for 0 added byte of nibble encode - let input = vec![0u8; (s_cst::NIBBLE_SIZE_BOUND as usize + 1) / 2 + 1]; + let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1]; let enc = as NodeCodec>::leaf_node(((0,0),&input), &[1]); let dec = as NodeCodec>::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { @@ -1273,9 +1254,9 @@ fn size_encode_limit_values () { ]; for i in 0..sizes.len() { let mut enc = Vec::new(); - s_encode_size_and_prefix(sizes[i], 0, &mut enc); + encode_size_and_prefix(sizes[i], 0, &mut enc); assert_eq!(enc, encs[i]); - let s_dec = s_decode_size(encs[i][0], &mut &encs[i][1..]); + let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); assert_eq!(s_dec, Some(sizes[i])); } } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index d3dd4d54..7c35f196 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -30,6 +30,7 @@ criterion_group!(benches, trie_mut_b, trie_mut_build_a, trie_mut_build_b, + nibble_common_prefix, ); criterion_main!(benches); @@ -70,7 +71,7 @@ fn root_a_big_v(c: &mut Criterion) { c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -90,7 +91,7 @@ fn root_b_big_v(c: &mut Criterion) { c.bench_function_over_inputs("root_b_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -111,7 +112,7 @@ fn root_a_small_v(c: &mut Criterion) { c.bench_function_over_inputs("root_a_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -131,7 +132,7 @@ fn root_b_small_v(c: &mut Criterion) { c.bench_function_over_inputs("root_b_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -158,7 +159,7 @@ fn root_old(c: &mut Criterion) { .iter() .map(|v|(&v.0, &v.1)); - reference_trie::ref_trie_root(inputc); + reference_trie::reference_trie_root(inputc); }) ,data); } @@ -175,7 +176,7 @@ fn root_new(c: &mut Criterion) { c.bench_function_over_inputs("root_new",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -217,10 +218,6 @@ fn fuzz_to_data(input: Vec) -> Vec<(Vec,Vec)> { fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec,Vec)> { let mut result = Vec::new(); - // enc = (minkeylen, maxkeylen (min max up to 32), datas) - // fix data len 2 bytes - let minkeylen = 1; - let maxkeylen = 32; let mut ix = 0; loop { let keylen = 32; @@ -253,23 +250,23 @@ fn input(seed: u64, len: usize) -> Vec<(Vec,Vec)> { data } -fn input2(seed: u64, len: usize, vl: usize) -> Vec<(Vec,Vec)> { +fn input2(seed: u64, len: usize, value_length: usize) -> Vec<(Vec,Vec)> { use rand::SeedableRng; use rand::RngCore; let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); let mut data = vec![0u8; len]; rng.fill_bytes(&mut data[..]); - let data = data_sorted_unique(fuzz_to_data2(data,vl)); + let data = data_sorted_unique(fuzz_to_data2(data,value_length)); data } -fn input_unsorted(seed: u64, len: usize, vl: usize) -> Vec<(Vec,Vec)> { +fn input_unsorted(seed: u64, len: usize, value_length: usize) -> Vec<(Vec,Vec)> { use rand::SeedableRng; use rand::RngCore; let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); let mut data = vec![0u8; len]; rng.fill_bytes(&mut data[..]); - fuzz_to_data(data) + fuzz_to_data2(data, value_length) } fn trie_mut_root_a(c: &mut Criterion) { @@ -280,7 +277,7 @@ fn trie_mut_root_a(c: &mut Criterion) { c.bench_function_over_inputs("trie_mut_root_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(|| { let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -288,29 +285,27 @@ fn trie_mut_root_a(c: &mut Criterion) { reference_trie::calc_root(inputc); - }) - ,data); + }), + data); } fn trie_mut_root_b(c: &mut Criterion) { let data : Vec,Vec)>> = vec![ - //input_unsorted(29, 204800, 512), input_unsorted(29, 204800, 32), ]; c.bench_function_over_inputs("trie_mut_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) .collect::>(); - reference_trie::calc_root(inputc); - }) - ,data); + }), + data); } fn trie_mut_ref_root_a(c: &mut Criterion) { @@ -322,16 +317,15 @@ fn trie_mut_ref_root_a(c: &mut Criterion) { b.iter(|| { let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) .collect::>(); - - reference_trie::ref_trie_root(inputc); - }) - ,data); + reference_trie::reference_trie_root(inputc); + }), + data); } fn trie_mut_ref_root_b(c: &mut Criterion) { @@ -343,16 +337,15 @@ fn trie_mut_ref_root_b(c: &mut Criterion) { c.bench_function_over_inputs("trie_mut_ref_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) .collect::>(); - - reference_trie::ref_trie_root(inputc); - }) - ,data); + reference_trie::reference_trie_root(inputc); + }), + data); } @@ -376,8 +369,8 @@ fn trie_mut_a(c: &mut Criterion) { .expect("changes trie: insertion to trie is not allowed to fail within runtime"); } - }) - ,data); + }), + data); } fn trie_mut_b(c: &mut Criterion) { @@ -400,8 +393,8 @@ fn trie_mut_b(c: &mut Criterion) { .expect("changes trie: insertion to trie is not allowed to fail within runtime"); } - }) - ,data); + }), + data); } fn trie_mut_build_a(c: &mut Criterion) { @@ -413,7 +406,7 @@ fn trie_mut_build_a(c: &mut Criterion) { c.bench_function_over_inputs("trie_mut_build_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(|| { let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) @@ -421,8 +414,8 @@ fn trie_mut_build_a(c: &mut Criterion) { let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); reference_trie::calc_root_build(inputc, &mut mdb); - }) - ,data); + }), + data); } fn trie_mut_build_b(c: &mut Criterion) { @@ -435,15 +428,14 @@ fn trie_mut_build_b(c: &mut Criterion) { c.bench_function_over_inputs("trie_mut_build_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| b.iter(||{ let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); - // this is in `ref_trie_root` added here to make things comparable + // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() .map(|v|(&v.0, &v.1)) .collect::>(); - let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); reference_trie::calc_root_build(inputc, &mut mdb); - }) - ,data); + }), + data); } diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 5812c39d..d9f1c11b 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -9,9 +9,9 @@ edition = "2018" cargo-fuzz = true [dependencies] -memory-db = { path = "../../memory-db", version = "0.12.2" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.12.2" } -keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.12.2" } +memory-db = { path = "../../memory-db", version = "0.15.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.15.0" } +keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.0" } honggfuzz = "0.5" [dependencies.trie-db] @@ -48,7 +48,3 @@ path = "fuzz_targets/no_ext_insert.rs" [[bin]] name = "no_ext_insert_rem" path = "fuzz_targets/no_ext_insert_rem.rs" - -[[bin]] -name = "hfuzz_trie_root_new" -path = "hongfuzz_targets/trie_root_new.rs" diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs index 24237eb4..4922e057 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs @@ -1,9 +1,9 @@ #![no_main] -use trie_db_fuzz::fuzz_that_no_ext_insert; +use trie_db_fuzz::fuzz_that_no_extension_insert; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_no_ext_insert(data); + fuzz_that_no_extension_insert(data); }); diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs index 387db148..608e25fc 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs @@ -1,9 +1,9 @@ #![no_main] -use trie_db_fuzz::fuzz_that_no_ext_insert_remove; +use trie_db_fuzz::fuzz_that_no_extension_insert_remove; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_no_ext_insert_remove(data); + fuzz_that_no_extension_insert_remove(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs index e9c76df6..32b65f83 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -1,9 +1,9 @@ #![no_main] -use trie_db_fuzz::fuzz_that_ref_trie_root; +use trie_db_fuzz::fuzz_that_reference_trie_root; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_ref_trie_root(data); + fuzz_that_reference_trie_root(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs index ac75c605..00763e26 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -1,9 +1,9 @@ #![no_main] -use trie_db_fuzz::fuzz_that_ref_trie_root_fix_len; +use trie_db_fuzz::fuzz_that_reference_trie_root_fix_length; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_ref_trie_root_fix_len(data); + fuzz_that_reference_trie_root_fix_length(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_new.rs b/trie-db/fuzz/fuzz_targets/trie_root_new.rs index 281e2f7f..9e9d41fa 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_new.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_new.rs @@ -1,10 +1,10 @@ #![no_main] -use trie_db_fuzz::fuzz_that_compare_impl; +use trie_db_fuzz::fuzz_that_compare_implementations; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_compare_impl(data); + fuzz_that_compare_implementations(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs index 06d06916..dba4cc68 100644 --- a/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs +++ b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs @@ -1,10 +1,10 @@ #![no_main] -use trie_db_fuzz::fuzz_that_unhashed_no_ext; +use trie_db_fuzz::fuzz_that_unhashed_no_extension; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_unhashed_no_ext(data); + fuzz_that_unhashed_no_extension(data); }); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index c26a33d8..a9519e60 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -4,10 +4,10 @@ use memory_db::{MemoryDB, HashKey, PrefixedKey}; use reference_trie::{ RefTrieDBMutNoExt, RefTrieDBMut, - ref_trie_root, - calc_root_no_ext, + reference_trie_root, + calc_root_no_extension, calc_root, - compare_no_ext_insert_remove, + compare_no_extension_insert_remove, }; use trie_db::{TrieMut, DBValue}; use keccak_hasher::KeccakHasher; @@ -78,7 +78,7 @@ fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { res } -pub fn fuzz_that_ref_trie_root(input: &[u8]) { +pub fn fuzz_that_reference_trie_root(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); @@ -86,21 +86,21 @@ pub fn fuzz_that_ref_trie_root(input: &[u8]) { for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } - assert_eq!(*t.root(), ref_trie_root(data)); + assert_eq!(*t.root(), reference_trie_root(data)); } -pub fn fuzz_that_ref_trie_root_fix_len(input: &[u8]) { - let data = data_sorted_unique(fuzz_to_data_fix_len(input)); +pub fn fuzz_that_reference_trie_root_fix_length(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data_fix_length(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } - assert_eq!(*t.root(), ref_trie_root(data)); + assert_eq!(*t.root(), reference_trie_root(data)); } -fn fuzz_to_data_fix_len(input: &[u8]) -> Vec<(Vec,Vec)> { +fn fuzz_to_data_fix_length(input: &[u8]) -> Vec<(Vec,Vec)> { let mut result = Vec::new(); let mut ix = 0; loop { @@ -127,21 +127,21 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { } -pub fn fuzz_that_compare_impl(input: &[u8]) { +pub fn fuzz_that_compare_implementations(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); //println!("data:{:?}", &data); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl(data, memdb, hashdb); + reference_trie::compare_implementations(data, memdb, hashdb); } -pub fn fuzz_that_unhashed_no_ext(input: &[u8]) { +pub fn fuzz_that_unhashed_no_extension(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); - reference_trie::compare_unhashed_no_ext(data); + reference_trie::compare_unhashed_no_extension(data); } -pub fn fuzz_that_no_ext_insert(input: &[u8]) { +pub fn fuzz_that_no_extension_insert(input: &[u8]) { let data = fuzz_to_data(input); //println!("data{:?}", data); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); @@ -154,13 +154,13 @@ pub fn fuzz_that_no_ext_insert(input: &[u8]) { // before. let data = data_sorted_unique(fuzz_to_data(input)); //println!("data{:?}", data); - assert_eq!(*t.root(), calc_root_no_ext(data)); + assert_eq!(*t.root(), calc_root_no_extension(data)); } -pub fn fuzz_that_no_ext_insert_remove(input: &[u8]) { +pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { let data = fuzz_to_data(input); let data = fuzz_removal(data); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - compare_no_ext_insert_remove(data, memdb); + compare_no_extension_insert_remove(data, memdb); } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 92519030..c58b7b21 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -29,9 +29,9 @@ use crate::{TrieLayout, TrieHash}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -macro_rules! exp_disp { - (@3, [$($inpp:expr),*]) => { exp_disp!(@2, [$($inpp,)* $($inpp),*]) }; - (@2, [$($inpp:expr),*]) => { exp_disp!(@1, [$($inpp,)* $($inpp),*]) }; +macro_rules! exponential_out { + (@3, [$($inpp:expr),*]) => { exponential_out!(@2, [$($inpp,)* $($inpp),*]) }; + (@2, [$($inpp:expr),*]) => { exponential_out!(@1, [$($inpp,)* $($inpp),*]) }; (@1, [$($inpp:expr),*]) => { [$($inpp,)* $($inpp),*] }; } @@ -44,7 +44,7 @@ pub trait CacheBuilder { /// The type of the cache. type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; /// Create a new cache. - fn new_vec_slice_buff() -> Self::AN; + fn new_vec_slice_buffer() -> Self::AN; } /// Cache builder for radix 16 trie. @@ -56,8 +56,8 @@ impl CacheBuilder for Cache16 { const SIZE: usize = 16; type AN = [CacheNode; 16]; #[inline(always)] - fn new_vec_slice_buff() -> Self::AN { - exp_disp!(@3, [None,None]) + fn new_vec_slice_buffer() -> Self::AN { + exponential_out!(@3, [None,None]) } } @@ -65,8 +65,8 @@ impl CacheBuilder for Cache4 { const SIZE: usize = 4; type AN = [CacheNode; 4]; #[inline(always)] - fn new_vec_slice_buff() -> Self::AN { - exp_disp!(@2, [None]) + fn new_vec_slice_buffer() -> Self::AN { + exponential_out!(@2, [None]) } } @@ -99,7 +99,7 @@ where #[inline(always)] fn set_elt(&mut self, depth:usize, sl: Option) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::CB::new_vec_slice_buff(), None, depth)); + self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; debug_assert!(self.0[last].2 <= depth); @@ -109,7 +109,7 @@ where #[inline(always)] fn set_node(&mut self, depth:usize, nibble_index:usize, node: CacheNode>) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::CB::new_vec_slice_buff(), None, depth)); + self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; @@ -178,7 +178,7 @@ where fn flush_branch( &mut self, - no_ext: bool, + no_extension: bool, cb_ext: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, new_depth: usize, @@ -203,9 +203,9 @@ where None }; - let h = if no_ext { + let h = if no_extension { // enc branch - self.alt_no_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + self.alt_no_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) } else { self.standard_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) }; @@ -242,7 +242,7 @@ where if let Some(nkeyix) = nkey { let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); - let encoded = T::C::ext_node(nib, nkeyix.1, branch_hash); + let encoded = T::C::extension_node(nib, nkeyix.1, branch_hash); let h = cb_ext.process(pr.left(), encoded, is_root); h } else { @@ -251,7 +251,7 @@ where } #[inline(always)] - fn alt_no_ext( + fn alt_no_extension( &mut self, key_branch: &[u8], cb_ext: &mut impl ProcessEncodedNode>, @@ -270,10 +270,10 @@ where nkeyix.1, self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref())); self.reset_depth(branch_d); - let ext_len = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); + let ext_length = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); let pr = NibbleSlice::::new_offset( &key_branch.as_ref()[..], - branch_d - ext_len, + branch_d - ext_length, ); cb_ext.process(pr.left(), encoded, is_root) } @@ -291,7 +291,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) B: AsRef<[u8]>, F: ProcessEncodedNode>, { - let no_ext = !T::USE_EXTENSION; + let no_extension = !T::USE_EXTENSION; let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); @@ -316,7 +316,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) // do not put with next, previous is last of a branch depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, depth_item, false); + depth_queue.flush_branch(no_extension, cb_ext, ref_branches, depth_item, false); } prev_val = (k, v); @@ -336,7 +336,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) } else { depth_queue.flush_val(cb_ext, last_depth, &prev_val); let ref_branches = prev_val.0; - depth_queue.flush_branch(no_ext, cb_ext, ref_branches, 0, true); + depth_queue.flush_branch(no_extension, cb_ext, ref_branches, 0, true); } } else { // nothing null root corner case @@ -514,19 +514,19 @@ mod test { #[test] fn trie_root_empty () { - compare_impl(vec![]) + compare_implementations(vec![]) } #[test] fn trie_one_node () { - compare_impl(vec![ + compare_implementations(vec![ (vec![1u8,2u8,3u8,4u8],vec![7u8]), ]); } #[test] fn root_extension_one () { - compare_impl(vec![ + compare_implementations(vec![ (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), ]); @@ -558,7 +558,7 @@ mod test { } } - fn test_iter_no_ext(data: Vec<(Vec,Vec)>) { + fn test_iter_no_extension(data: Vec<(Vec,Vec)>) { use reference_trie::{RefTrieDBMutNoExt, TrieMut, RefTrieDBNoExt, Trie}; let mut db = MemoryDB::, DBValue>::default(); @@ -584,51 +584,51 @@ mod test { } } - fn compare_impl(data: Vec<(Vec,Vec)>) { + fn compare_implementations(data: Vec<(Vec,Vec)>) { test_iter(data.clone()); - test_iter_no_ext(data.clone()); - compare_impl_h(data.clone()); - compare_impl_pk(data.clone()); - compare_impl_no_ext(data.clone()); - compare_impl_no_ext_pk(data.clone()); - compare_impl_no_ext_q(data.clone()); + test_iter_no_extension(data.clone()); + compare_implementations_h(data.clone()); + compare_implementations_prefixed(data.clone()); + compare_implementations_no_extension(data.clone()); + compare_implementations_no_extension_prefixed(data.clone()); + compare_implementations_no_extension_q(data.clone()); } - fn compare_impl_pk(data: Vec<(Vec,Vec)>) { + fn compare_implementations_prefixed(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl(data, memdb, hashdb); + reference_trie::compare_implementations(data, memdb, hashdb); } - fn compare_impl_h(data: Vec<(Vec,Vec)>) { + fn compare_implementations_h(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl(data, memdb, hashdb); + reference_trie::compare_implementations(data, memdb, hashdb); } - fn compare_impl_no_ext(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl_no_ext(data, memdb, hashdb); + reference_trie::compare_implementations_no_extension(data, memdb, hashdb); } - fn compare_impl_no_ext_q(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_q(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl_no_ext_q(data, memdb, hashdb); + reference_trie::compare_implementations_no_extension_q(data, memdb, hashdb); } - fn compare_impl_no_ext_pk(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_prefixed(data: Vec<(Vec,Vec)>) { // let memdb = MemoryDB::<_, HashKey<_>, _>::default(); // let hashdb = MemoryDB::, DBValue>::default(); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl_no_ext(data, memdb, hashdb); + reference_trie::compare_implementations_no_extension(data, memdb, hashdb); } - fn compare_impl_no_ext_unordered(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_unordered(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_impl_no_ext_unordered(data, memdb, hashdb); + reference_trie::compare_implementations_no_extension_unordered(data, memdb, hashdb); } - fn compare_no_ext_insert_remove(data: Vec<(bool, Vec,Vec)>) { + fn compare_no_extension_insert_remove(data: Vec<(bool, Vec,Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - reference_trie::compare_no_ext_insert_remove(data, memdb); + reference_trie::compare_no_extension_insert_remove(data, memdb); } fn compare_root(data: Vec<(Vec,Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); @@ -637,13 +637,13 @@ mod test { fn compare_unhashed(data: Vec<(Vec,Vec)>) { reference_trie::compare_unhashed(data); } - fn compare_unhashed_no_ext(data: Vec<(Vec,Vec)>) { - reference_trie::compare_unhashed_no_ext(data); + fn compare_unhashed_no_extension(data: Vec<(Vec,Vec)>) { + reference_trie::compare_unhashed_no_extension(data); } #[test] fn trie_middle_node1 () { - compare_impl(vec![ + compare_implementations(vec![ (vec![1u8,2u8],vec![8u8;32]), (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), ]); @@ -651,7 +651,7 @@ mod test { #[test] fn trie_middle_node2 () { - compare_impl(vec![ + compare_implementations(vec![ (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;32]), (vec![1u8,2u8],vec![8u8;32]), (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), @@ -673,7 +673,7 @@ mod test { (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), ]; compare_unhashed(d.clone()); - compare_unhashed_no_ext(d); + compare_unhashed_no_extension(d); } #[test] fn root_extension_tierce_big () { @@ -688,7 +688,7 @@ mod test { } #[test] fn trie_middle_node2x () { - compare_impl(vec![ + compare_implementations(vec![ (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;2]), (vec![1u8,2u8],vec![8u8;2]), (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), @@ -698,7 +698,7 @@ mod test { } #[test] fn fuzz1 () { - compare_impl(vec![ + compare_implementations(vec![ (vec![01u8],vec![42u8,9]), (vec![01u8,0u8],vec![0u8,0]), (vec![255u8,2u8],vec![1u8,0]), @@ -706,7 +706,7 @@ mod test { } #[test] fn fuzz2 () { - compare_impl(vec![ + compare_implementations(vec![ (vec![0,01u8],vec![42u8,9]), (vec![0,01u8,0u8],vec![0u8,0]), (vec![0,255u8,2u8],vec![1u8,0]), @@ -714,7 +714,7 @@ mod test { } #[test] fn fuzz3 () { - compare_impl(vec![ + compare_implementations(vec![ (vec![0],vec![196, 255]), (vec![48],vec![138, 255]), (vec![67],vec![0, 0]), @@ -724,23 +724,23 @@ mod test { ]); } #[test] - fn fuzz_noext1 () { - compare_impl(vec![ + fn fuzz_no_extension1 () { + compare_implementations(vec![ (vec![0],vec![128, 0]), (vec![128],vec![0, 0]), ]); } #[test] - fn fuzz_noext2 () { - compare_impl(vec![ + fn fuzz_no_extension2 () { + compare_implementations(vec![ (vec![0],vec![6, 255]), (vec![6],vec![255, 186]), (vec![255],vec![186, 255]), ]); } #[test] - fn fuzz_noext5 () { - compare_impl(vec![ + fn fuzz_no_extension5 () { + compare_implementations(vec![ (vec![0xaa], vec![0xa0]), (vec![0xaa, 0xaa], vec![0xaa]), (vec![0xaa, 0xbb], vec![0xab]), @@ -750,75 +750,75 @@ mod test { ]); } #[test] - fn fuzz_noext3 () { - compare_impl(vec![ + fn fuzz_no_extension3 () { + compare_implementations(vec![ (vec![0],vec![0, 0]), (vec![11,0],vec![0, 0]), (vec![11,252],vec![11, 0]), ]); - compare_impl_no_ext_unordered(vec![ + compare_implementations_no_extension_unordered(vec![ (vec![11,252],vec![11, 0]), (vec![11,0],vec![0, 0]), (vec![0],vec![0, 0]), ]); } #[test] - fn fuzz_noext4 () { - compare_impl_no_ext(vec![ + fn fuzz_no_extension4 () { + compare_implementations_no_extension(vec![ (vec![0x01, 0x56], vec![0x1]), (vec![0x02, 0x42], vec![0x2]), (vec![0x02, 0x50], vec![0x3]), ]); } #[test] - fn fuzz_noext_ins_rem_1 () { + fn fuzz_no_extension_insert_remove_1 () { let data = vec![ (false, vec![0], vec![251, 255]), (false, vec![0,1], vec![251, 255]), (false, vec![0,1,2], vec![255; 32]), (true, vec![0,1], vec![0, 251]), ]; - compare_no_ext_insert_remove(data); + compare_no_extension_insert_remove(data); } #[test] - fn fuzz_noext_ins_rem_2 () { + fn fuzz_no_extension_insert_remove_2 () { let data = vec![ (false, vec![0x00], vec![0xfd, 0xff]), (false, vec![0x10, 0x00], vec![1;32]), (false, vec![0x11, 0x10], vec![0;32]), (true, vec![0x10, 0x00], vec![]) ]; - compare_no_ext_insert_remove(data); + compare_no_extension_insert_remove(data); } #[test] - fn two_bytes_nibble_len () { + fn two_bytes_nibble_length () { let data = vec![ (vec![00u8],vec![0]), (vec![01u8;64],vec![0;32]), ]; - compare_impl_no_ext(data.clone()); - compare_impl_no_ext_pk(data.clone()); + compare_implementations_no_extension(data.clone()); + compare_implementations_no_extension_prefixed(data.clone()); } #[test] #[should_panic] - fn too_big_nibble_len_old () { - compare_impl_h(vec![ + fn too_big_nibble_length_old () { + compare_implementations_h(vec![ (vec![01u8;64],vec![0;32]), ]); } #[test] - fn too_big_nibble_len_new () { + fn too_big_nibble_length_new () { // truncate keep things working in both situation (but will conflict for multiple common prefix // val!!) - compare_impl_no_ext(vec![ + compare_implementations_no_extension(vec![ (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1],vec![0;32]), ]); } #[test] fn polka_re_test () { - compare_impl(vec![ + compare_implementations(vec![ (vec![77, 111, 111, 55, 111, 104, 121, 97], vec![68, 97, 105, 55, 105, 101, 116, 111]), (vec![101, 105, 67, 104, 111, 111, 66, 56], vec![97, 56, 97, 113, 117, 53, 97]), (vec![105, 97, 48, 77, 101, 105, 121, 101], vec![69, 109, 111, 111, 82, 49, 97, 105]), diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 31e43404..e9519760 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -70,30 +70,30 @@ pub trait NodeCodec: Sized { fn leaf_node(partial: Partial, value: &[u8]) -> Vec; /// Returns an encoded extension node - /// Note that number_nibble is the number of element of the iterator - /// it can possibly be obtain by `Iterator` `size_hint`, but - /// for simplicity it is used directly as a parameter. - fn ext_node( - partial: impl Iterator, - number_nibble: usize, - child_ref: ChildReference, - ) -> Vec; + /// Note that number_nibble is the number of element of the iterator + /// it can possibly be obtain by `Iterator` `size_hint`, but + /// for simplicity it is used directly as a parameter. + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child_ref: ChildReference, + ) -> Vec; /// Returns an encoded branch node. - /// Takes an iterator yielding `ChildReference` and an optional value. + /// Takes an iterator yielding `ChildReference` and an optional value. fn branch_node( - children: impl Iterator>>>, - value: Option<&[u8]>, - ) -> Vec; + children: impl Iterator>>>, + value: Option<&[u8]>, + ) -> Vec; /// Returns an encoded branch node with a possible partial path. - /// `number_nibble` is the partial path lenghth as in `ext_node`. + /// `number_nibble` is the partial path length as in `extension_node`. fn branch_node_nibbled( - partial: impl Iterator, - number_nibble: usize, - children: impl Iterator>>>, - value: Option<&[u8]> - ) -> Vec; + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + value: Option<&[u8]> + ) -> Vec; } /// Bitmap encoder for the number of children nodes. diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index ca7d801f..966496c0 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -173,7 +173,7 @@ where let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); let c = child_cb(child, Some(&pr), None); - C::ext_node( + C::extension_node( it, pr.len(), c, @@ -907,11 +907,11 @@ where } }, (Node::NibbledBranch(encoded, mut children, value), false) => { - let (cp, existing_len) = { + let (cp, existing_length) = { let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; - if cp == existing_len && cp == partial.len() { + if cp == existing_length && cp == partial.len() { // replace val if let Some(val) = value { @@ -922,11 +922,11 @@ where } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) } - } else if cp < existing_len { + } else if cp < existing_length { // partway through an extension -- nothing to do here. Action::Restore(Node::NibbledBranch(encoded, children, value)) } else { - // cp == existing_len && cp < partial.len() : check children + // cp == existing_length && cp < partial.len() : check children let idx = partial.at(cp) as usize; if let Some(child) = children[idx].take() { @@ -977,11 +977,11 @@ where } }, (Node::Extension(encoded, child_branch), _) => { - let (cp, existing_len) = { + let (cp, existing_length) = { let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; - if cp == existing_len { + if cp == existing_length { // try to remove from the child branch. #[cfg(feature = "std")] trace!(target: "trie", "removing from extension child, partial={:?}", partial); @@ -1428,7 +1428,7 @@ mod tests { use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayout, NodeCodec, - ReferenceNodeCodec, ref_trie_root, ref_trie_root_no_ext, + ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension, ExtensionLayout, BitMap16}; fn populate_trie<'db>( @@ -1452,7 +1452,7 @@ mod tests { } } - fn populate_trie_no_ext<'db>( + fn populate_trie_no_extension<'db>( db: &'db mut dyn HashDB, root: &'db mut ::Out, v: &[(Vec, Vec)] @@ -1466,7 +1466,7 @@ mod tests { t } - fn unpopulate_trie_no_ext<'db>(t: &mut RefTrieDBMutNoExt<'db>, v: &[(Vec, Vec)]) { + fn unpopulate_trie_no_extension<'db>(t: &mut RefTrieDBMutNoExt<'db>, v: &[(Vec, Vec)]) { for i in v { let key: &[u8]= &i.0; t.remove(key).unwrap(); @@ -1494,7 +1494,7 @@ mod tests { count: 100, }.make_with(&mut seed); - let real = ref_trie_root(x.clone()); + let real = reference_trie_root(x.clone()); let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut memtrie = populate_trie(&mut memdb, &mut root, &x); @@ -1523,7 +1523,7 @@ mod tests { assert_eq!(*memtrie.root(), hashed_null_node); } - // no_ext + // no_extension let mut seed = Default::default(); for test_i in 0..10 { if test_i % 50 == 0 { @@ -1537,10 +1537,10 @@ mod tests { count: 100, }.make_with(&mut seed); - let real = ref_trie_root_no_ext(x.clone()); + let real = reference_trie_root_no_extension(x.clone()); let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut memtrie = populate_trie_no_ext(&mut memdb, &mut root, &x); + let mut memtrie = populate_trie_no_extension(&mut memdb, &mut root, &x); memtrie.commit(); if *memtrie.root() != real { @@ -1552,7 +1552,7 @@ mod tests { } } assert_eq!(*memtrie.root(), real); - unpopulate_trie_no_ext(&mut memtrie, &x); + unpopulate_trie_no_extension(&mut memtrie, &x); memtrie.commit(); let hashed_null_node = reference_hashed_null_node(); if *memtrie.root() != hashed_null_node { @@ -1582,7 +1582,7 @@ mod tests { let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); } #[test] @@ -1600,7 +1600,7 @@ mod tests { } #[test] - fn remove_to_empty_no_ext() { + fn remove_to_empty_no_extension() { let big_value = b"00000000000000000000000000000000"; let big_value2 = b"00000000000000000000000000000002"; let big_value3 = b"00000000000000000000000000000004"; @@ -1616,7 +1616,7 @@ mod tests { t.remove(&[0x01]).unwrap(); // commit on drop } - assert_eq!(&root[..], &reference_trie::calc_root_no_ext(vec![ + assert_eq!(&root[..], &reference_trie::calc_root_no_extension(vec![ (vec![0x01u8, 0x23], big_value3.to_vec()), (vec![0x01u8, 0x34], big_value.to_vec()), ])[..]); @@ -1630,7 +1630,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); } #[test] @@ -1640,7 +1640,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x11u8, 0x23], vec![0x11u8, 0x23]) ])); @@ -1654,7 +1654,7 @@ mod tests { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x81u8, 0x23], vec![0x81u8, 0x23]), (vec![0xf1u8, 0x23], vec![0xf1u8, 0x23]), @@ -1668,7 +1668,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[], &[0x0]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![], vec![0x0]), (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), ])); @@ -1681,7 +1681,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x01u8, 0x34], vec![0x01u8, 0x34]), ])); @@ -1695,7 +1695,7 @@ mod tests { t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap(); t.insert(&[0x01, 0xf3, 0x45], &[0x02]).unwrap(); t.insert(&[0x01, 0xf3, 0xf5], &[0x03]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01, 0x23, 0x45], vec![0x01]), (vec![0x01, 0xf3, 0x45], vec![0x02]), (vec![0x01, 0xf3, 0xf5], vec![0x03]), @@ -1712,7 +1712,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x11u8, 0x23], big_value1).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], big_value0.to_vec()), (vec![0x11u8, 0x23], big_value1.to_vec()) ])); @@ -1727,7 +1727,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x11u8, 0x23], big_value).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], big_value.to_vec()), (vec![0x11u8, 0x23], big_value.to_vec()) ])); @@ -1783,7 +1783,7 @@ mod tests { count: 4, }.make_with(&mut seed); - let real = ref_trie_root(x.clone()); + let real = reference_trie_root(x.clone()); let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut memtrie = populate_trie(&mut memdb, &mut root, &x); @@ -1841,7 +1841,7 @@ mod tests { t.insert(key, value).unwrap(); } - assert_eq!(*t.root(), ref_trie_root(x.clone())); + assert_eq!(*t.root(), reference_trie_root(x.clone())); for &(ref key, _) in &x { t.insert(key, &[]).unwrap(); diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 4cd69331..34ee3e44 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -68,7 +68,7 @@ pub trait TrieStream { fn out(self) -> Vec; } -fn shared_prefix_len(first: &[T], second: &[T]) -> usize { +fn shared_prefix_length(first: &[T], second: &[T]) -> usize { first.iter() .zip(second.iter()) .position(|(f, s)| f != s) @@ -107,7 +107,7 @@ pub fn trie_root(input: I) -> H::Out where trie_root_inner::(input, false) } -fn trie_root_inner(input: I, no_ext: bool) -> H::Out where +fn trie_root_inner(input: I, no_extension: bool) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -137,13 +137,13 @@ fn trie_root_inner(input: I, no_ext: bool) -> H::Out where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream, no_ext); + build_trie::(&input, 0, &mut stream, no_extension); H::hash(&stream.out()) } /// Variant of `trie_root` for patricia trie without extension node. /// See [`trie_root`]. -pub fn trie_root_no_ext(input: I) -> H::Out where +pub fn trie_root_no_extension(input: I) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -167,7 +167,7 @@ pub fn unhashed_trie(input: I) -> Vec where unhashed_trie_inner::(input, false) } -fn unhashed_trie_inner(input: I, no_ext: bool) -> Vec where +fn unhashed_trie_inner(input: I, no_extension: bool) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -196,13 +196,13 @@ fn unhashed_trie_inner(input: I, no_ext: bool) -> Vec where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream, no_ext); + build_trie::(&input, 0, &mut stream, no_extension); stream.out() } /// Variant of `unhashed_trie` for patricia trie without extension node. /// See [`unhashed_trie`]. -pub fn unhashed_trie_no_ext(input: I) -> Vec where +pub fn unhashed_trie_no_extension(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -247,7 +247,7 @@ pub fn sec_trie_root(input: I) -> H::Out where /// Takes a slice of key/value tuples where the key is a slice of nibbles /// and encodes it into the provided `Stream`. -fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ext: bool) where +fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_extension: bool) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, @@ -266,13 +266,13 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex // shared with the first key. // e.g. input = [ [1'7'3'10'12'13], [1'7'3'], [1'7'7'8'9'] ] => [1'7'] is common => 2 let shared_nibble_count = input.iter().skip(1).fold(key.len(), |acc, &(ref k, _)| { - cmp::min( shared_prefix_len(key, k.as_ref()), acc ) + cmp::min( shared_prefix_length(key, k.as_ref()), acc ) }); // Add an extension node if the number of shared nibbles is greater // than what we saw on the last call (`cursor`): append the new part // of the path then recursively append the remainder of all items // who had this partial key. - let (cursor, o_branch_slice) = if no_ext { + let (cursor, o_branch_slice) = if no_extension { if shared_nibble_count > cursor { (shared_nibble_count, Some(&key[cursor..shared_nibble_count])) } else { @@ -280,7 +280,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex } } else if shared_nibble_count > cursor { stream.append_extension(&key[cursor..shared_nibble_count]); - build_trie_trampoline::(input, shared_nibble_count, stream, no_ext); + build_trie_trampoline::(input, shared_nibble_count, stream, no_extension); return; } else { (cursor, None) }; @@ -319,7 +319,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex &input[begin..(begin + count)], cursor + 1, stream, - no_ext, + no_extension, ); begin += count; } else { @@ -336,7 +336,7 @@ fn build_trie_trampoline( input: &[(A, B)], cursor: usize, stream: &mut S, - no_ext: bool, + no_extension: bool, ) where A: AsRef<[u8]>, B: AsRef<[u8]>, @@ -344,6 +344,6 @@ fn build_trie_trampoline( S: TrieStream, { let mut substream = S::new(); - build_trie::(input, cursor, &mut substream, no_ext); + build_trie::(input, cursor, &mut substream, no_extension); stream.append_substream::(substream); } From 68e38329eeebcc6475eab35ccdfb7185922ea02a Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 26 Jul 2019 17:23:31 +0200 Subject: [PATCH 106/120] Line length bellow 100. --- hash-db/src/lib.rs | 25 ++- memory-db/src/lib.rs | 7 +- test-support/reference-trie/src/lib.rs | 216 ++++++++++++++------ trie-db/fuzz/src/lib.rs | 2 +- trie-db/src/fatdb.rs | 10 +- trie-db/src/fatdbmut.rs | 13 +- trie-db/src/iter_build.rs | 12 +- trie-db/src/lib.rs | 19 +- trie-db/src/lookup.rs | 8 +- trie-db/src/nibble/mod.rs | 14 +- trie-db/src/nibble/nibbleslice.rs | 7 +- trie-db/src/nibble/nibblevec.rs | 84 +++++--- trie-db/src/node.rs | 21 +- trie-db/src/recorder.rs | 4 +- trie-db/src/sectriedb.rs | 8 +- trie-db/src/sectriedbmut.rs | 13 +- trie-db/src/triedb.rs | 95 +++++++-- trie-db/src/triedbmut.rs | 267 ++++++++++++++++++------- 18 files changed, 594 insertions(+), 231 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 70ca2acd..594969ad 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -52,7 +52,8 @@ pub type Prefix<'a> = (&'a[u8], (u8, u8)); /// `Out` associated type with the necessary bounds. pub trait Hasher: Sync + Send { /// The output type of the `Hasher` - type Out: AsRef<[u8]> + AsMut<[u8]> + Default + MaybeDebug + PartialEq + Eq + hash::Hash + Send + Sync + Clone + Copy; + type Out: AsRef<[u8]> + AsMut<[u8]> + Default + MaybeDebug + PartialEq + Eq + + hash::Hash + Send + Sync + Clone + Copy; /// What to use to build `HashMap`s with this `Hasher` type StdHasher: Sync + Send + Default + hash::Hasher; /// The length in bytes of the `Hasher` output @@ -122,8 +123,9 @@ pub trait HashDB: Send + Sync + AsHashDB { /// Like `insert()`, except you provide the key and the data is all moved. fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T); - /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may - /// happen without the data being eventually being inserted into the DB. It can be "owed" more than once. + /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of + /// `insert()`s may happen without the data being eventually being inserted into the DB. + /// It can be "owed" more than once. fn remove(&mut self, key: &H::Out, prefix: Prefix); } @@ -139,12 +141,16 @@ pub trait HashDBRef { impl<'a, H: Hasher, T> HashDBRef for &'a dyn HashDB { fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + HashDB::contains(*self, key, prefix) + } } impl<'a, H: Hasher, T> HashDBRef for &'a mut dyn HashDB { fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + HashDB::contains(*self, key, prefix) + } } /// Upcast trait for HashDB. @@ -163,8 +169,11 @@ pub trait AsPlainDB { fn as_plain_db_mut<'a>(&'a mut self) -> &'a mut (dyn PlainDB + 'a); } -// NOTE: There used to be a `impl AsHashDB for T` but that does not work with generics. See https://stackoverflow.com/questions/48432842/implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im -// This means we need concrete impls of AsHashDB in several places, which somewhat defeats the point of the trait. +// NOTE: There used to be a `impl AsHashDB for T` but that does not work with generics. +// See https://stackoverflow.com/questions/48432842/ +// implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im +// This means we need concrete impls of AsHashDB in several places, which somewhat defeats +// the point of the trait. impl<'a, H: Hasher, T> AsHashDB for &'a mut dyn HashDB { fn as_hash_db(&self) -> &dyn HashDB { &**self } fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB + 'b) { &mut **self } @@ -174,4 +183,4 @@ impl<'a, H: Hasher, T> AsHashDB for &'a mut dyn HashDB { impl<'a, K, V> AsPlainDB for &'a mut dyn PlainDB { fn as_plain_db(&self) -> &dyn PlainDB { &**self } fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn PlainDB + 'b) { &mut **self } -} +} \ No newline at end of file diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 909e9046..8099de8b 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -655,7 +655,10 @@ mod tests { assert_eq!(main.raw(&remove_key, EMPTY_PREFIX).unwrap(), (&"doggo".as_bytes().to_vec(), 0)); assert_eq!(main.raw(&insert_key, EMPTY_PREFIX).unwrap(), (&"arf".as_bytes().to_vec(), 2)); - assert_eq!(main.raw(&negative_remove_key, EMPTY_PREFIX).unwrap(), (&"negative".as_bytes().to_vec(), -2)); + assert_eq!( + main.raw(&negative_remove_key, EMPTY_PREFIX).unwrap(), + (&"negative".as_bytes().to_vec(), -2), + ); } #[test] @@ -664,4 +667,4 @@ mod tests { let hashed_null_node = KeccakHasher::hash(&[0u8][..]); assert_eq!(db.insert(EMPTY_PREFIX, &[0u8][..]), hashed_null_node); } -} +} \ No newline at end of file diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index dbfe72b4..4c30c5d5 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -218,7 +218,10 @@ const BRANCH_WITH_MASK_NO_EXT: u8 = 0b_11 << 6; /// cannot handle a number of nibbles that is zero or greater than 125 and if /// you attempt to do so *IT WILL PANIC*. fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator + 'a { - debug_assert!(nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); + debug_assert!( + nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, + "nibbles length too long. what kind of size of key are you trying to include in the trie!?!" + ); let first_byte = if leaf { LEAF_NODE_OFFSET } else { @@ -237,7 +240,10 @@ enum NodeKindNoExt { } /// Create a leaf/branch node, encoding a number of nibbles. -fn fuse_nibbles_node_no_extension<'a>(nibbles: &'a [u8], kind: NodeKindNoExt) -> impl Iterator + 'a { +fn fuse_nibbles_node_no_extension<'a>( + nibbles: &'a [u8], + kind: NodeKindNoExt, +) -> impl Iterator + 'a { let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibbles.len()); let iter_start = match kind { @@ -256,7 +262,11 @@ fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8 result } -fn branch_node_buffered>(has_value: bool, has_children: I, dest: &mut[u8]) { +fn branch_node_buffered>( + has_value: bool, + has_children: I, + dest: &mut[u8], +) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { @@ -298,7 +308,12 @@ impl TrieStream for ReferenceTrieStream { value.encode_to(&mut self.buffer); } - fn begin_branch(&mut self, maybe_key: Option<&[u8]>, maybe_value: Option<&[u8]>, has_children: impl Iterator) { + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator, + ) { self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); if let Some(partial) = maybe_key { // should not happen @@ -354,9 +369,13 @@ impl TrieStream for ReferenceTrieStreamNoExt { ) { if let Some(partial) = maybe_key { if maybe_value.is_some() { - self.buffer.extend(fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchWithValue)); + self.buffer.extend( + fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchWithValue) + ); } else { - self.buffer.extend(fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchNoValue)); + self.buffer.extend( + fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchNoValue) + ); } let bitmap = branch_node_bit_mask(has_children); self.buffer.extend([bitmap.0, bitmap.1].iter()); @@ -407,8 +426,10 @@ impl Encode for NodeHeader { NodeHeader::Null => output.push_byte(EMPTY_TRIE), NodeHeader::Branch(true) => output.push_byte(BRANCH_NODE_WITH_VALUE), NodeHeader::Branch(false) => output.push_byte(BRANCH_NODE_NO_VALUE), - NodeHeader::Leaf(nibble_count) => output.push_byte(LEAF_NODE_OFFSET + *nibble_count as u8), - NodeHeader::Extension(nibble_count) => output.push_byte(EXTENSION_NODE_OFFSET + *nibble_count as u8), + NodeHeader::Leaf(nibble_count) => + output.push_byte(LEAF_NODE_OFFSET + *nibble_count as u8), + NodeHeader::Extension(nibble_count) => + output.push_byte(EXTENSION_NODE_OFFSET + *nibble_count as u8), } } } @@ -504,8 +525,10 @@ impl Decode for NodeHeader { EMPTY_TRIE => NodeHeader::Null, BRANCH_NODE_NO_VALUE => NodeHeader::Branch(false), BRANCH_NODE_WITH_VALUE => NodeHeader::Branch(true), - i @ LEAF_NODE_OFFSET ..= LEAF_NODE_LAST => NodeHeader::Leaf((i - LEAF_NODE_OFFSET) as usize), - i @ EXTENSION_NODE_OFFSET ..= EXTENSION_NODE_LAST => NodeHeader::Extension((i - EXTENSION_NODE_OFFSET) as usize), + i @ LEAF_NODE_OFFSET ..= LEAF_NODE_LAST => + NodeHeader::Leaf((i - LEAF_NODE_OFFSET) as usize), + i @ EXTENSION_NODE_OFFSET ..= EXTENSION_NODE_LAST => + NodeHeader::Extension((i - EXTENSION_NODE_OFFSET) as usize), }) } } @@ -517,9 +540,12 @@ impl Decode for NodeHeaderNoExt { return Some(NodeHeaderNoExt::Null); } match i & (0b11 << 6) { - LEAF_PREFIX_MASK_NO_EXT => Some(NodeHeaderNoExt::Leaf(decode_size(i, input)?)), - BRANCH_WITHOUT_MASK_NO_EXT => Some(NodeHeaderNoExt::Branch(false, decode_size(i, input)?)), - BRANCH_WITH_MASK_NO_EXT => Some(NodeHeaderNoExt::Branch(true, decode_size(i, input)?)), + LEAF_PREFIX_MASK_NO_EXT => + Some(NodeHeaderNoExt::Leaf(decode_size(i, input)?)), + BRANCH_WITHOUT_MASK_NO_EXT => + Some(NodeHeaderNoExt::Branch(false, decode_size(i, input)?)), + BRANCH_WITH_MASK_NO_EXT => + Some(NodeHeaderNoExt::Branch(true, decode_size(i, input)?)), // do not allow any special encoding _ => None, } @@ -578,7 +604,12 @@ fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec>(partial: I, nibble_count: usize, offset: u8, over: u8) -> Vec { +fn partial_from_iterator_to_key>( + partial: I, + nibble_count: usize, + offset: u8, + over: u8, +) -> Vec { assert!(nibble_count < over as usize); let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); output.push(offset + nibble_count as u8); @@ -586,14 +617,21 @@ fn partial_from_iterator_to_key>(partial: I output } -fn partial_from_iterator_encode>(partial: I, nibble_count: usize, node_kind: NodeKindNoExt) -> Vec { +fn partial_from_iterator_encode>( + partial: I, + nibble_count: usize, + node_kind: NodeKindNoExt, +) -> Vec { let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); let mut output = Vec::with_capacity(3 + (nibble_count / N::NIBBLE_PER_BYTE)); match node_kind { - NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), - NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), - NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + NodeKindNoExt::Leaf => + NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => + NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => + NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; output.extend(partial); output @@ -609,9 +647,12 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> V let mut output = Vec::with_capacity(3 + partial.1.len()); match node_kind { - NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), - NodeKindNoExt::BranchWithValue => NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), - NodeKindNoExt::BranchNoValue => NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + NodeKindNoExt::Leaf => + NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => + NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => + NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if number_nibble_encoded > 0 { output.push(N::masked_right(number_nibble_encoded as u8, (partial.0).1)); @@ -622,8 +663,9 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> V // NOTE: what we'd really like here is: // `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` -// but due to the current limitations of Rust const evaluation we can't -// do `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. Perhaps one day soon? +// but due to the current limitations of Rust const evaluation we can't do +// `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. +// Perhaps one day soon? impl< H: Hasher, N: NibbleOps, @@ -640,11 +682,13 @@ impl< match NodeHeader::decode(input).ok_or(ReferenceError::BadFormat)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - let bitmap_slice = take(input, BITMAP::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap_slice = take(input, BITMAP::ENCODED_LEN) + .ok_or(ReferenceError::BadFormat)?; let bitmap = BITMAP::decode(&bitmap_slice[..])?; let value = if has_value { - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) } else { None @@ -653,9 +697,10 @@ impl< let child_val = &**input; let mut ix = 0; children.as_mut()[0] = ix; - for i in 0..N::NIBBLE_LEN { + for i in 0..N::NIBBLE_LENGTH { if bitmap.value_at(i) { - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; let _ = take(input, count); ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; } @@ -664,20 +709,30 @@ impl< Ok(Node::Branch((children, child_val), value)) } NodeHeader::Extension(nibble_count) => { - let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + let nibble_data = take( + input, + (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + ).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, N::number_padding(nibble_count)); - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; - Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; + Ok(Node::Extension(nibble_slice, take(input, count) + .ok_or(ReferenceError::BadFormat)?)) } NodeHeader::Leaf(nibble_count) => { - let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::number_padding(nibble_count)); - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; - Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) + let nibble_data = take( + input, + (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + ).ok_or(ReferenceError::BadFormat)?; + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + N::number_padding(nibble_count), + ); + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; + Ok(Node::Leaf(nibble_slice, take(input, count) + .ok_or(ReferenceError::BadFormat)?)) } } } @@ -706,11 +761,21 @@ impl< output } - fn extension_node(partial: impl Iterator, number_nibble: usize, child: ChildReference<::Out>) -> Vec { - let mut output = partial_from_iterator_to_key::(partial, number_nibble, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child: ChildReference<::Out>, + ) -> Vec { + let mut output = partial_from_iterator_to_key::( + partial, + number_nibble, + EXTENSION_NODE_OFFSET, + EXTENSION_NODE_OVER, + ); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), - ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), + ChildReference::Inline(inline_data, len) => + (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), }; output } @@ -726,7 +791,7 @@ impl< } else { false }; - branch_node_buffered::(have_value, children.map(|maybe_child| match maybe_child.borrow() { + let has_children = children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -736,7 +801,8 @@ impl< true } None => false, - }), prefix.as_mut()); + }); + branch_node_buffered::(have_value, has_children, prefix.as_mut()); output[0..BITMAP::ENCODED_LEN + 1].copy_from_slice(prefix.as_ref()); output } @@ -768,19 +834,26 @@ impl< match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { - let number_nibble_encoded = nibble_count % N::NIBBLE_PER_BYTE; - if number_nibble_encoded > 0 - && N::masked_left((N::NIBBLE_PER_BYTE - number_nibble_encoded) as u8, input[0]) != 0 { + let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; + let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; + // check that the padding is valid (if any) + if nibble_with_padding > 0 && N::masked_left(padding_length as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } - let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + let nibble_data = take( + input, + (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + ).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, N::number_padding(nibble_count)); - let bitmap_slice = take(input, BITMAP::ENCODED_LEN).ok_or(ReferenceError::BadFormat)?; + let bitmap_slice = take( + input, + BITMAP::ENCODED_LEN, + ).ok_or(ReferenceError::BadFormat)?; let bitmap = BITMAP::decode(&bitmap_slice[..])?; let value = if has_value { - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; Some(take(input, count).ok_or(ReferenceError::BadFormat)?) } else { None @@ -789,9 +862,10 @@ impl< let child_val = &**input; let mut ix = 0; children.as_mut()[0] = ix; - for i in 0..N::NIBBLE_LEN { + for i in 0..N::NIBBLE_LENGTH { if bitmap.value_at(i) { - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; let _ = take(input, count); ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; } @@ -800,15 +874,20 @@ impl< Ok(Node::NibbledBranch(nibble_slice, (children, child_val), value)) } NodeHeaderNoExt::Leaf(nibble_count) => { - let number_nibble_encoded = nibble_count % N::NIBBLE_PER_BYTE; - if number_nibble_encoded > 0 && N::masked_left((N::NIBBLE_PER_BYTE - number_nibble_encoded) as u8, input[0]) != 0 { + let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; + let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; + // check that the padding is valid (if any) + if nibble_with_padding > 0 && N::masked_left(padding_length as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } - let nibble_data = take(input, (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE) - .ok_or(ReferenceError::BadFormat)?; + let nibble_data = take( + input, + (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + ).ok_or(ReferenceError::BadFormat)?; let nibble_slice = NibbleSlice::new_offset(nibble_data, N::number_padding(nibble_count)); - let count = >::decode(input).ok_or(ReferenceError::BadFormat)?.0 as usize; + let count = >::decode(input) + .ok_or(ReferenceError::BadFormat)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) } } @@ -854,9 +933,17 @@ impl< maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { - partial_from_iterator_encode::(partial, number_nibble, NodeKindNoExt::BranchWithValue) + partial_from_iterator_encode::( + partial, + number_nibble, + NodeKindNoExt::BranchWithValue, + ) } else { - partial_from_iterator_encode::(partial, number_nibble, NodeKindNoExt::BranchNoValue) + partial_from_iterator_encode::( + partial, + number_nibble, + NodeKindNoExt::BranchNoValue, + ) }; let bitmap_index = output.len(); let mut bitmap: BITMAP::Buffer = Default::default(); @@ -1213,11 +1300,6 @@ pub fn compare_no_extension_insert_remove( *t.root() }; } - /*{ - let db : &dyn hash_db::HashDB<_,_> = &memdb; - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); - println!("{:x?}",t); - }*/ let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. @@ -1228,8 +1310,10 @@ pub fn compare_no_extension_insert_remove( fn too_big_nibble_length () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1]; - let enc = as NodeCodec>::leaf_node(((0,0),&input), &[1]); - let dec = as NodeCodec>::decode(&enc).unwrap(); + let enc = as NodeCodec> + ::leaf_node(((0,0),&input), &[1]); + let dec = as NodeCodec> + ::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl,_) = dec { Some(sl) } else { None }; @@ -1259,4 +1343,4 @@ fn size_encode_limit_values () { let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); assert_eq!(s_dec, Some(sizes[i])); } -} +} \ No newline at end of file diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index a9519e60..e6b4922b 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -163,4 +163,4 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); compare_no_extension_insert_remove(data, memdb); -} +} \ No newline at end of file diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 231bae31..600b58fb 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -58,7 +58,8 @@ where self.raw.contains(L::H::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) + -> Result, TrieHash, CError> where 'a: 'key { self.raw.get_with(L::H::hash(key).as_ref(), query) @@ -116,7 +117,12 @@ where .map(|res| { res.map(|(hash, value)| { let aux_hash = L::H::hash(&hash); - (self.trie.db().get(&aux_hash, Default::default()).expect("Missing fatdb hash").into_vec(), value) + ( + self.trie.db().get(&aux_hash, Default::default()) + .expect("Missing fatdb hash") + .into_vec(), + value, + ) }) }) } diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index ab902431..62a4b1f5 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -76,7 +76,11 @@ where self.raw.get(L::H::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { let hash = L::H::hash(key); let out = self.raw.insert(hash.as_ref(), value)?; let db = self.raw.db_mut(); @@ -120,7 +124,10 @@ mod test { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&KeccakHasher::hash(&[0x01u8, 0x23])), Ok(Some(DBValue::from_slice(&[0x01u8, 0x23])))); + assert_eq!( + t.get(&KeccakHasher::hash(&[0x01u8, 0x23])), + Ok(Some(DBValue::from_slice(&[0x01u8, 0x23]))), + ); } #[test] @@ -138,4 +145,4 @@ mod test { t.remove(&key).unwrap(); assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), None); } -} +} \ No newline at end of file diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index c58b7b21..29ab65db 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -37,7 +37,7 @@ macro_rules! exponential_out { type CacheNode = Option>; -/// A builder for fix constant length cache, should match `NibbleOps` `NIBBLE_LEN`. +/// A builder for fix constant length cache, should match `NibbleOps` `NIBBLE_LENGTH`. pub trait CacheBuilder { /// Size of cache. const SIZE: usize; @@ -794,8 +794,8 @@ mod test { #[test] fn two_bytes_nibble_length () { let data = vec![ - (vec![00u8],vec![0]), - (vec![01u8;64],vec![0;32]), + (vec![00u8], vec![0]), + (vec![01u8;64], vec![0;32]), ]; compare_implementations_no_extension(data.clone()); compare_implementations_no_extension_prefixed(data.clone()); @@ -804,15 +804,13 @@ mod test { #[should_panic] fn too_big_nibble_length_old () { compare_implementations_h(vec![ - (vec![01u8;64],vec![0;32]), + (vec![01u8;64], vec![0;32]), ]); } #[test] fn too_big_nibble_length_new () { - // truncate keep things working in both situation (but will conflict for multiple common prefix - // val!!) compare_implementations_no_extension(vec![ - (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1],vec![0;32]), + (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1], vec![0;32]), ]); } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 7939d173..a4b22560 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -124,8 +124,10 @@ pub enum TrieError { impl fmt::Display for TrieError where T: MaybeDebug, E: MaybeDebug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {:?}", root), - TrieError::IncompleteDatabase(ref missing) => write!(f, "Database missing expected key: {:?}", missing), + TrieError::InvalidStateRoot(ref root) => + write!(f, "Invalid state root: {:?}", root), + TrieError::IncompleteDatabase(ref missing) => + write!(f, "Database missing expected key: {:?}", missing), TrieError::DecoderError(ref hash, ref decoder_err) => { write!(f, "Decoding failed for hash {:?}; err: {:?}", hash, decoder_err) } @@ -144,7 +146,8 @@ impl Error for TrieError where T: fmt::Debug, E: Error { } } -/// Trie result type. Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. +/// Trie result type. +/// Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. pub type Result = ::core_::result::Result>>; @@ -246,7 +249,11 @@ pub trait TrieMut { /// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing /// `key` from the trie. Returns the old value associated with this key, if it existed. - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError>; + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError>; /// Remove a `key` from the trie. Equivalent to making it equal to the empty /// value. Returns the old value associated with this key, if it existed. @@ -468,4 +475,6 @@ pub trait TrieOps: Sized + TrieLayout { /// Alias accessor to hasher hash output type from a `TrieLayout`. pub type TrieHash = <::H as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. -pub type CError = <::C as NodeCodec<::H, ::N>>::Error; +pub type CError = < + ::C as NodeCodec<::H, ::N> +>::Error; \ No newline at end of file diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 59bc2e3b..def2ba4e 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -40,7 +40,10 @@ where { /// Look up the given key. If the value is found, it will be passed to the given /// function to decode or copy. - pub fn look_up(mut self, key: NibbleSlice) -> Result, TrieHash, CError> { + pub fn look_up( + mut self, + key: NibbleSlice, + ) -> Result, TrieHash, CError> { let mut partial = key; let mut hash = self.hash; let mut key_nibbles = 0; @@ -101,7 +104,8 @@ where match partial.len() == slice.len() { true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children.0.slice_at(partial.at(slice.len()) as usize, children.1) { + false => match children.0 + .slice_at(partial.at(slice.len()) as usize, children.1) { Some(x) => { node_data = x; partial = partial.mid(slice.len() + 1); diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index d6b37f02..cfe26276 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -38,7 +38,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Number of nibble per byte. const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; /// Number of child for a branch (trie radix). - const NIBBLE_LEN : usize = TWO_EXP[Self::BIT_PER_NIBBLE]; + const NIBBLE_LENGTH : usize = TWO_EXP[Self::BIT_PER_NIBBLE]; //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); /// Padding bitmasks, internally use for working on padding byte. /// Length of this array is `Self::BIT_PER_NIBBLE`. @@ -55,8 +55,8 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// } /// ``` const PADDING_BITMASK: &'static [(u8, usize)]; - /// Last nibble index as u8, just a convenience constant for iteration on all nibble. - const LAST_N_IX_U8: u8 = (Self::NIBBLE_PER_BYTE - 1) as u8; + /// Last nibble index as u8, a convenience constant for iteration on all nibble. + const LAST_NIBBLE_INDEX: u8 = (Self::NIBBLE_PER_BYTE - 1) as u8; /// Buffer type for slice index store (we do not include /// directly slice in it to avoid lifetime in @@ -280,7 +280,7 @@ pub trait ChildSliceIndex: AsRef<[usize]> + Clone { /// Constant length for the number of children. - const NIBBLE_LEN : usize; + const NIBBLE_LENGTH : usize; /// Constant size of header /// Should only be use for inner implementation. const CONTENT_HEADER_SIZE: usize; @@ -311,7 +311,7 @@ pub struct IterChildSliceIndex<'a, CS>(&'a CS, usize, &'a[u8]); impl<'a, CS: ChildSliceIndex> Iterator for IterChildSliceIndex<'a, CS> { type Item = Option<&'a[u8]>; fn next(&mut self) -> Option { - if self.1 == CS::NIBBLE_LEN { + if self.1 == CS::NIBBLE_LENGTH { return None; } self.1 += 1; @@ -340,9 +340,9 @@ macro_rules! child_slice_index { impl ChildSliceIndex for $me { const CONTENT_HEADER_SIZE: usize = $pre; - const NIBBLE_LEN: usize = $size; + const NIBBLE_LENGTH: usize = $size; } } } child_slice_index!(ChildSliceIndex16, 16, 1); -child_slice_index!(ChildSliceIndex4, 4, 1); +child_slice_index!(ChildSliceIndex4, 4, 1); \ No newline at end of file diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 77e8cf9a..915673a2 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -78,7 +78,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { // aligned let start = self.offset / N::NIBBLE_PER_BYTE; let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; - (self.offset % N::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end])) + ( + self.offset % N::NIBBLE_PER_BYTE, + ElasticArray36::from_slice(&self.data[start..end]), + ) } else { // unaligned let start = self.offset / N::NIBBLE_PER_BYTE; @@ -446,4 +449,4 @@ mod tests { assert!(n >= m.mid(4)); assert!(n <= m.mid(4)); } -} +} \ No newline at end of file diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 2b9b9ecc..893caed3 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -59,7 +59,8 @@ impl NibbleVec { if i == 0 { self.inner.push(N::push_at_left(0, nibble, 0)); } else { - let dest = self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed"); + let dest = self.inner.last_mut() + .expect("len != 0 since len % 2 != 0; inner has a last element; qed"); *dest = N::push_at_left(i as u8, nibble, *dest); } self.len += 1; @@ -119,13 +120,15 @@ impl NibbleVec { let last_index = self.len / N::NIBBLE_PER_BYTE; if offset > 0 { let (s1, s2) = N::split_shifts(offset); - self.inner[last_index] = N::masked_left(offset as u8, self.inner[last_index]) | (v.inner[0] >> s2); - (0..v.inner.len() - 1).for_each(|i|self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); + self.inner[last_index] = N::masked_left(offset as u8, self.inner[last_index]) + | (v.inner[0] >> s2); + (0..v.inner.len() - 1) + .for_each(|i| self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); if final_offset > 0 { self.inner.push(v.inner[v.inner.len() - 1] << s1); } } else { - (0..v.inner.len()).for_each(|i|self.inner.push(v.inner[i])); + (0..v.inner.len()).for_each(|i| self.inner.push(v.inner[i])); } self.len += v.len; } @@ -142,10 +145,13 @@ impl NibbleVec { } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - self.inner[kend] = N::masked_left((N::NIBBLE_PER_BYTE - pad) as u8, self.inner[kend]); + self.inner[kend] = N::masked_left( + (N::NIBBLE_PER_BYTE - pad) as u8, + self.inner[kend], + ); let (s1, s2) = N::split_shifts(pad); self.inner[kend] |= sl[0] >> s1; - (0..sl.len() - 1).for_each(|i|self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); + (0..sl.len() - 1).for_each(|i| self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); self.inner.push(sl[sl.len() - 1] << s2); } } @@ -157,13 +163,13 @@ impl NibbleVec { /// Can be slow. pub(crate) fn append_slice_nibble( &mut self, - o_sl: Option<&NibbleSlice>, + o_slice: Option<&NibbleSlice>, o_index: Option, ) -> usize { let mut res = 0; - if let Some(sl) = o_sl { - self.append_partial(sl.right()); - res += sl.len(); + if let Some(slice) = o_slice { + self.append_partial(slice.right()); + res += slice.len(); } if let Some(ix) = o_index { self.push(ix); @@ -171,6 +177,7 @@ impl NibbleVec { } res } + /// Utility function for `append_slice_nibble` after a clone. /// Can be slow. pub(crate) fn clone_append_slice_nibble( @@ -244,18 +251,49 @@ mod tests { } #[test] fn append_partial() { - append_partial_inner::(&[1,2,3], &[], ((1,1), &[0x23])); - append_partial_inner::(&[1,2,3], &[1], ((0,0), &[0x23])); - append_partial_inner::(&[0,1,2,3], &[0], ((1,1), &[0x23])); - append_partial_inner::(&[1, 0, 2, 0, 3], &[], ((1,1), &[0x23])); - append_partial_inner::(&[1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((1,1), &[0x23, 0x12])); - append_partial_inner::(&[2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((2,0b1001), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[], ((3,0b111001), &[0x23, 0x12])); - append_partial_inner::(&[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3], ((1,1), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], ((1,1), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2, 3], ((2,0b1001), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((1,1), &[0x23, 0x12])); - append_partial_inner::(&[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], &[3, 2], ((3,0b111001), &[0x23, 0x12])); + append_partial_inner::(&[1,2,3], &[], ((1, 1), &[0x23])); + append_partial_inner::(&[1,2,3], &[1], ((0, 0), &[0x23])); + append_partial_inner::(&[0,1,2,3], &[0], ((1, 1), &[0x23])); + append_partial_inner::(&[1, 0, 2, 0, 3], &[], ((1, 1), &[0x23])); + append_partial_inner::( + &[1, 0, 2, 0, 3, 0, 1, 0, 2], + &[], + ((1,1), &[0x23, 0x12]), + ); + append_partial_inner::( + &[2, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[], + ((2, 0b1001), &[0x23, 0x12]), + ); + append_partial_inner::( + &[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[], + ((3, 0b111001), &[0x23, 0x12])); + append_partial_inner::( + &[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[3], + ((1, 1), &[0x23, 0x12]), + ); + append_partial_inner::( + &[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[3, 2, 3], + ((1, 1), &[0x23, 0x12]), + ); + append_partial_inner::( + &[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[3, 2, 3], + ((2, 0b1001), &[0x23, 0x12]), + ); + append_partial_inner::( + &[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[3, 2], + ((1, 1), &[0x23, 0x12]), + ); + append_partial_inner::( + &[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], + &[3, 2], + ((3, 0b111001), &[0x23, 0x12]), + ); } fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8,u8), &[u8])) { @@ -290,4 +328,4 @@ mod tests { test_trun(&[1,2,3], 4, (&[], 0)); } -} +} \ No newline at end of file diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 9bb1647d..f70c41ec 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -71,7 +71,12 @@ impl Branch { i += 1; data.extend_from_slice(&ix.to_ne_bytes()[..]); } - Branch { data, data_index, ubounds_index, child_head: N::ChildSliceIndex::CONTENT_HEADER_SIZE } + Branch { + data, + data_index, + ubounds_index, + child_head: N::ChildSliceIndex::CONTENT_HEADER_SIZE, + } } /// Get the node value, if any. @@ -134,10 +139,14 @@ impl<'a, N: NibbleOps> From> for OwnedNode { fn from(node: Node<'a, N>) -> Self { match node { Node::Empty => OwnedNode::Empty, - Node::Leaf(k, v) => OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), - Node::Extension(k, child) => OwnedNode::Extension(k.into(), DBValue::from_slice(child)), - Node::Branch(c, val) => OwnedNode::Branch(Branch::new::(&c, val)), - Node::NibbledBranch(k, c, val) => OwnedNode::NibbledBranch(k.into(), Branch::new::(&c, val)), + Node::Leaf(k, v) => + OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), + Node::Extension(k, child) => + OwnedNode::Extension(k.into(), DBValue::from_slice(child)), + Node::Branch(c, val) => + OwnedNode::Branch(Branch::new::(&c, val)), + Node::NibbledBranch(k, c, val) => + OwnedNode::NibbledBranch(k.into(), Branch::new::(&c, val)), } } -} +} \ No newline at end of file diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index 28879e24..6c0bb4b6 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -184,8 +184,8 @@ mod tests { 254, 16, 83, 28, 5, 111, 103, 12, 99, 97, 116, 52, 11, 111, 116, 100, 111, 103, 24, 104, 111, 116, 99, 97, 116, 52, 11, 110, 115, 101, 114, 116, 24, 114, 101, 109, 111, 118, 101, 124, 254, 192, 0, 64, 10, 5, 116, 116, 101, 114, 36, 99, 111, 110, 102, - 117, 115, 105, 111, 110, 40, 8, 5, 110, 99, 104, 16, 116, 105, 109, 101, 52, 11, 111, - 116, 100, 111, 103, 24, 110, 111, 116, 99, 97, 116 + 117, 115, 105, 111, 110, 40, 8, 5, 110, 99, 104, 16, 116, 105, 109, 101, 52, 11, + 111, 116, 100, 111, 103, 24, 110, 111, 116, 99, 97, 116 ] ]); } diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 5b300482..ddf1df2e 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -66,7 +66,11 @@ where self.raw.contains(L::H::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> + fn get_with<'a, 'key, Q: Query>( + &'a self, + key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> where 'a: 'key { self.raw.get_with(L::H::hash(key).as_ref(), query) @@ -100,4 +104,4 @@ mod test { let t = RefSecTrieDB::new(&db, &root).unwrap(); assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); } -} +} \ No newline at end of file diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index 8cbc6c9c..28b45a5e 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -17,7 +17,8 @@ use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// -/// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object. +/// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` +/// object. pub struct SecTrieDBMut<'db, L> where L: TrieLayout @@ -75,7 +76,10 @@ where self.raw.get(&L::H::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { + fn insert( + &mut self, key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { self.raw.insert(&L::H::hash(key).as_ref(), value) } @@ -101,6 +105,9 @@ mod test { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&KeccakHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); + assert_eq!( + t.get(&KeccakHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), + DBValue::from_slice(&[0x01u8, 0x23]), + ); } } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index eb73c603..f3f4f250 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -17,7 +17,8 @@ use nibble::{NibbleSlice, NibbleOps, ChildSliceIndex}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; -use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, TrieLayout, CError, TrieHash}; +use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, + TrieLayout, CError, TrieHash}; use super::nibble::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; @@ -100,8 +101,8 @@ where } /// 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. If `is_root_data` then this is root-data and + /// This could be a simple identity operation in the case that the node is sufficiently small, + /// but may require a database lookup. If `is_root_data` then this is root-data and /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. fn get_raw_or_lookup( @@ -187,7 +188,8 @@ where .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: self.partial_key.clone_append_slice_nibble(Some(&slice), None), + partial_key: self.partial_key + .clone_append_slice_nibble(Some(&slice), None), index: None, }) .finish(), @@ -199,7 +201,8 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: self.partial_key.clone_append_slice_nibble(None, Some(i as u8)), + partial_key: self.partial_key + .clone_append_slice_nibble(None, Some(i as u8)), }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -218,7 +221,8 @@ where trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: self.partial_key.clone_append_slice_nibble(Some(&slice), Some(i as u8)), + partial_key: self.partial_key + .clone_append_slice_nibble(Some(&slice), Some(i as u8)), }).collect(); match (f.debug_struct("Node::NibbledBranch"), self.index) { (ref mut d, Some(ref i)) => d.field("index", i), @@ -293,7 +297,7 @@ impl Crumb { | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), (&Status::AtChild(x), &OwnedNode::Branch(_)) | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) - if x < N::NIBBLE_LEN => Status::AtChild(x + 1), + if x < N::NIBBLE_LENGTH => Status::AtChild(x + 1), _ => Status::Exiting, } } @@ -309,7 +313,11 @@ pub struct TrieDBIterator<'a, L: TrieLayout> { impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { - let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: NibbleVec::new() }; + let mut r = TrieDBIterator { + db, + trail: Vec::with_capacity(8), + key_nibbles: NibbleVec::new(), + }; db.root_data().and_then(|root_data| r.descend(&root_data))?; Ok(r) } @@ -352,7 +360,9 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { self.key_nibbles.append_partial(slice.right()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); - let data = self.db.get_raw_or_lookup(&*item, key.back(full_key_nibbles).left())?; + let data = self.db + .get_raw_or_lookup(&*item, key.back(full_key_nibbles) + .left())?; data } else { self.descend(&node_data)?; @@ -377,7 +387,9 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { full_key_nibbles += 1; partial = partial.mid(1); - let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; + let child = self.db + .get_raw_or_lookup(&*child, key.back(full_key_nibbles) + .left())?; child } else { return Ok(()) @@ -407,7 +419,9 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { full_key_nibbles += slice.len() + 1; partial = partial.mid(slice.len() + 1); - let child = self.db.get_raw_or_lookup(&*child, key.back(full_key_nibbles).left())?; + let child = self.db + .get_raw_or_lookup(&*child, key.back(full_key_nibbles) + .left())?; child } else { return Ok(()) @@ -490,8 +504,8 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { } IterStep::PopTrail }, - (Status::At, &OwnedNode::Branch(ref branch)) - | (Status::At, &OwnedNode::NibbledBranch(_, ref branch)) if branch.has_value() => { + (Status::At, &OwnedNode::NibbledBranch(_, ref branch)) + | (Status::At, &OwnedNode::Branch(ref branch)) if branch.has_value() => { let value = branch.get_value().expect("already checked `has_value`"); return Some(Ok((self.key().right().1.into(), DBValue::from_slice(value)))); }, @@ -630,12 +644,28 @@ mod tests { let t = RefTrieDB::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); - assert_eq!(iter.next().unwrap().unwrap(), (hex!("0103000000000000000464").to_vec(), DBValue::from_slice(&hex!("fffffffffe")[..]))); + assert_eq!( + iter.next().unwrap().unwrap(), + ( + hex!("0103000000000000000464").to_vec(), + DBValue::from_slice(&hex!("fffffffffe")[..]), + ) + ); iter.seek(&hex!("00")[..]).unwrap(); - assert_eq!(pairs, iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()); + assert_eq!( + pairs, + iter.map(|x| x.unwrap()) + .map(|(k, v)| (k, v[..].to_vec())) + .collect::>() + ); let mut iter = t.iter().unwrap(); iter.seek(&hex!("0103000000000000000465")[..]).unwrap(); - assert_eq!(&pairs[1..], &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..]); + assert_eq!( + &pairs[1..], + &iter.map(|x| x.unwrap()) + .map(|(k, v)| (k, v[..].to_vec())) + .collect::>()[..] + ); } #[test] @@ -677,7 +707,12 @@ mod tests { #[test] fn iterator() { - let d = vec![DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B")]; + 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::, DBValue>::default(); let mut root = Default::default(); @@ -689,7 +724,15 @@ mod tests { } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); + assert_eq!( + d.iter() + .map(|i| i.clone().into_vec()) + .collect::>(), + t.iter() + .unwrap() + .map(|x| x.unwrap().0) + .collect::>() + ); assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } @@ -722,7 +765,12 @@ mod tests { #[test] fn iterator_seek() { - let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; + 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::, DBValue>::default(); let mut root = Default::default(); @@ -795,7 +843,12 @@ mod tests { #[test] fn debug_output_supports_pretty_print() { - let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; + 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::, DBValue>::default(); let mut root = Default::default(); @@ -881,4 +934,4 @@ mod tests { let query_result = lookup.look_up(NibbleSlice::new(b"A")); assert_eq!(query_result.unwrap().unwrap(), true); } -} +} \ No newline at end of file diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 966496c0..683a53cc 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -63,8 +63,8 @@ impl From for NodeHandle { } fn empty_children() -> Vec>> { - let mut res = Vec::with_capacity(N::NIBBLE_LEN); - (0..N::NIBBLE_LEN).for_each(|_|res.push(None)); + let mut res = Vec::with_capacity(N::NIBBLE_LENGTH); + (0..N::NIBBLE_LENGTH).for_each(|_|res.push(None)); res } @@ -94,7 +94,8 @@ enum Node { impl Node where - O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy + O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy { // load an inline node into memory or get the hash to do the lookup later. fn inline_or_hash( @@ -124,11 +125,11 @@ where where N: NibbleOps, C: NodeCodec, H: Hasher, { - let dec_children = | + let decode_children = | encoded_children: IterChildSliceIndex, storage: &'b mut NodeStorage, | { - let mut res = Vec::with_capacity(N::ChildSliceIndex::NIBBLE_LEN); + let mut res = Vec::with_capacity(N::ChildSliceIndex::NIBBLE_LENGTH); encoded_children.for_each(|o_data|{ let v = o_data.map(|data|Self::inline_or_hash::(data, db, storage)); res.push(v) @@ -145,11 +146,17 @@ where Self::inline_or_hash::(cb, db, storage)) }, EncodedNode::Branch(encoded_children, val) => { - let children = dec_children(encoded_children.0.iter(encoded_children.1), storage); + let children = decode_children( + encoded_children.0.iter(encoded_children.1), + storage, + ); Node::Branch(children, val.map(DBValue::from_slice)) }, EncodedNode::NibbledBranch(k, encoded_children, val) => { - let children = dec_children(encoded_children.0.iter(encoded_children.1), storage); + let children = decode_children( + encoded_children.0.iter(encoded_children.1), + storage, + ); Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) }, } @@ -534,7 +541,8 @@ where NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => self.cache(h, key.left())?, }; - let stored = self.storage.destroy(h); // cache then destroy for hash handle (handle being root in most case), direct access somehow? + // cache then destroy for hash handle (handle being root in most case) + let stored = self.storage.destroy(h); let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { trie.insert_inspector(stored, key, value, old_val).map(|a| a.into_action()) })?.expect("Insertion never deletes."); @@ -579,16 +587,19 @@ where let idx = partial.at(0) as usize; key.advance(1); if let Some(child) = children[idx].take() { - // original had something there. recurse down into it. + // Original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; children[idx] = Some(new_child.into()); if !changed { - // the new node we composed didn't change. that means our branch is untouched too. + // The new node we composed didn't change. + // It means our branch is untouched too. return Ok(InsertAction::Restore(Node::Branch(children, stored_value))); } } else { - // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.to_stored(), value))); + // Original had nothing there. compose a leaf. + let leaf = self.storage.alloc( + Stored::New(Node::Leaf(key.to_stored(), value)) + ); children[idx] = Some(leaf.into()); } @@ -604,7 +615,11 @@ where let cp = partial.common_prefix(&existing_key); if cp == existing_key.len() && cp == partial.len() { let unchanged = stored_value.as_ref() == Some(&value); - let branch = Node::NibbledBranch(existing_key.to_stored(), children, Some(value)); + let branch = Node::NibbledBranch( + existing_key.to_stored(), + children, + Some(value), + ); *old_val = stored_value; match unchanged { @@ -653,30 +668,37 @@ where } } else { - // append after cp == existing_key and partial > cp + // Append after cp == existing_key and partial > cp #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); let idx = partial.at(cp) as usize; key.advance(cp + 1); if let Some(child) = children[idx].take() { - // original had something there. recurse down into it. + // Original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; children[idx] = Some(new_child.into()); if !changed { - // the new node we composed didn't change. that means our branch is untouched too. - let n_branch = Node::NibbledBranch(existing_key.to_stored(), children, stored_value); + // The new node we composed didn't change. + // It means our branch is untouched too. + let n_branch = Node::NibbledBranch( + existing_key.to_stored(), + children, + stored_value, + ); return Ok(InsertAction::Restore(n_branch)); } } else { - // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.to_stored(), value))); + // Original had nothing there. compose a leaf. + let leaf = self.storage.alloc( + Stored::New(Node::Leaf(key.to_stored(), value)), + ); children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::NibbledBranch( existing_key.to_stored(), children, stored_value, - )) + )) } }, Node::Leaf(encoded, stored_value) => { @@ -699,7 +721,8 @@ where #[cfg(feature = "std")] trace!( target: "trie", - "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", + "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}):\ + TRANSMUTE,AUGMENT", existing_key.len(), partial.len(), ); @@ -711,7 +734,10 @@ where Node::Branch(children, Some(stored_value)) } else { let idx = existing_key.at(cp) as usize; - let new_leaf = Node::Leaf(existing_key.mid(cp + 1).to_stored(), stored_value); + let new_leaf = Node::Leaf( + existing_key.mid(cp + 1).to_stored(), + stored_value, + ); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); if L::USE_EXTENSION { @@ -721,8 +747,10 @@ where } }; - // always replace because whatever we get out here is not the branch we started with. - let branch_action = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); + // always replace because whatever we get out here + // is not the branch we started with. + let branch_action = self.insert_inspector(branch, key, value, old_val)? + .unwrap_node(); InsertAction::Replace(branch_action) } else if !L::USE_EXTENSION { #[cfg(feature = "std")] @@ -736,7 +764,8 @@ where Some(stored_value), ); // augment the new branch. - let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); + let branch = self.insert_inspector(branch, key, value, old_val)? + .unwrap_node(); InsertAction::Replace(branch) @@ -758,7 +787,13 @@ where } else { debug_assert!(L::USE_EXTENSION); #[cfg(feature = "std")] - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", + existing_key.len(), + partial.len(), + cp, + ); // partially-shared prefix for an extension. // start by making a leaf. @@ -767,7 +802,8 @@ where // augment it. this will result in the Leaf -> cp == 0 routine, // which creates a branch. key.advance(cp); - let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); + let augmented_low = self.insert_inspector(low, key, value, old_val)? + .unwrap_node(); // make an extension using it. this is a replacement. InsertAction::Replace(Node::Extension( existing_key.to_stored_range(cp), @@ -781,7 +817,13 @@ where let cp = partial.common_prefix(&existing_key); if cp == 0 { #[cfg(feature = "std")] - trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + trace!( + target: "trie", + "no-common-prefix, not-both-empty (exist={:?}; new={:?}):\ + TRANSMUTE,AUGMENT", + existing_key.len(), + partial.len(), + ); // partial isn't empty: make a branch here // extensions may not have empty partial keys. @@ -799,7 +841,12 @@ where }; // continue inserting. - let branch_action = self.insert_inspector(Node::Branch(children, None), key, value, old_val)?.unwrap_node(); + let branch_action = self.insert_inspector( + Node::Branch(children, None), + key, + value, + old_val, + )?.unwrap_node(); InsertAction::Replace(branch_action) } else if cp == existing_key.len() { #[cfg(feature = "std")] @@ -819,7 +866,14 @@ where } } else { #[cfg(feature = "std")] - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}):\ + AUGMENT-AT-END", + existing_key.len(), + partial.len(), + cp, + ); // partially-shared. let low = Node::Extension(existing_key.mid(cp).to_stored(), child_branch); @@ -839,7 +893,12 @@ where } /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle>, key: &mut NibbleFullKey, old_val: &mut Option) -> Result, TrieHash, CError> { + fn remove_at( + &mut self, + handle: NodeHandle>, + key: &mut NibbleFullKey, + old_val: &mut Option, + ) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { @@ -848,7 +907,11 @@ where } }; - let opt = self.inspect(stored, key, move |trie, node, key| trie.remove_inspector(node, key, old_val))?; + let opt = self.inspect( + stored, + key, + move |trie, node, key| trie.remove_inspector(node, key, old_val), + )?; Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } @@ -864,7 +927,8 @@ where Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), - (Node::NibbledBranch(n, c, None), true) => Action::Restore(Node::NibbledBranch(n, c, None)), + (Node::NibbledBranch(n, c, None), true) => + Action::Restore(Node::NibbledBranch(n, c, None)), (Node::Branch(children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. @@ -879,7 +943,11 @@ where let idx = partial.at(0) as usize; if let Some(child) = children[idx].take() { #[cfg(feature = "std")] - trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); + trace!( + target: "trie", + "removing value out of branch child, partial={:?}", + partial, + ); let prefix = key.clone(); key.advance(1); match self.remove_at(child, key, old_val)? { @@ -931,7 +999,11 @@ where if let Some(child) = children[idx].take() { #[cfg(feature = "std")] - trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); + trace!( + target: "trie", + "removing value out of branch child, partial={:?}", + partial, + ); let prefix = key.clone(); key.advance(cp + 1); match self.remove_at(child, key, old_val)? { @@ -949,8 +1021,14 @@ where // the child we took was deleted. // the node may need fixing. #[cfg(feature = "std")] - trace!(target: "trie", "branch child deleted, partial={:?}", partial); - Action::Replace(self.fix(Node::NibbledBranch(encoded, children, value), prefix)?) + trace!( + target: "trie", + "branch child deleted, partial={:?}", + partial, + ); + Action::Replace( + self.fix(Node::NibbledBranch(encoded, children, value),prefix)? + ) }, } } else { @@ -994,7 +1072,9 @@ where // if the child branch was unchanged, then the extension is too. // otherwise, this extension may need fixing. match changed { - true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix)?), + true => Action::Replace( + self.fix(Node::Extension(encoded, new_child), prefix)? + ), false => Action::Restore(Node::Extension(encoded, new_child)), } } @@ -1045,12 +1125,14 @@ where } match (used_index, value) { - (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), + (UsedIndex::None, None) => + panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. make an extension. let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); - let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + let child = children[a as usize].take() + .expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); self.fix(new_node, key) } @@ -1089,17 +1171,19 @@ where } match (used_index, value) { - (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), + (UsedIndex::None, None) => + panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. use child instead - let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + let child = children[a as usize].take() + .expect("used_index only set if occupied; qed"); let mut kc = key.clone(); kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); let (st, ost, op) = match kc.left() { (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), - (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { + (st, (i,v)) if i == L::N::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); - so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); + so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0,0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), @@ -1115,21 +1199,36 @@ where let child_node = match stored { Stored::New(node) => node, Stored::Cached(node, hash) => { - self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); + self.death_row.insert(( + hash, + (child_pref.0[..].into(), child_pref.1), + )); node }, }; match child_node { Node::Leaf(sub_partial, value) => { let mut enc_nibble = enc_nibble; - combine_key::(&mut enc_nibble, (L::N::NIBBLE_PER_BYTE - 1, &[a][..])); - combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + combine_key::( + &mut enc_nibble, + (L::N::NIBBLE_PER_BYTE - 1, &[a][..]), + ); + combine_key::( + &mut enc_nibble, + (sub_partial.0, &sub_partial.1[..]), + ); Ok(Node::Leaf(enc_nibble, value)) }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let mut enc_nibble = enc_nibble; - combine_key::(&mut enc_nibble, (L::N::NIBBLE_PER_BYTE - 1, &[a][..])); - combine_key::(&mut enc_nibble, (sub_partial.0, &sub_partial.1[..])); + combine_key::( + &mut enc_nibble, + (L::N::NIBBLE_PER_BYTE - 1, &[a][..]), + ); + combine_key::( + &mut enc_nibble, + (sub_partial.0, &sub_partial.1[..]), + ); Ok(Node::NibbledBranch(enc_nibble, ch_children, ch_value)) }, _ => unreachable!(), @@ -1150,16 +1249,17 @@ where } }, Node::Extension(partial, child) => { - // we could advance key if there was not the recursion case, there might be prefix from - // branch. + // We could advance key, but this code can also be called + // recursively, so there might be some prefix from branch. let a = partial.1[partial.1.len() - 1] & (255 >> 4); let mut kc = key.clone(); kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); let (st, ost, op) = match kc.left() { (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), - (st, (i,v)) if i == L::N::LAST_N_IX_U8 => { + (st, (i, v)) if i == L::N::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); - so.push(L::N::masked_left(L::N::LAST_N_IX_U8, v) | a); + // Complete last byte with `a`. + so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0,0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), @@ -1184,13 +1284,19 @@ where // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); + self.death_row.insert( + (hash, (child_pref.0[..].into(), child_pref.1)), + ); } // subpartial let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] - trace!(target: "trie", "fixing: extension combination. new_partial={:?}", partial); + trace!( + target: "trie", + "fixing: extension combination. new_partial={:?}", + partial, + ); self.fix(Node::Extension(partial, sub_child), key) } Node::Leaf(sub_partial, value) => { @@ -1203,7 +1309,11 @@ where let mut partial = partial; combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] - trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", partial); + trace!( + target: "trie", + "fixing: extension -> leaf. new_partial={:?}", + partial, + ); Ok(Node::Leaf(partial, value)) } child_node => { @@ -1246,12 +1356,14 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>(|child, o_slice, o_index| { - let mov = k.append_slice_nibble(o_slice, o_index); - let cr = self.commit_child(child, &mut k); - k.drop_lasts(mov); - cr - }); + let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>( + |child, o_slice, o_index| { + let mov = k.append_slice_nibble(o_slice, o_index); + let cr = self.commit_child(child, &mut k); + k.drop_lasts(mov); + cr + } + ); #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); @@ -1262,7 +1374,9 @@ where Stored::Cached(node, hash) => { // probably won't happen, but update the root and move on. *self.root = hash; - self.root_handle = NodeHandle::InMemory(self.storage.alloc(Stored::Cached(node, hash))); + self.root_handle = NodeHandle::InMemory( + self.storage.alloc(Stored::Cached(node, hash)), + ); } } } @@ -1284,7 +1398,11 @@ where Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { let encoded = { - let commit_child = |node_handle, o_slice: Option<&NibbleSlice>, o_index: Option| { + let commit_child = | + node_handle, + o_slice: Option<&NibbleSlice>, + o_index: Option + | { let mov = prefix.append_slice_nibble(o_slice, o_index); let cr = self.commit_child(node_handle, prefix); prefix.drop_lasts(mov); @@ -1297,7 +1415,8 @@ where self.hash_count +=1; ChildReference::Hash(hash) } else { - // it's a small value, so we cram it into a `TrieHash` and tag with length + // it's a small value, so we cram it into a `TrieHash` + // and tag with length let mut h = >::default(); let len = encoded.len(); h.as_mut()[..len].copy_from_slice(&encoded[..len]); @@ -1344,7 +1463,11 @@ where self.lookup(NibbleSlice::new(key), &self.root_handle) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, TrieHash, CError> { + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { if value.is_empty() { return self.remove(key) } let mut old_val = None; @@ -1582,7 +1705,10 @@ mod tests { let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); + assert_eq!( + *t.root(), + reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ]), + ); } #[test] @@ -1630,7 +1756,10 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); + assert_eq!( + *t.root(), + reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ]), + ); } #[test] @@ -1891,4 +2020,4 @@ mod tests { test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); } -} +} \ No newline at end of file From 19f92fb40d624565adeb12734622db102e970167 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 26 Jul 2019 17:39:18 +0200 Subject: [PATCH 107/120] add after comma space and after closure paramater space. --- hash-db/src/lib.rs | 4 +- memory-db/src/lib.rs | 8 +- test-support/reference-trie/src/lib.rs | 92 +++++++------- trie-db/src/iter_build.rs | 162 ++++++++++++------------- trie-db/src/lib.rs | 4 +- trie-db/src/nibble/mod.rs | 6 +- trie-db/src/nibble/nibbleslice.rs | 8 +- trie-db/src/nibble/nibblevec.rs | 42 +++---- trie-db/src/node.rs | 4 +- trie-db/src/node_codec.rs | 2 +- trie-db/src/triedb.rs | 16 +-- trie-db/src/triedbmut.rs | 44 +++---- 12 files changed, 196 insertions(+), 196 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 594969ad..332f74c2 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -35,7 +35,7 @@ impl MaybeDebug for T {} /// An empty prefix constant. Mainly use for comparison /// and for root nodes. -pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0,0)); +pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0, 0)); /// The prefix of a trie node, a prefix is the nibble path up to /// the node in the trie. @@ -183,4 +183,4 @@ impl<'a, H: Hasher, T> AsHashDB for &'a mut dyn HashDB { impl<'a, K, V> AsPlainDB for &'a mut dyn PlainDB { fn as_plain_db(&self) -> &dyn PlainDB { &**self } fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn PlainDB + 'b) { &mut **self } -} \ No newline at end of file +} diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 8099de8b..cfc02896 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -202,7 +202,7 @@ pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { key.clone() } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] /// Key function that only uses the hash pub struct HashKey(PhantomData); @@ -214,7 +214,7 @@ impl KeyFunction for HashKey { } } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] /// Key function that concatenates prefix and hash. pub struct PrefixedKey(PhantomData); @@ -226,7 +226,7 @@ impl KeyFunction for PrefixedKey { } } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] /// Key function that concatenates prefix and hash. /// This is doing useless computation and should only be /// used for legacy purpose. @@ -667,4 +667,4 @@ mod tests { let hashed_null_node = KeccakHasher::hash(&[0u8][..]); assert_eq!(db.insert(EMPTY_PREFIX, &[0u8][..]), hashed_null_node); } -} \ No newline at end of file +} diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 4c30c5d5..cadb6f83 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -100,7 +100,7 @@ impl BitMap for BitMap16 { fn decode(data: &[u8]) -> Result { u16::decode(&mut &data[..]) .ok_or(ReferenceError::BadFormat) - .map(|v|BitMap16(v)) + .map(|v| BitMap16(v)) } fn value_at(&self, i: usize) -> bool { @@ -766,7 +766,7 @@ impl< number_nibble: usize, child: ChildReference<::Out>, ) -> Vec { - let mut output = partial_from_iterator_to_key::( + let mut output = partial_from_iterator_to_key::( partial, number_nibble, EXTENSION_NODE_OFFSET, @@ -933,13 +933,13 @@ impl< maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { - partial_from_iterator_encode::( + partial_from_iterator_encode::( partial, number_nibble, NodeKindNoExt::BranchWithValue, ) } else { - partial_from_iterator_encode::( + partial_from_iterator_encode::( partial, number_nibble, NodeKindNoExt::BranchNoValue, @@ -947,7 +947,7 @@ impl< }; let bitmap_index = output.len(); let mut bitmap: BITMAP::Buffer = Default::default(); - (0..BITMAP::ENCODED_LEN).for_each(|_|output.push(0)); + (0..BITMAP::ENCODED_LEN).for_each(|_| output.push(0)); if let Some(value) = maybe_value { value.encode_to(&mut output); }; @@ -970,8 +970,8 @@ impl< } /// Compare trie builder and in memory trie. -pub fn compare_implementations + Eq> ( - data: Vec<(Vec,Vec)>, +pub fn compare_implementations + Eq> ( + data: Vec<(Vec, Vec)>, mut memdb: X, mut hashdb: X, ) { @@ -984,14 +984,14 @@ pub fn compare_implementations + Eq> ( let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } t.commit(); t.root().clone() }; if root_new != root { { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let db : &dyn hash_db::HashDB<_, _> = &hashdb; let t = RefTrieDB::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -999,7 +999,7 @@ pub fn compare_implementations + Eq> ( } } { - let db : &dyn hash_db::HashDB<_,_> = &memdb; + let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDB::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -1015,8 +1015,8 @@ pub fn compare_implementations + Eq> ( /// Compare trie builder and trie root implementations. pub fn compare_root( - data: Vec<(Vec,Vec)>, - mut memdb: impl hash_db::HashDB, + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, ) { let root_new = { let mut cb = TrieRoot::::default(); @@ -1027,7 +1027,7 @@ pub fn compare_root( let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } t.root().clone() }; @@ -1037,7 +1037,7 @@ pub fn compare_root( /// Compare trie builder and trie root unhashed implementations. pub fn compare_unhashed( - data: Vec<(Vec,Vec)>, + data: Vec<(Vec, Vec)>, ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); @@ -1052,7 +1052,7 @@ pub fn compare_unhashed( /// Compare trie builder and trie root unhashed implementations. /// This uses the variant without extension nodes. pub fn compare_unhashed_no_extension( - data: Vec<(Vec,Vec)>, + data: Vec<(Vec, Vec)>, ) { let root_new = { let mut cb = trie_db::TrieRootUnhashed::::default(); @@ -1065,7 +1065,7 @@ pub fn compare_unhashed_no_extension( } /// Trie builder root calculation utility. -pub fn calc_root( +pub fn calc_root( data: I, ) -> ::Out where @@ -1080,7 +1080,7 @@ pub fn calc_root( /// Trie builder root calculation utility. /// This uses the variant without extension nodes. -pub fn calc_root_no_extension( +pub fn calc_root_no_extension( data: I, ) -> ::Out where @@ -1094,7 +1094,7 @@ pub fn calc_root_no_extension( } /// Trie builder trie building utility. -pub fn calc_root_build( +pub fn calc_root_build( data: I, hashdb: &mut DB ) -> ::Out @@ -1102,7 +1102,7 @@ pub fn calc_root_build( I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); trie_visit::(data.into_iter(), &mut cb); @@ -1111,7 +1111,7 @@ pub fn calc_root_build( /// Trie builder trie building utility. /// This uses the variant without extension nodes. -pub fn calc_root_build_no_extension( +pub fn calc_root_build_no_extension( data: I, hashdb: &mut DB, ) -> ::Out @@ -1119,7 +1119,7 @@ pub fn calc_root_build_no_extension( I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: hash_db::HashDB { let mut cb = TrieBuilder::new(hashdb); trie_db::trie_visit::(data.into_iter(), &mut cb); @@ -1129,9 +1129,9 @@ pub fn calc_root_build_no_extension( /// Compare trie builder and in memory trie. /// This uses the variant without extension nodes. pub fn compare_implementations_no_extension( - data: Vec<(Vec,Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); @@ -1142,14 +1142,14 @@ pub fn compare_implementations_no_extension( let mut root = Default::default(); let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } t.root().clone() }; if root != root_new { { - let db : &dyn hash_db::HashDB<_,_> = &memdb; + let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -1157,7 +1157,7 @@ pub fn compare_implementations_no_extension( } } { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let db : &dyn hash_db::HashDB<_, _> = &hashdb; let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -1173,9 +1173,9 @@ pub fn compare_implementations_no_extension( /// This uses the variant without extension nodes. /// This uses a radix 4 trie. pub fn compare_implementations_no_extension_q( - data: Vec<(Vec,Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, ) { let root_new = { let mut cb = TrieBuilder::new(&mut hashdb); @@ -1186,19 +1186,19 @@ pub fn compare_implementations_no_extension_q( let mut root = Default::default(); let mut t = RefTrieDBMutNoExtQ::new(&mut memdb, &mut root); for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } t.root().clone() }; { - let db : &dyn hash_db::HashDB<_,_> = &memdb; + let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); println!("{:?}", t); } if root != root_new { { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let db : &dyn hash_db::HashDB<_, _> = &hashdb; let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); println!("it: {:?}", t); for a in t.iter().unwrap() { @@ -1207,7 +1207,7 @@ pub fn compare_implementations_no_extension_q( } { - let db : &dyn hash_db::HashDB<_,_> = &memdb; + let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); println!("fu: {:?}", t); for a in t.iter().unwrap() { @@ -1221,17 +1221,17 @@ pub fn compare_implementations_no_extension_q( /// `compare_implementations_no_extension` for unordered input. pub fn compare_implementations_no_extension_unordered( - data: Vec<(Vec,Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, ) { let mut b_map = std::collections::btree_map::BTreeMap::new(); let root = { let mut root = Default::default(); let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for i in 0..data.len() { - t.insert(&data[i].0[..],&data[i].1[..]).unwrap(); - b_map.insert(data[i].0.clone(),data[i].1.clone()); + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); + b_map.insert(data[i].0.clone(), data[i].1.clone()); } t.root().clone() }; @@ -1243,7 +1243,7 @@ pub fn compare_implementations_no_extension_unordered( if root != root_new { { - let db : &dyn hash_db::HashDB<_,_> = &memdb; + let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -1251,7 +1251,7 @@ pub fn compare_implementations_no_extension_unordered( } } { - let db : &dyn hash_db::HashDB<_,_> = &hashdb; + let db : &dyn hash_db::HashDB<_, _> = &hashdb; let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { @@ -1266,8 +1266,8 @@ pub fn compare_implementations_no_extension_unordered( /// Testing utility that uses some periodic removal over /// its input test data. pub fn compare_no_extension_insert_remove( - data: Vec<(bool, Vec,Vec)>, - mut memdb: impl hash_db::HashDB, + data: Vec<(bool, Vec, Vec)>, + mut memdb: impl hash_db::HashDB, ) { let mut data2 = std::collections::BTreeMap::new(); let mut root = Default::default(); @@ -1311,10 +1311,10 @@ fn too_big_nibble_length () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1]; let enc = as NodeCodec> - ::leaf_node(((0,0),&input), &[1]); + ::leaf_node(((0, 0), &input), &[1]); let dec = as NodeCodec> ::decode(&enc).unwrap(); - let o_sl = if let Node::Leaf(sl,_) = dec { + let o_sl = if let Node::Leaf(sl, _) = dec { Some(sl) } else { None }; assert!(o_sl.is_some()); @@ -1343,4 +1343,4 @@ fn size_encode_limit_values () { let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); assert_eq!(s_dec, Some(sizes[i])); } -} \ No newline at end of file +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 29ab65db..1baed4ad 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -57,7 +57,7 @@ impl CacheBuilder for Cache16 { type AN = [CacheNode; 16]; #[inline(always)] fn new_vec_slice_buffer() -> Self::AN { - exponential_out!(@3, [None,None]) + exponential_out!(@3, [None, None]) } } @@ -80,12 +80,12 @@ type ArrayNode = <::CB as CacheBuilder>>::AN; /// Note that it is not memory optimal (all depth are allocated even if some are empty due /// to node partial). /// Three field are used, a cache over the children, an optional associated value and the depth. -struct CacheAccum (Vec<(ArrayNode, Option, usize)>,PhantomData); +struct CacheAccum (Vec<(ArrayNode, Option, usize)>, PhantomData); /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; -impl CacheAccum +impl CacheAccum where T: TrieLayout, V: AsRef<[u8]>, @@ -160,11 +160,11 @@ where &mut self, cb_ext: &mut impl ProcessEncodedNode>, target_depth: usize, - (k2, v2): &(impl AsRef<[u8]>,impl AsRef<[u8]>), + (k2, v2): &(impl AsRef<[u8]>, impl AsRef<[u8]>), ) { let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],target_depth + 1); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], target_depth + 1); let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::::new_offset( &k2.as_ref()[..], @@ -211,7 +211,7 @@ where }; if !is_root { // put hash in parent - let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..],llix); + let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..], llix); self.set_node(llix, nibble as usize, Some(h)); } } @@ -263,8 +263,8 @@ where debug_assert!(self.0[last].2 == branch_d); // enc branch let v = self.0[last].1.take(); - let nkeyix = nkey.unwrap_or((0,0)); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..],nkeyix.0); + let nkeyix = nkey.unwrap_or((0, 0)); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let encoded = T::C::branch_node_nibbled( pr.right_range_iter(nkeyix.1), nkeyix.1, @@ -292,7 +292,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) F: ProcessEncodedNode>, { let no_extension = !T::USE_EXTENSION; - let mut depth_queue = CacheAccum::::new(); + let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); if let Some(mut prev_val) = iter_input.next() { @@ -326,7 +326,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) if single { // one single element corner case let (k2, v2) = prev_val; - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..],last_depth); + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], last_depth); let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::::new_offset( &k2.as_ref()[..], @@ -361,7 +361,7 @@ pub trait ProcessEncodedNode { pub struct TrieBuilder<'a, H, HO, V, DB> { db: &'a mut DB, pub root: Option, - _ph: PhantomData<(H,V)>, + _ph: PhantomData<(H, V)>, } impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { @@ -370,7 +370,7 @@ impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { } } -impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> +impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { fn process( &mut self, @@ -520,19 +520,19 @@ mod test { #[test] fn trie_one_node () { compare_implementations(vec![ - (vec![1u8,2u8,3u8,4u8],vec![7u8]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8]), ]); } #[test] fn root_extension_one () { compare_implementations(vec![ - (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), ]); } - fn test_iter(data: Vec<(Vec,Vec)>) { + fn test_iter(data: Vec<(Vec, Vec)>) { use reference_trie::{RefTrieDBMut, TrieMut, RefTrieDB, Trie}; let mut db = MemoryDB::, DBValue>::default(); @@ -547,18 +547,18 @@ mod test { } let t = RefTrieDB::new(&db, &root).unwrap(); for (i, kv) in t.iter().unwrap().enumerate() { - let (k,v) = kv.unwrap(); + let (k, v) = kv.unwrap(); let key: &[u8]= &data[i].0; let val: &[u8] = &data[i].1; - assert_eq!(k,key); - assert_eq!(v,val); + assert_eq!(k, key); + assert_eq!(v, val); } for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); } } - fn test_iter_no_extension(data: Vec<(Vec,Vec)>) { + fn test_iter_no_extension(data: Vec<(Vec, Vec)>) { use reference_trie::{RefTrieDBMutNoExt, TrieMut, RefTrieDBNoExt, Trie}; let mut db = MemoryDB::, DBValue>::default(); @@ -573,18 +573,18 @@ mod test { } let t = RefTrieDBNoExt::new(&db, &root).unwrap(); for (i, kv) in t.iter().unwrap().enumerate() { - let (k,v) = kv.unwrap(); + let (k, v) = kv.unwrap(); let key: &[u8]= &data[i].0; let val: &[u8] = &data[i].1; - assert_eq!(k,key); - assert_eq!(v,val); + assert_eq!(k, key); + assert_eq!(v, val); } for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); } } - fn compare_implementations(data: Vec<(Vec,Vec)>) { + fn compare_implementations(data: Vec<(Vec, Vec)>) { test_iter(data.clone()); test_iter_no_extension(data.clone()); compare_implementations_h(data.clone()); @@ -594,83 +594,83 @@ mod test { compare_implementations_no_extension_q(data.clone()); } - fn compare_implementations_prefixed(data: Vec<(Vec,Vec)>) { + fn compare_implementations_prefixed(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations(data, memdb, hashdb); } - fn compare_implementations_h(data: Vec<(Vec,Vec)>) { + fn compare_implementations_h(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations(data, memdb, hashdb); } - fn compare_implementations_no_extension(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension(data, memdb, hashdb); } - fn compare_implementations_no_extension_q(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_q(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension_q(data, memdb, hashdb); } - fn compare_implementations_no_extension_prefixed(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_prefixed(data: Vec<(Vec, Vec)>) { // let memdb = MemoryDB::<_, HashKey<_>, _>::default(); // let hashdb = MemoryDB::, DBValue>::default(); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension(data, memdb, hashdb); } - fn compare_implementations_no_extension_unordered(data: Vec<(Vec,Vec)>) { + fn compare_implementations_no_extension_unordered(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension_unordered(data, memdb, hashdb); } - fn compare_no_extension_insert_remove(data: Vec<(bool, Vec,Vec)>) { + fn compare_no_extension_insert_remove(data: Vec<(bool, Vec, Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); reference_trie::compare_no_extension_insert_remove(data, memdb); } - fn compare_root(data: Vec<(Vec,Vec)>) { + fn compare_root(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); reference_trie::compare_root(data, memdb); } - fn compare_unhashed(data: Vec<(Vec,Vec)>) { + fn compare_unhashed(data: Vec<(Vec, Vec)>) { reference_trie::compare_unhashed(data); } - fn compare_unhashed_no_extension(data: Vec<(Vec,Vec)>) { + fn compare_unhashed_no_extension(data: Vec<(Vec, Vec)>) { reference_trie::compare_unhashed_no_extension(data); } #[test] fn trie_middle_node1 () { compare_implementations(vec![ - (vec![1u8,2u8],vec![8u8;32]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + (vec![1u8, 2u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), ]); } #[test] fn trie_middle_node2 () { compare_implementations(vec![ - (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;32]), - (vec![1u8,2u8],vec![8u8;32]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), - (vec![1u8,2u8,3u8,5u8],vec![7u8;32]), - (vec![1u8,2u8,3u8,5u8,3u8],vec![7u8;32]), + (vec![0u8, 2u8, 3u8, 5u8, 3u8], vec![1u8;32]), + (vec![1u8, 2u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 5u8], vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 5u8, 3u8], vec![7u8;32]), ]); } #[test] fn root_extension_bis () { compare_root(vec![ - (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), ]); } #[test] fn root_extension_tierce () { let d = vec![ - (vec![1u8,2u8,3u8,3u8],vec![8u8;2]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;2]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;2]), ]; compare_unhashed(d.clone()); compare_unhashed_no_extension(d); @@ -679,63 +679,63 @@ mod test { fn root_extension_tierce_big () { // on more content unhashed would hash compare_unhashed(vec![ - (vec![1u8,2u8,3u8,3u8],vec![8u8;32]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;32]), - (vec![1u8,6u8,3u8,3u8],vec![8u8;32]), - (vec![6u8,2u8,3u8,3u8],vec![8u8;32]), - (vec![6u8,2u8,3u8,13u8],vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + (vec![1u8, 6u8, 3u8, 3u8], vec![8u8;32]), + (vec![6u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![6u8, 2u8, 3u8, 13u8], vec![8u8;32]), ]); } #[test] fn trie_middle_node2x () { compare_implementations(vec![ - (vec![0u8,2u8,3u8,5u8,3u8],vec![1u8;2]), - (vec![1u8,2u8],vec![8u8;2]), - (vec![1u8,2u8,3u8,4u8],vec![7u8;2]), - (vec![1u8,2u8,3u8,5u8],vec![7u8;2]), - (vec![1u8,2u8,3u8,5u8,3u8],vec![7u8;2]), + (vec![0u8, 2u8, 3u8, 5u8, 3u8], vec![1u8;2]), + (vec![1u8, 2u8], vec![8u8;2]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;2]), + (vec![1u8, 2u8, 3u8, 5u8], vec![7u8;2]), + (vec![1u8, 2u8, 3u8, 5u8, 3u8], vec![7u8;2]), ]); } #[test] fn fuzz1 () { compare_implementations(vec![ - (vec![01u8],vec![42u8,9]), - (vec![01u8,0u8],vec![0u8,0]), - (vec![255u8,2u8],vec![1u8,0]), + (vec![01u8], vec![42u8, 9]), + (vec![01u8, 0u8], vec![0u8, 0]), + (vec![255u8, 2u8], vec![1u8, 0]), ]); } #[test] fn fuzz2 () { compare_implementations(vec![ - (vec![0,01u8],vec![42u8,9]), - (vec![0,01u8,0u8],vec![0u8,0]), - (vec![0,255u8,2u8],vec![1u8,0]), + (vec![0, 01u8], vec![42u8, 9]), + (vec![0, 01u8, 0u8], vec![0u8, 0]), + (vec![0, 255u8, 2u8], vec![1u8, 0]), ]); } #[test] fn fuzz3 () { compare_implementations(vec![ - (vec![0],vec![196, 255]), - (vec![48],vec![138, 255]), - (vec![67],vec![0, 0]), - (vec![128],vec![255, 0]), - (vec![247],vec![0, 196]), - (vec![255],vec![0, 0]), + (vec![0], vec![196, 255]), + (vec![48], vec![138, 255]), + (vec![67], vec![0, 0]), + (vec![128], vec![255, 0]), + (vec![247], vec![0, 196]), + (vec![255], vec![0, 0]), ]); } #[test] fn fuzz_no_extension1 () { compare_implementations(vec![ - (vec![0],vec![128, 0]), - (vec![128],vec![0, 0]), + (vec![0], vec![128, 0]), + (vec![128], vec![0, 0]), ]); } #[test] fn fuzz_no_extension2 () { compare_implementations(vec![ - (vec![0],vec![6, 255]), - (vec![6],vec![255, 186]), - (vec![255],vec![186, 255]), + (vec![0], vec![6, 255]), + (vec![6], vec![255, 186]), + (vec![255], vec![186, 255]), ]); } #[test] @@ -752,15 +752,15 @@ mod test { #[test] fn fuzz_no_extension3 () { compare_implementations(vec![ - (vec![0],vec![0, 0]), - (vec![11,0],vec![0, 0]), - (vec![11,252],vec![11, 0]), + (vec![0], vec![0, 0]), + (vec![11, 0], vec![0, 0]), + (vec![11, 252], vec![11, 0]), ]); compare_implementations_no_extension_unordered(vec![ - (vec![11,252],vec![11, 0]), - (vec![11,0],vec![0, 0]), - (vec![0],vec![0, 0]), + (vec![11, 252], vec![11, 0]), + (vec![11, 0], vec![0, 0]), + (vec![0], vec![0, 0]), ]); } #[test] @@ -775,9 +775,9 @@ mod test { fn fuzz_no_extension_insert_remove_1 () { let data = vec![ (false, vec![0], vec![251, 255]), - (false, vec![0,1], vec![251, 255]), - (false, vec![0,1,2], vec![255; 32]), - (true, vec![0,1], vec![0, 251]), + (false, vec![0, 1], vec![251, 255]), + (false, vec![0, 1, 2], vec![255; 32]), + (true, vec![0, 1], vec![0, 251]), ]; compare_no_extension_insert_remove(data); } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index a4b22560..a83a0d61 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -201,7 +201,7 @@ pub trait Trie { /// Does the trie contain a given key? fn contains(&self, key: &[u8]) -> Result, CError> { - self.get(key).map(|x|x.is_some() ) + self.get(key).map(|x| x.is_some() ) } /// What is the value of the given key in this trie? @@ -477,4 +477,4 @@ pub type TrieHash = <::H as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = < ::C as NodeCodec<::H, ::N> ->::Error; \ No newline at end of file +>::Error; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index cfe26276..e1a6a8dd 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -166,7 +166,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy let shift = old_offset - ofset; let (s1, s2) = Self::split_shifts(shift); let kl = key.1.len(); - (0..kl - 1).for_each(|i|key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1); + (0..kl - 1).for_each(|i| key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1); key.1[kl - 1] = key.1[kl - 1] << s2; true } else if old_offset < ofset { @@ -174,7 +174,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy let shift = ofset - old_offset; let (s1, s2) = Self::split_shifts(shift); key.1.push(0); - (1..key.1.len()).rev().for_each(|i|key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2); + (1..key.1.len()).rev().for_each(|i| key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2); key.1[0] = key.1[0] >> s2; true } else { @@ -345,4 +345,4 @@ macro_rules! child_slice_index { } } child_slice_index!(ChildSliceIndex16, 16, 1); -child_slice_index!(ChildSliceIndex4, 4, 1); \ No newline at end of file +child_slice_index!(ChildSliceIndex4, 4, 1); diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 915673a2..90040810 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -155,7 +155,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { if nb > 0 { ((nb, N::masked_right(nb, self.data[split])), &self.data[split + 1 ..]) } else { - ((0,0), &self.data[split..]) + ((0, 0), &self.data[split..]) } } @@ -226,14 +226,14 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let split = self.offset / N::NIBBLE_PER_BYTE; let ix = (self.offset % N::NIBBLE_PER_BYTE) as u8; if ix == 0 { - (&self.data[..split], (0,0)) + (&self.data[..split], (0, 0)) } else { (&self.data[..split], (ix, N::masked_left(ix, self.data[split]))) } } /// Owned version of a `Prefix` from a `left` method call. - pub fn left_owned(&'a self) -> (ElasticArray36, (u8,u8)) { + pub fn left_owned(&'a self) -> (ElasticArray36, (u8, u8)) { let (a, b) = self.left(); (a.into(), b) } @@ -449,4 +449,4 @@ mod tests { assert!(n >= m.mid(4)); assert!(n <= m.mid(4)); } -} \ No newline at end of file +} diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 893caed3..80835e7b 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -90,7 +90,7 @@ impl NibbleVec { let end = self.len - n; let end_index = end / N::NIBBLE_PER_BYTE + if end % N::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; - (end_index..self.inner.len()).for_each(|_|{ self.inner.pop(); }); + (end_index..self.inner.len()).for_each(|_| { self.inner.pop(); }); self.len = end; let pos = self.len % N::NIBBLE_PER_BYTE; if pos != 0 { @@ -104,7 +104,7 @@ impl NibbleVec { let split = self.len / N::NIBBLE_PER_BYTE; let pos = (self.len % N::NIBBLE_PER_BYTE) as u8; if pos == 0 { - (&self.inner[..split], (0,0)) + (&self.inner[..split], (0, 0)) } else { (&self.inner[..split], (pos, N::masked_left(pos, self.inner[split]))) } @@ -251,14 +251,14 @@ mod tests { } #[test] fn append_partial() { - append_partial_inner::(&[1,2,3], &[], ((1, 1), &[0x23])); - append_partial_inner::(&[1,2,3], &[1], ((0, 0), &[0x23])); - append_partial_inner::(&[0,1,2,3], &[0], ((1, 1), &[0x23])); + append_partial_inner::(&[1, 2, 3], &[], ((1, 1), &[0x23])); + append_partial_inner::(&[1, 2, 3], &[1], ((0, 0), &[0x23])); + append_partial_inner::(&[0, 1, 2, 3], &[0], ((1, 1), &[0x23])); append_partial_inner::(&[1, 0, 2, 0, 3], &[], ((1, 1), &[0x23])); append_partial_inner::( &[1, 0, 2, 0, 3, 0, 1, 0, 2], &[], - ((1,1), &[0x23, 0x12]), + ((1, 1), &[0x23, 0x12]), ); append_partial_inner::( &[2, 1, 0, 2, 0, 3, 0, 1, 0, 2], @@ -296,11 +296,11 @@ mod tests { ); } - fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8,u8), &[u8])) { + fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8, u8), &[u8])) { let mut resv = NibbleVec::::new(); - res.iter().for_each(|r|resv.push(*r)); + res.iter().for_each(|r| resv.push(*r)); let mut initv = NibbleVec::::new(); - init.iter().for_each(|r|initv.push(*r)); + init.iter().for_each(|r| initv.push(*r)); initv.append_partial(partial); assert_eq!(resv, initv); } @@ -315,17 +315,17 @@ mod tests { k.drop_lasts(b); assert_eq!((&k.inner[..], k.len), c); }; - test_trun(&[1,2,3,4], 0, (&[0x12, 0x34], 4)); - test_trun(&[1,2,3,4], 1, (&[0x12, 0x30], 3)); - test_trun(&[1,2,3,4], 2, (&[0x12], 2)); - test_trun(&[1,2,3,4], 3, (&[0x10], 1)); - test_trun(&[1,2,3,4], 4, (&[], 0)); - test_trun(&[1,2,3,4], 5, (&[], 0)); - test_trun(&[1,2,3], 0, (&[0x12, 0x30], 3)); - test_trun(&[1,2,3], 1, (&[0x12], 2)); - test_trun(&[1,2,3], 2, (&[0x10], 1)); - test_trun(&[1,2,3], 3, (&[], 0)); - test_trun(&[1,2,3], 4, (&[], 0)); + test_trun(&[1, 2, 3, 4], 0, (&[0x12, 0x34], 4)); + test_trun(&[1, 2, 3, 4], 1, (&[0x12, 0x30], 3)); + test_trun(&[1, 2, 3, 4], 2, (&[0x12], 2)); + test_trun(&[1, 2, 3, 4], 3, (&[0x10], 1)); + test_trun(&[1, 2, 3, 4], 4, (&[], 0)); + test_trun(&[1, 2, 3, 4], 5, (&[], 0)); + test_trun(&[1, 2, 3], 0, (&[0x12, 0x30], 3)); + test_trun(&[1, 2, 3], 1, (&[0x12], 2)); + test_trun(&[1, 2, 3], 2, (&[0x10], 1)); + test_trun(&[1, 2, 3], 3, (&[], 0)); + test_trun(&[1, 2, 3], 4, (&[], 0)); } -} \ No newline at end of file +} diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index f70c41ec..187206c4 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -62,7 +62,7 @@ impl Branch { ) -> Self { let mut data: Vec = children_slice.1.into(); let data_index = data.len(); - let ubounds_index = data_index + maybe_value.map(|v|{ + let ubounds_index = data_index + maybe_value.map(|v| { data.extend_from_slice(v); v.len() }).unwrap_or(0); @@ -149,4 +149,4 @@ impl<'a, N: NibbleOps> From> for OwnedNode { OwnedNode::NibbledBranch(k.into(), Branch::new::(&c, val)), } } -} \ No newline at end of file +} diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index e9519760..657b6aea 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -42,7 +42,7 @@ impl Error for T {} /// It contains a right aligned padded first byte (first pair element is the number of nibbles /// (0 to max nb nibble - 1), second pair element is the padded nibble), and a slice over /// the remaining bytes. -pub type Partial<'a> = ((u8,u8), &'a[u8]); +pub type Partial<'a> = ((u8, u8), &'a[u8]); /// Trait for trie node encoding/decoding /// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index f3f4f250..d96681cf 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -333,7 +333,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { loop { let data = { let node = L::C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; + .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; match node { Node::Leaf(slice, _) => { if slice >= partial { @@ -440,7 +440,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { let node_data = &self.db.get_raw_or_lookup(d, self.key_nibbles.as_prefix())?; let node = L::C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(>::default(), e)))?; + .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) } @@ -497,7 +497,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { self.key_nibbles.drop_lasts(n.len()); }, OwnedNode::Branch(_) => { self.key_nibbles.pop(); }, - OwnedNode::NibbledBranch(ref n,_) => { + OwnedNode::NibbledBranch(ref n, _) => { self.key_nibbles.drop_lasts(n.len() + 1); }, OwnedNode::Empty => {}, @@ -518,7 +518,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { ) }, (Status::At, &OwnedNode::Branch(_)) - | (Status::At, &OwnedNode::NibbledBranch(_,_)) => IterStep::Continue, + | (Status::At, &OwnedNode::NibbledBranch(_, _)) => IterStep::Continue, (Status::AtChild(i), &OwnedNode::Branch(ref branch)) | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, ref branch)) if branch.index(i).is_some() => { @@ -534,7 +534,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { self.key_nibbles.as_prefix())) }, (Status::AtChild(i), &OwnedNode::Branch(_)) - | (Status::AtChild(i), &OwnedNode::NibbledBranch(_,_)) => { + | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, _)) => { if i == 0 { self.key_nibbles.push(0); } @@ -603,7 +603,7 @@ mod tests { (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -675,7 +675,7 @@ mod tests { (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::<_, PrefixedKey<_>,_>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -934,4 +934,4 @@ mod tests { let query_result = lookup.look_up(NibbleSlice::new(b"A")); assert_eq!(query_result.unwrap().unwrap(), true); } -} \ No newline at end of file +} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 683a53cc..91dfb419 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -62,9 +62,9 @@ impl From for NodeHandle { } } -fn empty_children() -> Vec>> { +fn empty_children() -> Vec>> { let mut res = Vec::with_capacity(N::NIBBLE_LENGTH); - (0..N::NIBBLE_LENGTH).for_each(|_|res.push(None)); + (0..N::NIBBLE_LENGTH).for_each(|_| res.push(None)); res } @@ -130,8 +130,8 @@ where storage: &'b mut NodeStorage, | { let mut res = Vec::with_capacity(N::ChildSliceIndex::NIBBLE_LENGTH); - encoded_children.for_each(|o_data|{ - let v = o_data.map(|data|Self::inline_or_hash::(data, db, storage)); + encoded_children.for_each(|o_data| { + let v = o_data.map(|data| Self::inline_or_hash::(data, db, storage)); res.push(v) }); res @@ -166,7 +166,7 @@ where fn into_encoded(self, mut child_cb: F) -> Vec where N: NibbleOps, - C: NodeCodec, + C: NodeCodec, F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { @@ -193,9 +193,9 @@ where .map(Option::take) .enumerate() .map(|(i, maybe_child)| { - maybe_child.map(|child|child_cb(child, None, Some(i as u8))) + maybe_child.map(|child| child_cb(child, None, Some(i as u8))) }), - value.as_ref().map(|v|&v[..]) + value.as_ref().map(|v| &v[..]) ) }, Node::NibbledBranch(partial, mut children, value) => { @@ -208,14 +208,14 @@ where children.iter_mut() .map(Option::take) .enumerate() - .map(|(i, maybe_child)|{ + .map(|(i, maybe_child)| { //let branch_index = [i as u8]; maybe_child.map(|child| { let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); child_cb(child, Some(&pr), Some(i as u8)) }) }), - value.as_ref().map(|v|&v[..]) + value.as_ref().map(|v| &v[..]) ) }, } @@ -357,7 +357,7 @@ where db: &'a mut dyn HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, - death_row: HashSet<(TrieHash, (ElasticArray36, (u8,u8)))>, + death_row: HashSet<(TrieHash, (ElasticArray36, (u8, u8)))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, @@ -1027,7 +1027,7 @@ where partial, ); Action::Replace( - self.fix(Node::NibbledBranch(encoded, children, value),prefix)? + self.fix(Node::NibbledBranch(encoded, children, value), prefix)? ) }, } @@ -1181,14 +1181,14 @@ where kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); let (st, ost, op) = match kc.left() { (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), - (st, (i,v)) if i == L::N::LAST_NIBBLE_INDEX => { + (st, (i, v)) if i == L::N::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); - (st, Some(so), (0,0)) + (st, Some(so), (0, 0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), }; - let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); + let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { @@ -1260,11 +1260,11 @@ where let mut so: ElasticArray36 = st.into(); // Complete last byte with `a`. so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); - (st, Some(so), (0,0)) + (st, Some(so), (0, 0)) }, (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), }; - let child_pref = (ost.as_ref().map(|st|&st[..]).unwrap_or(st), op); + let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), @@ -1538,7 +1538,7 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { } else { 0 }; - (st..end.1.len()).for_each(|i|start.1.push(end.1[i])); + (st..end.1.len()).for_each(|i| start.1.push(end.1[i])); } #[cfg(test)] @@ -1731,7 +1731,7 @@ mod tests { let big_value2 = b"00000000000000000000000000000002"; let big_value3 = b"00000000000000000000000000000004"; - let mut memdb = MemoryDB::<_,PrefixedKey<_>,_>::default(); + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); @@ -2009,10 +2009,10 @@ mod tests { fn combine_test() { let a: ElasticArray36 = [0x12, 0x34][..].into(); let b: &[u8] = [0x56, 0x78][..].into(); - let test_comb = |a: (_,&ElasticArray36<_>), b, c| { - let mut a = (a.0,a.1.clone()); + let test_comb = |a: (_, &ElasticArray36<_>), b, c| { + let mut a = (a.0, a.1.clone()); super::combine_key::(&mut a, b); - assert_eq!((a.0,&a.1[..]), c); + assert_eq!((a.0, &a.1[..]), c); }; test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); test_comb((1, &a), (0, &b), (1, &[0x12, 0x34, 0x56, 0x78][..])); @@ -2020,4 +2020,4 @@ mod tests { test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); } -} \ No newline at end of file +} From 4f3bb58b7949675745c9136202251207298fb3ba Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 26 Jul 2019 19:13:18 +0200 Subject: [PATCH 108/120] Renaming of variables and methods. --- test-support/reference-trie/src/lib.rs | 32 ++--- trie-db/benches/bench.rs | 178 +++++++++++++------------ trie-db/fuzz/src/lib.rs | 16 ++- trie-db/src/iter_build.rs | 89 ++++++------- trie-db/src/nibble/mod.rs | 5 +- trie-db/src/nibble/nibbleslice.rs | 4 + trie-db/src/nibble/nibblevec.rs | 6 +- trie-db/src/node.rs | 5 + trie-db/src/node_codec.rs | 9 +- trie-db/src/triedbmut.rs | 104 +++++++-------- 10 files changed, 238 insertions(+), 210 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index cadb6f83..51d170b8 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -107,15 +107,15 @@ impl BitMap for BitMap16 { self.0 & (1u16 << i) != 0 } - fn encode>(has_children: I , dest: &mut [u8]) { + fn encode>(has_children: I , output: &mut [u8]) { let mut bitmap: u16 = 0; let mut cursor: u16 = 1; for v in has_children { if v { bitmap |= cursor } cursor <<= 1; } - dest[0] = (bitmap % 256) as u8; - dest[1] = (bitmap / 256) as u8; + output[0] = (bitmap % 256) as u8; + output[1] = (bitmap / 256) as u8; } } @@ -139,14 +139,14 @@ impl BitMap for BitMap4 { self.0 & (1u8 << i) != 0 } - fn encode>(has_children: I , dest: &mut [u8]) { + fn encode>(has_children: I , output: &mut [u8]) { let mut bitmap: u8 = 0; let mut cursor: u8 = 1; for v in has_children { if v { bitmap |= cursor } cursor <<= 1; } - dest[0] = bitmap; + output[0] = bitmap; } } @@ -265,15 +265,15 @@ fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8 fn branch_node_buffered>( has_value: bool, has_children: I, - dest: &mut[u8], + output: &mut[u8], ) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE }; - dest[0] = first; - BITMAP::encode(has_children, &mut dest[1..]); + output[0] = first; + BITMAP::encode(has_children, &mut output[1..]); } fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { @@ -491,8 +491,7 @@ fn test_encoding_simple_trie() { BRANCH_WITHOUT_MASK_NO_EXT, BRANCH_WITH_MASK_NO_EXT, ].iter() { - for i in (0..1000) - .chain(NIBBLE_SIZE_BOUND_NO_EXT - 2..NIBBLE_SIZE_BOUND_NO_EXT + 2) { + for i in (0..1000).chain(NIBBLE_SIZE_BOUND_NO_EXT - 2..NIBBLE_SIZE_BOUND_NO_EXT + 2) { let mut output = Vec::new(); encode_size_and_prefix(i, *prefix, &mut output); let input = &mut &output[..]; @@ -782,7 +781,8 @@ impl< fn branch_node( children: impl Iterator::Out>>>>, - maybe_value: Option<&[u8]>) -> Vec { + maybe_value: Option<&[u8]>, + ) -> Vec { let mut output = vec![0; BITMAP::ENCODED_LEN + 1]; let mut prefix: BITMAP::Buffer = Default::default(); let have_value = if let Some(value) = maybe_value { @@ -820,7 +820,7 @@ impl< impl< H: Hasher, N: NibbleOps, - BITMAP: BitMap + BITMAP: BitMap, > NodeCodec for ReferenceNodeCodecNoExt { type Error = ReferenceError; @@ -1192,15 +1192,15 @@ pub fn compare_implementations_no_extension_q( }; { let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); - println!("{:?}", t); + let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); + println!("{:?}", t); } if root != root_new { { let db : &dyn hash_db::HashDB<_, _> = &hashdb; let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); - println!("it: {:?}", t); + println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); } @@ -1209,7 +1209,7 @@ pub fn compare_implementations_no_extension_q( { let db : &dyn hash_db::HashDB<_, _> = &memdb; let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); - println!("fu: {:?}", t); + println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 7c35f196..29b88996 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -55,7 +55,7 @@ fn nibble_common_prefix(b: &mut Criterion) { (NibbleSlice::::new(pair.0), NibbleSlice::::new(pair.1)) }).collect(); - b.iter(&mut ||{ + b.iter(&mut || { for (left, right) in mixed.iter() { let _ = black_box(left.common_prefix(&right)); } @@ -64,13 +64,13 @@ fn nibble_common_prefix(b: &mut Criterion) { } fn root_a_big_v(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input2(29, 204800 / 2, 512 * 2), ]; - c.bench_function_over_inputs("root_a_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_a_big_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() @@ -79,116 +79,122 @@ fn root_a_big_v(c: &mut Criterion) { reference_trie::calc_root(inputc); - }) - ,data); + }), + data, + ); } fn root_b_big_v(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input2(29, 204800, 512), ]; - c.bench_function_over_inputs("root_b_big_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_b_big_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::calc_root(inputc); - }) - ,data); + }), + data, + ); } fn root_a_small_v(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input2(29, 204800, 32), ]; - c.bench_function_over_inputs("root_a_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_a_small_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::calc_root(inputc); - }) - ,data); + }), + data, + ); } fn root_b_small_v(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input2(29, 204800 / 2, 32 * 2), ]; - c.bench_function_over_inputs("root_b_small_v",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_b_small_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::calc_root(inputc); - }) - ,data); + }), + data, + ); } fn root_old(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input(1, 5120), input(41, 10240), input(18, 102400), input(29, 204800), ]; - c.bench_function_over_inputs("root_old",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_old", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); let inputc = datac .iter() - .map(|v|(&v.0, &v.1)); + .map(|v| (&v.0, &v.1)); reference_trie::reference_trie_root(inputc); - }) - ,data); + }), + data, + ); } fn root_new(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input(1, 5120), input(41, 10240), input(18, 102400), input(29, 204800), ]; - c.bench_function_over_inputs("root_new",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("root_new", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::calc_root(inputc); - }) - ,data); + }), + data, + ); } -fn fuzz_to_data(input: Vec) -> Vec<(Vec,Vec)> { +fn fuzz_to_data(input: Vec) -> Vec<(Vec, Vec)> { let mut result = Vec::new(); // enc = (minkeylen, maxkeylen (min max up to 32), datas) // fix data len 2 bytes @@ -211,12 +217,12 @@ fn fuzz_to_data(input: Vec) -> Vec<(Vec,Vec)> { input[ix..ix + 2].to_vec() } else { break }; ix += 2; - result.push((key,val)); + result.push((key, val)); } result } -fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec,Vec)> { +fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec, Vec)> { let mut result = Vec::new(); let mut ix = 0; loop { @@ -226,21 +232,21 @@ fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec,Vec)> { } else { break }; ix += keylen; let val = vec![input[ix];vl]; - result.push((key,val)); + result.push((key, val)); } result } -fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { +fn data_sorted_unique(input: Vec<(Vec, Vec)>) -> Vec<(Vec, Vec)> { let mut m = std::collections::BTreeMap::new(); - for (k,v) in input.into_iter() { - let _ = m.insert(k,v); // latest value for uniqueness + for (k, v) in input.into_iter() { + let _ = m.insert(k, v); // latest value for uniqueness } m.into_iter().collect() } -fn input(seed: u64, len: usize) -> Vec<(Vec,Vec)> { +fn input(seed: u64, len: usize) -> Vec<(Vec, Vec)> { use rand::SeedableRng; use rand::RngCore; let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); @@ -250,17 +256,17 @@ fn input(seed: u64, len: usize) -> Vec<(Vec,Vec)> { data } -fn input2(seed: u64, len: usize, value_length: usize) -> Vec<(Vec,Vec)> { +fn input2(seed: u64, len: usize, value_length: usize) -> Vec<(Vec, Vec)> { use rand::SeedableRng; use rand::RngCore; let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); let mut data = vec![0u8; len]; rng.fill_bytes(&mut data[..]); - let data = data_sorted_unique(fuzz_to_data2(data,value_length)); + let data = data_sorted_unique(fuzz_to_data2(data, value_length)); data } -fn input_unsorted(seed: u64, len: usize, value_length: usize) -> Vec<(Vec,Vec)> { +fn input_unsorted(seed: u64, len: usize, value_length: usize) -> Vec<(Vec, Vec)> { use rand::SeedableRng; use rand::RngCore; let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); @@ -270,13 +276,13 @@ fn input_unsorted(seed: u64, len: usize, value_length: usize) -> Vec<(Vec,Ve } fn trie_mut_root_a(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input_unsorted(29, 204800 / 2, 512 * 2), ]; - c.bench_function_over_inputs("trie_mut_root_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + c.bench_function_over_inputs("trie_mut_root_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| b.iter(|| { - let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() @@ -290,17 +296,17 @@ fn trie_mut_root_a(c: &mut Criterion) { } fn trie_mut_root_b(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input_unsorted(29, 204800, 32), ]; - c.bench_function_over_inputs("trie_mut_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + c.bench_function_over_inputs("trie_mut_root_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::calc_root(inputc); @@ -309,18 +315,18 @@ fn trie_mut_root_b(c: &mut Criterion) { } fn trie_mut_ref_root_a(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input_unsorted(29, 204800 / 2, 512 * 2), ]; - c.bench_function_over_inputs("trie_mut_ref_root_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + c.bench_function_over_inputs("trie_mut_ref_root_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| b.iter(|| { - let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation + let datac:Vec<(Vec, Vec)> = data.clone(); // no need to sort for trie_root, see implementation // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::reference_trie_root(inputc); @@ -329,18 +335,18 @@ fn trie_mut_ref_root_a(c: &mut Criterion) { } fn trie_mut_ref_root_b(c: &mut Criterion) { - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ //input_unsorted(29, 204800, 512), input_unsorted(29, 204800, 32), ]; - c.bench_function_over_inputs("trie_mut_ref_root_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); // no need to sort for trie_root, see implementation + c.bench_function_over_inputs("trie_mut_ref_root_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // no need to sort for trie_root, see implementation // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); reference_trie::reference_trie_root(inputc); @@ -353,13 +359,13 @@ fn trie_mut_ref_root_b(c: &mut Criterion) { fn trie_mut_a(c: &mut Criterion) { use trie_db::TrieMut; use memory_db::HashKey; - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input_unsorted(29, 204800 / 2, 512 * 2), ]; - c.bench_function_over_inputs("trie_mut_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + c.bench_function_over_inputs("trie_mut_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| b.iter(|| { - let datac:Vec<(Vec,Vec)> = data.clone(); + let datac:Vec<(Vec, Vec)> = data.clone(); let mut root = Default::default(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); @@ -376,14 +382,14 @@ fn trie_mut_a(c: &mut Criterion) { fn trie_mut_b(c: &mut Criterion) { use trie_db::TrieMut; use memory_db::HashKey; - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ //input_unsorted(29, 204800, 512), input_unsorted(29, 204800, 32), ]; - c.bench_function_over_inputs("trie_mut_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data.clone(); + c.bench_function_over_inputs("trie_mut_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); let mut root = Default::default(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); @@ -399,17 +405,17 @@ fn trie_mut_b(c: &mut Criterion) { fn trie_mut_build_a(c: &mut Criterion) { use memory_db::HashKey; - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ input_unsorted(29, 204800 / 2, 512 * 2), ]; - c.bench_function_over_inputs("trie_mut_build_a",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| + c.bench_function_over_inputs("trie_mut_build_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| b.iter(|| { - let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); @@ -420,18 +426,18 @@ fn trie_mut_build_a(c: &mut Criterion) { fn trie_mut_build_b(c: &mut Criterion) { use memory_db::HashKey; - let data : Vec,Vec)>> = vec![ + let data : Vec, Vec)>> = vec![ //input_unsorted(29, 204800, 512), input_unsorted(29, 204800, 32), ]; - c.bench_function_over_inputs("trie_mut_build_b",|b: &mut Bencher, data: &Vec<(Vec,Vec)>| - b.iter(||{ - let datac:Vec<(Vec,Vec)> = data_sorted_unique(data.clone()); + c.bench_function_over_inputs("trie_mut_build_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); // this is in `reference_trie_root` added here to make things comparable let inputc = datac .iter() - .map(|v|(&v.0, &v.1)) + .map(|v| (&v.0, &v.1)) .collect::>(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index e6b4922b..4445970c 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2019 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 memory_db::{MemoryDB, HashKey, PrefixedKey}; @@ -163,4 +177,4 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); compare_no_extension_insert_remove(data, memdb); -} \ No newline at end of file +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 1baed4ad..9cd49d82 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -42,9 +42,9 @@ pub trait CacheBuilder { /// Size of cache. const SIZE: usize; /// The type of the cache. - type AN: AsRef<[CacheNode]> + AsMut<[CacheNode]>; + type Cache: AsRef<[CacheNode]> + AsMut<[CacheNode]>; /// Create a new cache. - fn new_vec_slice_buffer() -> Self::AN; + fn new_vec_slice_buffer() -> Self::Cache; } /// Cache builder for radix 16 trie. @@ -54,23 +54,23 @@ pub struct Cache4; impl CacheBuilder for Cache16 { const SIZE: usize = 16; - type AN = [CacheNode; 16]; + type Cache = [CacheNode; 16]; #[inline(always)] - fn new_vec_slice_buffer() -> Self::AN { + fn new_vec_slice_buffer() -> Self::Cache { exponential_out!(@3, [None, None]) } } impl CacheBuilder for Cache4 { const SIZE: usize = 4; - type AN = [CacheNode; 4]; + type Cache = [CacheNode; 4]; #[inline(always)] - fn new_vec_slice_buffer() -> Self::AN { + fn new_vec_slice_buffer() -> Self::Cache { exponential_out!(@2, [None]) } } -type ArrayNode = <::CB as CacheBuilder>>::AN; +type ArrayNode = <::CB as CacheBuilder>>::Cache; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index @@ -86,10 +86,10 @@ struct CacheAccum (Vec<(ArrayNode, Option, usize)>, Phan const INITIAL_DEPTH: usize = 10; impl CacheAccum -where - T: TrieLayout, - V: AsRef<[u8]>, - { + where + T: TrieLayout, + V: AsRef<[u8]>, +{ fn new() -> Self { let v = Vec::with_capacity(INITIAL_DEPTH); @@ -97,17 +97,17 @@ where } #[inline(always)] - fn set_elt(&mut self, depth:usize, sl: Option) { + fn set_cache_value(&mut self, depth:usize, value: Option) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; debug_assert!(self.0[last].2 <= depth); - self.0[last].1 = sl; + self.0[last].1 = value; } #[inline(always)] - fn set_node(&mut self, depth:usize, nibble_index:usize, node: CacheNode>) { + fn set_node(&mut self, depth: usize, nibble_index: usize, node: CacheNode>) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); } @@ -150,13 +150,12 @@ where } #[inline(always)] - fn reset_depth(&mut self, depth:usize) { - + fn reset_depth(&mut self, depth: usize) { debug_assert!(self.0[self.0.len() - 1].2 == depth); self.0.pop(); } - fn flush_val ( + fn flush_value ( &mut self, cb_ext: &mut impl ProcessEncodedNode>, target_depth: usize, @@ -204,10 +203,10 @@ where }; let h = if no_extension { - // enc branch - self.alt_no_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + // encode branch + self.no_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) } else { - self.standard_ext(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + self.standard_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) }; if !is_root { // put hash in parent @@ -218,7 +217,7 @@ where } #[inline(always)] - fn standard_ext( + fn standard_extension( &mut self, key_branch: &[u8], cb_ext: &mut impl ProcessEncodedNode>, @@ -229,7 +228,7 @@ where let last = self.0.len() - 1; assert_eq!(self.0[last].2, branch_d); - // enc branch + // encode branch let v = self.0[last].1.take(); let encoded = T::C::branch_node( self.0[last].0.as_ref().iter(), @@ -251,7 +250,7 @@ where } #[inline(always)] - fn alt_no_extension( + fn no_extension( &mut self, key_branch: &[u8], cb_ext: &mut impl ProcessEncodedNode>, @@ -261,7 +260,7 @@ where ) -> ChildReference> { let last = self.0.len() - 1; debug_assert!(self.0[last].2 == branch_d); - // enc branch + // encode branch let v = self.0[last].1.take(); let nkeyix = nkey.unwrap_or((0, 0)); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); @@ -295,37 +294,37 @@ pub fn trie_visit(input: I, cb_ext: &mut F) let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); - if let Some(mut prev_val) = iter_input.next() { + if let Some(mut previous_value) = iter_input.next() { // depth of last item let mut last_depth = 0; let mut single = true; for (k, v) in iter_input { single = false; - let common_depth = T::N::biggest_depth(&prev_val.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = T::N::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == prev_val.0.as_ref().len() * T::N::NIBBLE_PER_BYTE { + if common_depth == previous_value.0.as_ref().len() * T::N::NIBBLE_PER_BYTE { // the new key include the previous one : branch value case // just stored value at branch depth - depth_queue.set_elt(common_depth, Some(prev_val.1)); + depth_queue.set_cache_value(common_depth, Some(previous_value.1)); } else if depth_item >= last_depth { - // put prev with next (common branch prev val can be flush) - depth_queue.flush_val(cb_ext, depth_item, &prev_val); + // put previous with next (common branch previous value can be flush) + depth_queue.flush_value(cb_ext, depth_item, &previous_value); } else if depth_item < last_depth { // do not put with next, previous is last of a branch - depth_queue.flush_val(cb_ext, last_depth, &prev_val); - let ref_branches = prev_val.0; + depth_queue.flush_value(cb_ext, last_depth, &previous_value); + let ref_branches = previous_value.0; depth_queue.flush_branch(no_extension, cb_ext, ref_branches, depth_item, false); } - prev_val = (k, v); + previous_value = (k, v); last_depth = depth_item; } // last pendings if single { // one single element corner case - let (k2, v2) = prev_val; + let (k2, v2) = previous_value; let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], last_depth); let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::::new_offset( @@ -334,8 +333,8 @@ pub fn trie_visit(input: I, cb_ext: &mut F) ); cb_ext.process(pr.left(), encoded, true); } else { - depth_queue.flush_val(cb_ext, last_depth, &prev_val); - let ref_branches = prev_val.0; + depth_queue.flush_value(cb_ext, last_depth, &previous_value); + let ref_branches = previous_value.0; depth_queue.flush_branch(no_extension, cb_ext, ref_branches, 0, true); } } else { @@ -541,17 +540,17 @@ mod test { let mut t = RefTrieDBMut::new(&mut db, &mut root); for i in 0..data.len() { let key: &[u8]= &data[i].0; - let val: &[u8] = &data[i].1; - t.insert(key, val).unwrap(); + let value: &[u8] = &data[i].1; + t.insert(key, value).unwrap(); } } let t = RefTrieDB::new(&db, &root).unwrap(); for (i, kv) in t.iter().unwrap().enumerate() { let (k, v) = kv.unwrap(); let key: &[u8]= &data[i].0; - let val: &[u8] = &data[i].1; + let value: &[u8] = &data[i].1; assert_eq!(k, key); - assert_eq!(v, val); + assert_eq!(v, value); } for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); @@ -567,17 +566,17 @@ mod test { let mut t = RefTrieDBMutNoExt::new(&mut db, &mut root); for i in 0..data.len() { let key: &[u8]= &data[i].0; - let val: &[u8] = &data[i].1; - t.insert(key, val).unwrap(); + let value: &[u8] = &data[i].1; + t.insert(key, value).unwrap(); } } let t = RefTrieDBNoExt::new(&db, &root).unwrap(); for (i, kv) in t.iter().unwrap().enumerate() { let (k, v) = kv.unwrap(); let key: &[u8]= &data[i].0; - let val: &[u8] = &data[i].1; + let value: &[u8] = &data[i].1; assert_eq!(k, key); - assert_eq!(v, val); + assert_eq!(v, value); } for (k, v) in data.into_iter() { assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); @@ -615,8 +614,6 @@ mod test { reference_trie::compare_implementations_no_extension_q(data, memdb, hashdb); } fn compare_implementations_no_extension_prefixed(data: Vec<(Vec, Vec)>) { -// let memdb = MemoryDB::<_, HashKey<_>, _>::default(); -// let hashdb = MemoryDB::, DBValue>::default(); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension(data, memdb, hashdb); diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index e1a6a8dd..99332c26 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -34,12 +34,11 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// See [`ByteLayout`]. const REPR : ByteLayout; /// Single nibble length in bit. - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; // 2usize.pow(Self::REPR as u32); + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; /// Number of nibble per byte. const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; /// Number of child for a branch (trie radix). const NIBBLE_LENGTH : usize = TWO_EXP[Self::BIT_PER_NIBBLE]; - //2usize.pow(8 as u32 / Self::NIBBLE_PER_BYTE as u32); /// Padding bitmasks, internally use for working on padding byte. /// Length of this array is `Self::BIT_PER_NIBBLE`. /// The first element of each pair is a bit mask to apply, @@ -63,7 +62,6 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// trait type ChildSliceIndex: ChildSliceIndex; - /// Mask a byte from a `ix` > 0 (ix being content). /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. #[inline(always)] @@ -181,6 +179,7 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy false } } + } /// Radix 16 `NibbleOps` definition. diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 90040810..aa85778c 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -300,6 +300,7 @@ mod tests { fn basics() { basics_inner::(); } + fn basics_inner() { let n = NibbleSlice::::new(D); assert_eq!(n.len(), 6); @@ -319,6 +320,7 @@ mod tests { fn iterator() { iterator_inner::(); } + fn iterator_inner() { let n = NibbleSlice::::new(D); let mut nibbles: Vec = vec![]; @@ -330,6 +332,7 @@ mod tests { fn mid() { mid_inner::(); } + fn mid_inner() { let n = NibbleSlice::::new(D); let m = n.mid(2); @@ -358,6 +361,7 @@ mod tests { assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); } + #[test] fn range_iter() { let n = NibbleSlice::::new(D); diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 80835e7b..b8e951b6 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -59,9 +59,9 @@ impl NibbleVec { if i == 0 { self.inner.push(N::push_at_left(0, nibble, 0)); } else { - let dest = self.inner.last_mut() + let output = self.inner.last_mut() .expect("len != 0 since len % 2 != 0; inner has a last element; qed"); - *dest = N::push_at_left(i as u8, nibble, *dest); + *output = N::push_at_left(i as u8, nibble, *output); } self.len += 1; } @@ -232,6 +232,7 @@ mod tests { push_pop_inner::(); push_pop_inner::(); } + fn push_pop_inner() { let mut v = NibbleVec::::new(); @@ -249,6 +250,7 @@ mod tests { assert_eq!(v.len(), i); } } + #[test] fn append_partial() { append_partial_inner::(&[1, 2, 3], &[], ((1, 1), &[0x23])); diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 187206c4..5c5488f4 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -49,9 +49,14 @@ pub enum Node<'a, N: NibbleOps> { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq, Clone)] pub struct Branch { + /// All data in branch as a single byte buffer. + /// It contains children slice, then value, then data: Vec, + /// Start of data/value index. data_index: usize, + /// Start of ubounds, ubounds are the index of every children. ubounds_index: usize, + /// Size of a child header (a constant value). child_head: usize, } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 657b6aea..58e7285b 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -107,12 +107,13 @@ pub trait BitMap: Sized { /// Codec buffer to use. type Buffer: AsRef<[u8]> + AsMut<[u8]> + Default; - /// decode bitmap + /// Decode bitmap from its encoded full slice. fn decode(data: &[u8]) -> Result; - /// got values + /// Return wether the bitmap registered a value for a branch + /// child index. fn value_at(&self, i: usize) -> bool; - /// encode to dest of right len - fn encode>(has_children: I , dest: &mut [u8]); + /// Encode bitmap, output slice must be of right length. + fn encode>(has_children: I , output: &mut [u8]); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 91dfb419..528444a1 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -612,8 +612,8 @@ where trace!(target: "trie", "branch: ROUTE,AUGMENT"); let existing_key = NibbleSlice::from_stored(&encoded); - let cp = partial.common_prefix(&existing_key); - if cp == existing_key.len() && cp == partial.len() { + let common = partial.common_prefix(&existing_key); + if common == existing_key.len() && common == partial.len() { let unchanged = stored_value.as_ref() == Some(&value); let branch = Node::NibbledBranch( existing_key.to_stored(), @@ -626,40 +626,40 @@ where true => InsertAction::Restore(branch), false => InsertAction::Replace(branch), } - } else if cp < existing_key.len() { + } else if common < existing_key.len() { // insert a branch value in between #[cfg(feature = "std")] trace!( target: "trie", - "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), - cp, + common, ); - let nbranch_partial = existing_key.mid(cp + 1).to_stored(); + let nbranch_partial = existing_key.mid(common + 1).to_stored(); let low = Node::NibbledBranch(nbranch_partial, children, stored_value); - let ix = existing_key.at(cp); + let ix = existing_key.at(common); let mut children = empty_children::<_, L::N>(); let alloc_storage = self.storage.alloc(Stored::New(low)); children[ix as usize] = Some(alloc_storage.into()); - if partial.len() - cp == 0 { + if partial.len() - common == 0 { InsertAction::Replace(Node::NibbledBranch( - existing_key.to_stored_range(cp), + existing_key.to_stored_range(common), children, Some(value), ) ) } else { - let ix = partial.at(cp); - let stored_leaf = Node::Leaf(partial.mid(cp + 1).to_stored(), value); + let ix = partial.at(common); + let stored_leaf = Node::Leaf(partial.mid(common + 1).to_stored(), value); let leaf = self.storage.alloc(Stored::New(stored_leaf)); children[ix as usize] = Some(leaf.into()); InsertAction::Replace(Node::NibbledBranch( - existing_key.to_stored_range(cp), + existing_key.to_stored_range(common), children, None, ) @@ -668,11 +668,11 @@ where } } else { - // Append after cp == existing_key and partial > cp + // Append after common == existing_key and partial > common #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); - let idx = partial.at(cp) as usize; - key.advance(cp + 1); + let idx = partial.at(common) as usize; + key.advance(common + 1); if let Some(child) = children[idx].take() { // Original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; @@ -703,8 +703,8 @@ where }, Node::Leaf(encoded, stored_value) => { let existing_key = NibbleSlice::from_stored(&encoded); - let cp = partial.common_prefix(&existing_key); - if cp == existing_key.len() && cp == partial.len() { + let common = partial.common_prefix(&existing_key); + if common == existing_key.len() && common == partial.len() { #[cfg(feature = "std")] trace!(target: "trie", "equivalent-leaf: REPLACE"); // equivalent leaf. @@ -716,8 +716,8 @@ where true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), } - } else if (L::USE_EXTENSION && cp == 0) - || (!L::USE_EXTENSION && cp < existing_key.len()) { + } else if (L::USE_EXTENSION && common == 0) + || (!L::USE_EXTENSION && common < existing_key.len()) { #[cfg(feature = "std")] trace!( target: "trie", @@ -733,9 +733,9 @@ where // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) } else { - let idx = existing_key.at(cp) as usize; + let idx = existing_key.at(common) as usize; let new_leaf = Node::Leaf( - existing_key.mid(cp + 1).to_stored(), + existing_key.mid(common + 1).to_stored(), stored_value, ); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); @@ -743,7 +743,7 @@ where if L::USE_EXTENSION { Node::Branch(children, None) } else { - Node::NibbledBranch(partial.to_stored_range(cp), children, None) + Node::NibbledBranch(partial.to_stored_range(common), children, None) } }; @@ -754,7 +754,7 @@ where InsertAction::Replace(branch_action) } else if !L::USE_EXTENSION { #[cfg(feature = "std")] - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); // fully-shared prefix for an extension. // make a stub branch @@ -769,16 +769,16 @@ where InsertAction::Replace(branch) - } else if cp == existing_key.len() { + } else if common == existing_key.len() { debug_assert!(L::USE_EXTENSION); #[cfg(feature = "std")] - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); // fully-shared prefix for an extension. // make a stub branch and an extension. let branch = Node::Branch(empty_children::<_, L::N>(), Some(stored_value)); // augment the new branch. - key.advance(cp); + key.advance(common); let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); // always replace since we took a leaf and made an extension. @@ -789,24 +789,24 @@ where #[cfg(feature = "std")] trace!( target: "trie", - "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), - cp, + common, ); // partially-shared prefix for an extension. // start by making a leaf. - let low = Node::Leaf(existing_key.mid(cp).to_stored(), stored_value); + let low = Node::Leaf(existing_key.mid(common).to_stored(), stored_value); - // augment it. this will result in the Leaf -> cp == 0 routine, + // augment it. this will result in the Leaf -> common == 0 routine, // which creates a branch. - key.advance(cp); + key.advance(common); let augmented_low = self.insert_inspector(low, key, value, old_val)? .unwrap_node(); // make an extension using it. this is a replacement. InsertAction::Replace(Node::Extension( - existing_key.to_stored_range(cp), + existing_key.to_stored_range(common), self.storage.alloc(Stored::New(augmented_low)).into() )) } @@ -814,8 +814,8 @@ where Node::Extension(encoded, child_branch) => { debug_assert!(L::USE_EXTENSION); let existing_key = NibbleSlice::from_stored(&encoded); - let cp = partial.common_prefix(&existing_key); - if cp == 0 { + let common = partial.common_prefix(&existing_key); + if common == 0 { #[cfg(feature = "std")] trace!( target: "trie", @@ -848,14 +848,14 @@ where old_val, )?.unwrap_node(); InsertAction::Replace(branch_action) - } else if cp == existing_key.len() { + } else if common == existing_key.len() { #[cfg(feature = "std")] - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); // fully-shared prefix. // insert into the child node. - key.advance(cp); + key.advance(common); let (new_child, changed) = self.insert_at(child_branch, key, value, old_val)?; let new_ext = Node::Extension(existing_key.to_stored(), new_child.into()); @@ -868,23 +868,23 @@ where #[cfg(feature = "std")] trace!( target: "trie", - "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}):\ + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ AUGMENT-AT-END", existing_key.len(), partial.len(), - cp, + common, ); // partially-shared. - let low = Node::Extension(existing_key.mid(cp).to_stored(), child_branch); - // augment the extension. this will take the cp == 0 path, creating a branch. - key.advance(cp); + let low = Node::Extension(existing_key.mid(common).to_stored(), child_branch); + // augment the extension. this will take the common == 0 path, creating a branch. + key.advance(common); let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); // always replace, since this extension is not the one we started with. // this is known because the partial key is only the common prefix. InsertAction::Replace(Node::Extension( - existing_key.to_stored_range(cp), + existing_key.to_stored_range(common), self.storage.alloc(Stored::New(augmented_low)).into() )) } @@ -975,11 +975,11 @@ where } }, (Node::NibbledBranch(encoded, mut children, value), false) => { - let (cp, existing_length) = { + let (common, existing_length) = { let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; - if cp == existing_length && cp == partial.len() { + if common == existing_length && common == partial.len() { // replace val if let Some(val) = value { @@ -990,12 +990,12 @@ where } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) } - } else if cp < existing_length { + } else if common < existing_length { // partway through an extension -- nothing to do here. Action::Restore(Node::NibbledBranch(encoded, children, value)) } else { - // cp == existing_length && cp < partial.len() : check children - let idx = partial.at(cp) as usize; + // common == existing_length && common < partial.len() : check children + let idx = partial.at(common) as usize; if let Some(child) = children[idx].take() { #[cfg(feature = "std")] @@ -1005,7 +1005,7 @@ where partial, ); let prefix = key.clone(); - key.advance(cp + 1); + key.advance(common + 1); match self.remove_at(child, key, old_val)? { Some((new, changed)) => { children[idx] = Some(new.into()); @@ -1055,16 +1055,16 @@ where } }, (Node::Extension(encoded, child_branch), _) => { - let (cp, existing_length) = { + let (common, existing_length) = { let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; - if cp == existing_length { + if common == existing_length { // try to remove from the child branch. #[cfg(feature = "std")] trace!(target: "trie", "removing from extension child, partial={:?}", partial); let prefix = key.clone(); - key.advance(cp); + key.advance(common); match self.remove_at(child_branch, key, old_val)? { Some((new_child, changed)) => { let new_child = new_child.into(); From a65724dd466f53f985f0534d74da0ce786141f41 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 26 Jul 2019 19:16:34 +0200 Subject: [PATCH 109/120] Some line length. --- trie-db/src/triedbmut.rs | 12 ++++++++---- trie-root/src/lib.rs | 13 +++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 528444a1..79a2eb30 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -631,7 +631,8 @@ where #[cfg(feature = "std")] trace!( target: "trie", - "partially-shared-prefix (exist={:?}; new={:?}; common={:?}): AUGMENT-AT-END", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ + AUGMENT-AT-END", existing_key.len(), partial.len(), common, @@ -789,7 +790,8 @@ where #[cfg(feature = "std")] trace!( target: "trie", - "partially-shared-prefix (exist={:?}; new={:?}; common={:?}): AUGMENT-AT-END", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ + AUGMENT-AT-END", existing_key.len(), partial.len(), common, @@ -877,9 +879,11 @@ where // partially-shared. let low = Node::Extension(existing_key.mid(common).to_stored(), child_branch); - // augment the extension. this will take the common == 0 path, creating a branch. + // augment the extension. this will take the common == 0 path, + // creating a branch. key.advance(common); - let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); + let augmented_low = self.insert_inspector(low, key, value, old_val)? + .unwrap_node(); // always replace, since this extension is not the one we started with. // this is known because the partial key is only the common prefix. diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 34ee3e44..bffc85cc 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -280,7 +280,12 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex } } else if shared_nibble_count > cursor { stream.append_extension(&key[cursor..shared_nibble_count]); - build_trie_trampoline::(input, shared_nibble_count, stream, no_extension); + build_trie_trampoline::( + input, + shared_nibble_count, + stream, + no_extension, + ); return; } else { (cursor, None) }; @@ -310,8 +315,8 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex // Put out the node header: stream.begin_branch(o_branch_slice, value, shared_nibble_counts.iter().map(|&n| n > 0)); - // Fill in each slot in the branch node. We don't need to bother with empty slots since they - // were registered in the header. + // Fill in each slot in the branch node. We don't need to bother with empty slots + // since they were registered in the header. let mut begin = match value { None => 0, _ => 1 }; for &count in &shared_nibble_counts { if count > 0 { @@ -346,4 +351,4 @@ fn build_trie_trampoline( let mut substream = S::new(); build_trie::(input, cursor, &mut substream, no_extension); stream.append_substream::(substream); -} +} \ No newline at end of file From 502740b973fcc9d66b8fe769189609c67f1dd88d Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 29 Jul 2019 10:23:35 +0200 Subject: [PATCH 110/120] Rename subtrait of TrieLayout to full names. --- hash-db/src/lib.rs | 32 ++++---- memory-db/src/lib.rs | 75 +++++++++--------- test-support/reference-trie/src/lib.rs | 32 ++++---- test-support/trie-bench/src/lib.rs | 8 +- trie-db/fuzz/src/lib.rs | 2 - trie-db/src/fatdb.rs | 14 ++-- trie-db/src/fatdbmut.rs | 22 +++--- trie-db/src/iter_build.rs | 46 +++++------ trie-db/src/lib.rs | 36 ++++----- trie-db/src/lookup.rs | 12 +-- trie-db/src/sectriedb.rs | 10 +-- trie-db/src/sectriedbmut.rs | 16 ++-- trie-db/src/triedb.rs | 30 ++++---- trie-db/src/triedbmut.rs | 102 ++++++++++++------------- 14 files changed, 217 insertions(+), 220 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 332f74c2..06e82197 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -33,20 +33,22 @@ pub trait MaybeDebug {} impl MaybeDebug for T {} -/// An empty prefix constant. Mainly use for comparison -/// and for root nodes. -pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0, 0)); - -/// The prefix of a trie node, a prefix is the nibble path up to -/// the node in the trie. -/// For a value node, it is the node key with its node partial -/// bytes removed (the node key can be split into prefix and node partial). -/// As the leftmost portion of the node key, its internal representation -/// is a byte slice followed by a padded byte. -/// The padded byte is a pair of u8 containing the number of nibble first, -/// and the left aligned padded value second. +/// A trie node prefix, it is the nibble path from the trie root +/// to the trie node. +/// For a node containing no partial key value it is the full key. +/// For a value node or node containing a partial key, it is the full key minus its node partial +/// nibbles (the node key can be split into prefix and node partial). +/// Therefore it is always the leftmost portion of the node key, so its internal representation +/// is a non expanded byte slice followed by a last padded byte representation. +/// The padded byte is a pair of u8 containing the number of nibble, followed by +/// the left aligned padded value. pub type Prefix<'a> = (&'a[u8], (u8, u8)); +/// An empty prefix constant. +/// Can be use when the prefix is not use internally +/// or for root nodes. +pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0, 0)); + /// Trait describing an object that can hash a slice of bytes. Used to abstract /// other types over the hashing algorithm. Defines a single `hash` method and an /// `Out` associated type with the necessary bounds. @@ -54,12 +56,12 @@ pub trait Hasher: Sync + Send { /// The output type of the `Hasher` type Out: AsRef<[u8]> + AsMut<[u8]> + Default + MaybeDebug + PartialEq + Eq + hash::Hash + Send + Sync + Clone + Copy; - /// What to use to build `HashMap`s with this `Hasher` + /// What to use to build `HashMap`s with this `Hasher`. type StdHasher: Sync + Send + Default + hash::Hasher; - /// The length in bytes of the `Hasher` output + /// The length in bytes of the `Hasher` output. const LENGTH: usize; - /// Compute the hash of the provided slice of bytes returning the `Out` type of the `Hasher` + /// Compute the hash of the provided slice of bytes returning the `Out` type of the `Hasher`. fn hash(x: &[u8]) -> Self::Out; } diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index cfc02896..638950e5 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -163,45 +163,6 @@ pub trait KeyFunction { fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; } -/// Derive a database key from hash and prefix. -/// -/// This method does not preserve ordering of prefix. -/// -/// If ordering is needed, a length encoded prefix would be needed. -pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { - let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); - prefixed_key.extend_from_slice(prefix.0); - if (prefix.1).0 > 0 { - prefixed_key.push((prefix.1).1); - } - prefixed_key.extend_from_slice(key.as_ref()); - prefixed_key -} - -/// Legacy method for db using previous version of prefix encoding. -/// Only for trie radix 16. -pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { - let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); - if (prefix.1).0 == 0 { - prefixed_key.push(0); - prefixed_key.extend_from_slice(prefix.0); - } else { - let mut prev = 0x01u8; - for i in prefix.0.iter() { - prefixed_key.push((prev << 4) + (*i >> 4)); - prev = *i; - } - prefixed_key.push((prev << 4) + ((prefix.1).1 >> 4)); - } - prefixed_key.extend_from_slice(key.as_ref()); - prefixed_key -} - -/// Make database key from hash only. -pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { - key.clone() -} - #[derive(Clone, Debug)] /// Key function that only uses the hash pub struct HashKey(PhantomData); @@ -214,6 +175,11 @@ impl KeyFunction for HashKey { } } +/// Make database key from hash only. +pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { + key.clone() +} + #[derive(Clone, Debug)] /// Key function that concatenates prefix and hash. pub struct PrefixedKey(PhantomData); @@ -226,10 +192,22 @@ impl KeyFunction for PrefixedKey { } } +/// Derive a database key from hash value of the node (key) and the node prefix. +pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + prefixed_key.extend_from_slice(prefix.0); + if (prefix.1).0 > 0 { + prefixed_key.push((prefix.1).1); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + #[derive(Clone, Debug)] /// Key function that concatenates prefix and hash. /// This is doing useless computation and should only be /// used for legacy purpose. +/// It shall be remove in the future. pub struct LegacyPrefixedKey(PhantomData); impl KeyFunction for LegacyPrefixedKey { @@ -240,6 +218,25 @@ impl KeyFunction for LegacyPrefixedKey { } } +/// Legacy method for db using previous version of prefix encoding. +/// Only for trie radix 16 trie. +pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + if (prefix.1).0 == 0 { + prefixed_key.push(0); + prefixed_key.extend_from_slice(prefix.0); + } else { + let mut prev = 0x01u8; + for i in prefix.0.iter() { + prefixed_key.push((prev << 4) + (*i >> 4)); + prev = *i; + } + prefixed_key.push((prev << 4) + ((prefix.1).1 >> 4)); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + impl<'a, H, KF, T> Default for MemoryDB where H: KeyHasher, diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 51d170b8..12ee1a30 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -44,10 +44,10 @@ pub struct ExtensionLayout; impl TrieLayout for ExtensionLayout { const USE_EXTENSION: bool = true; - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodec; - type N = NibbleHalf; - type CB = Cache16; + type Hash = keccak_hasher::KeccakHasher; + type Codec = ReferenceNodeCodec; + type Nibble = NibbleHalf; + type Cache = Cache16; } impl TrieOps for ExtensionLayout { } @@ -57,10 +57,10 @@ pub struct NoExtensionLayout; impl TrieLayout for NoExtensionLayout { const USE_EXTENSION: bool = false; - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; - type N = NibbleHalf; - type CB = Cache16; + type Hash = keccak_hasher::KeccakHasher; + type Codec = ReferenceNodeCodecNoExt; + type Nibble = NibbleHalf; + type Cache = Cache16; } /// trie layout similar to substrate one @@ -68,10 +68,10 @@ pub struct SimpleNoExtensionLayout(PhantomData); impl TrieLayout for SimpleNoExtensionLayout { const USE_EXTENSION: bool = false; - type H = H; - type C = ReferenceNodeCodecNoExt; - type N = NibbleHalf; - type CB = Cache16; + type Hash = H; + type Codec = ReferenceNodeCodecNoExt; + type Nibble = NibbleHalf; + type Cache = Cache16; } impl TrieOps for SimpleNoExtensionLayout { } @@ -81,10 +81,10 @@ pub struct NoExtensionLayoutQuarter; impl TrieLayout for NoExtensionLayoutQuarter { const USE_EXTENSION: bool = false; - type H = keccak_hasher::KeccakHasher; - type C = ReferenceNodeCodecNoExt; - type N = NibbleQuarter; - type CB = Cache4; + type Hash = keccak_hasher::KeccakHasher; + type Codec = ReferenceNodeCodecNoExt; + type Nibble = NibbleQuarter; + type Cache = Cache4; } impl TrieOps for NoExtensionLayoutQuarter { } diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index b1813989..a484559c 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -39,14 +39,14 @@ impl ::std::fmt::Debug for TrieInsertionList { fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) where - ::Out: 'static + ::Out: 'static { let funs = vec![ Fun::new("Closed", |b, d: &TrieInsertionList| b.iter(&mut ||{ - trie_root::(d.0.clone()) + trie_root::(d.0.clone()) })), Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut ||{ - let mut memdb = MemoryDB::<_, HashKey, _>::new(&L::C::empty_node()[..]); + let mut memdb = MemoryDB::<_, HashKey, _>::new(&L::Codec::empty_node()[..]); let mut root = >::default(); let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { @@ -54,7 +54,7 @@ where } })), Fun::new("Iter", |b, d: &TrieInsertionList| { - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::C::empty_node()[..]); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::Codec::empty_node()[..]); let mut root = >::default(); { let mut t = TrieDBMut::::new(&mut memdb, &mut root); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 4445970c..1544b9e5 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -20,7 +20,6 @@ use reference_trie::{ RefTrieDBMut, reference_trie_root, calc_root_no_extension, - calc_root, compare_no_extension_insert_remove, }; use trie_db::{TrieMut, DBValue}; @@ -72,7 +71,6 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { let mut res = Vec::new(); let mut torem = None; - let mut a = 0; for (a, d) in data.into_iter().enumerate() { if a % 7 == 6 { // a random removal some time diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 600b58fb..1ffd0d95 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -38,14 +38,14 @@ where /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. pub fn new( - db: &'db dyn HashDBRef, + db: &'db dyn HashDBRef, root: &'db TrieHash, ) -> Result, CError> { Ok(FatDB { raw: TrieDB::new(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDBRef { self.raw.db() } + pub fn db(&self) -> &dyn HashDBRef { self.raw.db() } } impl<'db, L> Trie for FatDB<'db, L> @@ -55,14 +55,14 @@ where fn root(&self) -> &TrieHash { self.raw.root() } fn contains(&self, key: &[u8]) -> Result, CError> { - self.raw.contains(L::H::hash(key).as_ref()) + self.raw.contains(L::Hash::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(L::H::hash(key).as_ref(), query) + self.raw.get_with(L::Hash::hash(key).as_ref(), query) } fn iter<'a>(&'a self) -> Result< @@ -101,7 +101,7 @@ where L: TrieLayout, { fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { - let hashed_key = L::H::hash(key); + let hashed_key = L::Hash::hash(key); self.trie_iterator.seek(hashed_key.as_ref()) } } @@ -116,7 +116,7 @@ where self.trie_iterator.next() .map(|res| { res.map(|(hash, value)| { - let aux_hash = L::H::hash(&hash); + let aux_hash = L::Hash::hash(&hash); ( self.trie.db().get(&aux_hash, Default::default()) .expect("Missing fatdb hash") diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 62a4b1f5..943c4f1c 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -33,7 +33,7 @@ where /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { + pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { FatDBMut { raw: TrieDBMut::new(db, root) } } @@ -41,19 +41,19 @@ where /// /// Returns an error if root does not exist. pub fn from_existing( - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDB, root: &'db mut TrieHash ) -> Result, CError> { Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { + pub fn db(&self) -> &dyn HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut dyn HashDB { + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } } @@ -67,13 +67,13 @@ where fn is_empty(&self) -> bool { self.raw.is_empty() } fn contains(&self, key: &[u8]) -> Result, CError> { - self.raw.contains(L::H::hash(key).as_ref()) + self.raw.contains(L::Hash::hash(key).as_ref()) } fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(L::H::hash(key).as_ref()) + self.raw.get(L::Hash::hash(key).as_ref()) } fn insert( @@ -81,25 +81,25 @@ where key: &[u8], value: &[u8], ) -> Result, TrieHash, CError> { - let hash = L::H::hash(key); + let hash = L::Hash::hash(key); let out = self.raw.insert(hash.as_ref(), value)?; let db = self.raw.db_mut(); // insert if it doesn't exist. if out.is_none() { - let aux_hash = L::H::hash(hash.as_ref()); + let aux_hash = L::Hash::hash(hash.as_ref()); db.emplace(aux_hash, EMPTY_PREFIX, DBValue::from_slice(key)); } Ok(out) } fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { - let hash = L::H::hash(key); + let hash = L::Hash::hash(key); let out = self.raw.remove(hash.as_ref())?; // remove if it already exists. if out.is_some() { - let aux_hash = L::H::hash(hash.as_ref()); + let aux_hash = L::Hash::hash(hash.as_ref()); self.raw.db_mut().remove(&aux_hash, EMPTY_PREFIX); } @@ -145,4 +145,4 @@ mod test { t.remove(&key).unwrap(); assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), None); } -} \ No newline at end of file +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 9cd49d82..24e7eb06 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -70,7 +70,7 @@ impl CacheBuilder for Cache4 { } } -type ArrayNode = <::CB as CacheBuilder>>::Cache; +type ArrayNode = <::Cache as CacheBuilder>>::Cache; // (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) // first usize to get nb of added value, second usize last added index @@ -99,7 +99,7 @@ impl CacheAccum #[inline(always)] fn set_cache_value(&mut self, depth:usize, value: Option) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); + self.0.push((T::Cache::new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; debug_assert!(self.0[last].2 <= depth); @@ -109,7 +109,7 @@ impl CacheAccum #[inline(always)] fn set_node(&mut self, depth: usize, nibble_index: usize, node: CacheNode>) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::CB::new_vec_slice_buffer(), None, depth)); + self.0.push((T::Cache::new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; @@ -161,13 +161,13 @@ impl CacheAccum target_depth: usize, (k2, v2): &(impl AsRef<[u8]>, impl AsRef<[u8]>), ) { - let nibble_value = T::N::left_nibble_at(&k2.as_ref()[..], target_depth); + let nibble_value = T::Nibble::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], target_depth + 1); - let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset( + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], target_depth + 1); + let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::::new_offset( &k2.as_ref()[..], - k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len(), + k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), ); let hash = cb_ext.process(pr.left(), encoded, false); @@ -210,7 +210,7 @@ impl CacheAccum }; if !is_root { // put hash in parent - let nibble: u8 = T::N::left_nibble_at(&ref_branch.as_ref()[..], llix); + let nibble: u8 = T::Nibble::left_nibble_at(&ref_branch.as_ref()[..], llix); self.set_node(llix, nibble as usize, Some(h)); } } @@ -230,18 +230,18 @@ impl CacheAccum // encode branch let v = self.0[last].1.take(); - let encoded = T::C::branch_node( + let encoded = T::Codec::branch_node( self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref()), ); self.reset_depth(branch_d); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); - let encoded = T::C::extension_node(nib, nkeyix.1, branch_hash); + let encoded = T::Codec::extension_node(nib, nkeyix.1, branch_hash); let h = cb_ext.process(pr.left(), encoded, is_root); h } else { @@ -263,14 +263,14 @@ impl CacheAccum // encode branch let v = self.0[last].1.take(); let nkeyix = nkey.unwrap_or((0, 0)); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); - let encoded = T::C::branch_node_nibbled( + let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let encoded = T::Codec::branch_node_nibbled( pr.right_range_iter(nkeyix.1), nkeyix.1, self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref())); self.reset_depth(branch_d); let ext_length = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); - let pr = NibbleSlice::::new_offset( + let pr = NibbleSlice::::new_offset( &key_branch.as_ref()[..], branch_d - ext_length, ); @@ -301,10 +301,10 @@ pub fn trie_visit(input: I, cb_ext: &mut F) let mut single = true; for (k, v) in iter_input { single = false; - let common_depth = T::N::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = T::Nibble::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == previous_value.0.as_ref().len() * T::N::NIBBLE_PER_BYTE { + if common_depth == previous_value.0.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE { // the new key include the previous one : branch value case // just stored value at branch depth depth_queue.set_cache_value(common_depth, Some(previous_value.1)); @@ -325,11 +325,11 @@ pub fn trie_visit(input: I, cb_ext: &mut F) if single { // one single element corner case let (k2, v2) = previous_value; - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], last_depth); - let encoded = T::C::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset( + let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], last_depth); + let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::::new_offset( &k2.as_ref()[..], - k2.as_ref().len() * T::N::NIBBLE_PER_BYTE - nkey.len(), + k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), ); cb_ext.process(pr.left(), encoded, true); } else { @@ -339,7 +339,7 @@ pub fn trie_visit(input: I, cb_ext: &mut F) } } else { // nothing null root corner case - cb_ext.process(hash_db::EMPTY_PREFIX, T::C::empty_node().to_vec(), true); + cb_ext.process(hash_db::EMPTY_PREFIX, T::Codec::empty_node().to_vec(), true); } } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index a83a0d61..4485d0d1 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -197,7 +197,7 @@ pub trait Trie { fn root(&self) -> &TrieHash; /// Is the trie empty? - fn is_empty(&self) -> bool { *self.root() == L::C::hashed_null_node() } + fn is_empty(&self) -> bool { *self.root() == L::Codec::hashed_null_node() } /// Does the trie contain a given key? fn contains(&self, key: &[u8]) -> Result, CError> { @@ -214,7 +214,7 @@ pub trait Trie { /// Search for the key with the given query parameter. See the docs of the `Query` /// trait for more details. - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q @@ -326,7 +326,7 @@ impl<'db, L: TrieLayout> Trie for TrieKinds<'db, L> { wrapper!(self, contains, key) } - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q, ) -> Result, TrieHash, CError> @@ -356,7 +356,7 @@ where /// Create new immutable instance of Trie. pub fn readonly( &self, - db: &'db dyn HashDBRef, + db: &'db dyn HashDBRef, root: &'db TrieHash ) -> Result, TrieHash, CError> { match self.spec { @@ -369,7 +369,7 @@ where /// Create new mutable instance of Trie. pub fn create( &self, - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDB, root: &'db mut TrieHash, ) -> Box + 'db> { match self.spec { @@ -382,7 +382,7 @@ where /// Create new mutable instance of trie and check for errors. pub fn from_existing( &self, - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDB, root: &'db mut TrieHash, ) -> Result + 'db>, TrieHash, CError> { match self.spec { @@ -405,14 +405,14 @@ pub trait TrieLayout { /// use branch and node with partials in both. const USE_EXTENSION: bool; /// Hasher to use for this trie. - type H: Hasher; + type Hash: Hasher; /// Codec to use (needs to match hasher and nibble ops). - type C: NodeCodec; + type Codec: NodeCodec; /// Trie nibble constants. It defines trie radix. - type N: NibbleOps; + type Nibble: NibbleOps; /// Technical trait for cache, it should match the radix /// of `NibbleOps`. - type CB: CacheBuilder<::Out>; + type Cache: CacheBuilder<::Out>; } /// Trait with operation on key value iterator. @@ -421,8 +421,8 @@ pub trait TrieLayout { /// and exists only to allow alternate algorithm usage. pub trait TrieOps: Sized + TrieLayout { /// Operation to build a trie db from its ordered iterator over its key/values. - fn trie_build(db: &mut DB, input: I) -> ::Out where - DB: HashDB, + fn trie_build(db: &mut DB, input: I) -> ::Out where + DB: HashDB, I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -432,12 +432,12 @@ pub trait TrieOps: Sized + TrieLayout { cb.root.unwrap_or(Default::default()) } /// Determine a trie root given its ordered contents, closed form. - fn trie_root(input: I) -> ::Out where + fn trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let mut cb = TrieRoot::::default(); + let mut cb = TrieRoot::::default(); trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -447,7 +447,7 @@ pub trait TrieOps: Sized + TrieLayout { A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let mut cb = TrieRootUnhashed::::default(); + let mut cb = TrieRootUnhashed::::default(); trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } @@ -459,7 +459,7 @@ pub trait TrieOps: Sized + TrieLayout { } /// A trie root formed from the items, with keys attached according to their /// compact-encoded index (using `parity-codec` crate). - fn ordered_trie_root(input: I) -> ::Out + fn ordered_trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]>, @@ -473,8 +473,8 @@ pub trait TrieOps: Sized + TrieLayout { } /// Alias accessor to hasher hash output type from a `TrieLayout`. -pub type TrieHash = <::H as Hasher>::Out; +pub type TrieHash = <::Hash as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = < - ::C as NodeCodec<::H, ::N> + ::Codec as NodeCodec<::Hash, ::Nibble> >::Error; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index def2ba4e..11143ab1 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -24,9 +24,9 @@ use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash, Chi use alloc::boxed::Box; /// Trie lookup helper object. -pub struct Lookup<'a, L: TrieLayout, Q: Query> { +pub struct Lookup<'a, L: TrieLayout, Q: Query> { /// database to query from. - pub db: &'a dyn HashDBRef, + pub db: &'a dyn HashDBRef, /// Query object to record nodes and transform data. pub query: Q, /// Hash to start at @@ -36,13 +36,13 @@ pub struct Lookup<'a, L: TrieLayout, Q: Query> { impl<'a, L, Q> Lookup<'a, L, Q> where L: TrieLayout, - Q: Query, + Q: Query, { /// Look up the given key. If the value is found, it will be passed to the given /// function to decode or copy. pub fn look_up( mut self, - key: NibbleSlice, + key: NibbleSlice, ) -> Result, TrieHash, CError> { let mut partial = key; let mut hash = self.hash; @@ -64,7 +64,7 @@ where // without incrementing the depth. let mut node_data = &node_data[..]; loop { - let decoded = match L::C::decode(node_data) { + let decoded = match L::Codec::decode(node_data) { Ok(node) => node, Err(e) => { return Err(Box::new(TrieError::DecoderError(hash, e))) @@ -119,7 +119,7 @@ where } // check if new node data is inline or hash. - if let Some(h) = L::C::try_decode_hash(&node_data) { + if let Some(h) = L::Codec::try_decode_hash(&node_data) { hash = h; break } diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index ddf1df2e..e98880bd 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -39,7 +39,7 @@ where /// This guarantees the trie is built correctly. /// Returns an error if root does not exist. pub fn new( - db: &'db dyn HashDBRef, + db: &'db dyn HashDBRef, root: &'db TrieHash, ) -> Result, CError> { Ok(SecTrieDB { raw: TrieDB::new(db, root)? }) @@ -63,17 +63,17 @@ where fn root(&self) -> &TrieHash { self.raw.root() } fn contains(&self, key: &[u8]) -> Result, CError> { - self.raw.contains(L::H::hash(key).as_ref()) + self.raw.contains(L::Hash::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q, ) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(L::H::hash(key).as_ref(), query) + self.raw.get_with(L::Hash::hash(key).as_ref(), query) } fn iter<'a>(&'a self) -> Result< @@ -104,4 +104,4 @@ mod test { let t = RefSecTrieDB::new(&db, &root).unwrap(); assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); } -} \ No newline at end of file +} diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index 28b45a5e..324002fb 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -33,7 +33,7 @@ where /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { + pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { SecTrieDBMut { raw: TrieDBMut::new(db, root) } } @@ -41,17 +41,17 @@ where /// /// Returns an error if root does not exist. pub fn from_existing( - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDB, root: &'db mut TrieHash, ) -> Result, CError> { Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { self.raw.db() } + pub fn db(&self) -> &dyn HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } } impl<'db, L> TrieMut for SecTrieDBMut<'db, L> @@ -67,24 +67,24 @@ where } fn contains(&self, key: &[u8]) -> Result, CError> { - self.raw.contains(&L::H::hash(key).as_ref()) + self.raw.contains(&L::Hash::hash(key).as_ref()) } fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(&L::H::hash(key).as_ref()) + self.raw.get(&L::Hash::hash(key).as_ref()) } fn insert( &mut self, key: &[u8], value: &[u8], ) -> Result, TrieHash, CError> { - self.raw.insert(&L::H::hash(key).as_ref(), value) + self.raw.insert(&L::Hash::hash(key).as_ref(), value) } fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { - self.raw.remove(&L::H::hash(key).as_ref()) + self.raw.remove(&L::Hash::hash(key).as_ref()) } } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index d96681cf..dd1984f9 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -67,7 +67,7 @@ pub struct TrieDB<'db, L> where L: TrieLayout, { - db: &'db dyn HashDBRef, + db: &'db dyn HashDBRef, root: &'db TrieHash, /// The number of hashes performed so far in operations on this trie. hash_count: usize, @@ -80,7 +80,7 @@ where /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new( - db: &'db dyn HashDBRef, + db: &'db dyn HashDBRef, root: &'db TrieHash ) -> Result, CError> { if !db.contains(root, EMPTY_PREFIX) { @@ -91,7 +91,7 @@ where } /// Get the backing database. - pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } + pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } /// Get the data of the root node. pub fn root_data(&self) -> Result, CError> { @@ -109,7 +109,7 @@ where &'db self, node: &[u8], partial_key: Prefix, ) -> Result, TrieHash, CError> { - match (partial_key.0.is_empty() && (partial_key.1).0 == 0, L::C::try_decode_hash(node)) { + match (partial_key.0.is_empty() && (partial_key.1).0 == 0, L::Codec::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) @@ -127,7 +127,7 @@ where { fn root(&self) -> &TrieHash { self.root } - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q, @@ -159,7 +159,7 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: NibbleVec, + partial_key: NibbleVec, index: Option, } @@ -170,7 +170,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { - match L::C::decode(&node) { + match L::Codec::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { (ref mut d, Some(i)) => d.field("index", &i), @@ -306,8 +306,8 @@ impl Crumb { /// Iterator for going through all values in the trie. pub struct TrieDBIterator<'a, L: TrieLayout> { db: &'a TrieDB<'a, L>, - trail: Vec>, - key_nibbles: NibbleVec, + trail: Vec>, + key_nibbles: NibbleVec, } impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { @@ -325,14 +325,14 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { fn seek<'key>( &mut self, node_data: &DBValue, - key: NibbleSlice<'key, L::N>, + key: NibbleSlice<'key, L::Nibble>, ) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; let mut full_key_nibbles = 0; loop { let data = { - let node = L::C::decode(&node_data) + let node = L::Codec::decode(&node_data) .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; match node { Node::Leaf(slice, _) => { @@ -439,13 +439,13 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// Descend into a payload. fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { let node_data = &self.db.get_raw_or_lookup(d, self.key_nibbles.as_prefix())?; - let node = L::C::decode(&node_data) + let node = L::Codec::decode(&node_data) .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) } /// Descend into a payload. - fn descend_into_node(&mut self, node: OwnedNode) { + fn descend_into_node(&mut self, node: OwnedNode) { let trail = &mut self.trail; let key_nibbles = &mut self.key_nibbles; trail.push(Crumb { status: Status::Entering, node }); @@ -460,7 +460,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// The present key. This can only be called on valued node (key is therefore /// aligned to byte). - fn key(&self) -> NibbleSlice { + fn key(&self) -> NibbleSlice { self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") } @@ -549,7 +549,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { self.trail.pop(); }, IterStep::Descend::, CError>(Ok(d)) => { - let node = L::C::decode(&d).ok()?; + let node = L::Codec::decode(&d).ok()?; self.descend_into_node(node.into()) }, IterStep::Descend::, CError>(Err(e)) => { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 79a2eb30..ad95a7c1 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -354,7 +354,7 @@ where L: TrieLayout, { storage: NodeStorage>, - db: &'a mut dyn HashDB, + db: &'a mut dyn HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, death_row: HashSet<(TrieHash, (ElasticArray36, (u8, u8)))>, @@ -368,9 +368,9 @@ where L: TrieLayout, { /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut dyn HashDB, root: &'a mut TrieHash) -> Self { - *root = L::C::hashed_null_node(); - let root_handle = NodeHandle::Hash(L::C::hashed_null_node()); + pub fn new(db: &'a mut dyn HashDB, root: &'a mut TrieHash) -> Self { + *root = L::Codec::hashed_null_node(); + let root_handle = NodeHandle::Hash(L::Codec::hashed_null_node()); TrieDBMut { storage: NodeStorage::empty(), @@ -385,7 +385,7 @@ where /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing( - db: &'a mut dyn HashDB, + db: &'a mut dyn HashDB, root: &'a mut TrieHash, ) -> Result, CError> { if !db.contains(root, EMPTY_PREFIX) { @@ -403,12 +403,12 @@ where }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { + pub fn db(&self) -> &dyn HashDB { self.db } /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut dyn HashDB { + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.db } @@ -420,7 +420,7 @@ where ) -> Result, CError> { let node_encoded = self.db.get(&hash, key) .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + let node = Node::from_encoded::( &node_encoded, &*self.db, &mut self.storage @@ -433,14 +433,14 @@ where fn inspect( &mut self, stored: Stored>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, inspector: F, ) -> Result>, bool)>, TrieHash, CError> where F: FnOnce( &mut Self, Node>, - &mut NibbleFullKey, + &mut NibbleFullKey, ) -> Result>, TrieHash, CError>, { Ok(match stored { @@ -466,7 +466,7 @@ where // walk the trie, attempting to find the key's node. fn lookup<'x, 'key>( &'x self, - mut partial: NibbleSlice<'key, L::N>, + mut partial: NibbleSlice<'key, L::Nibble>, handle: &NodeHandle>, ) -> Result, TrieHash, CError> where 'x: 'key @@ -533,7 +533,7 @@ where fn insert_at( &mut self, handle: NodeHandle>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, ) -> Result<(StorageHandle, bool), TrieHash, CError> { @@ -554,7 +554,7 @@ where fn insert_inspector( &mut self, node: Node>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, ) -> Result>, TrieHash, CError> { @@ -640,7 +640,7 @@ where let nbranch_partial = existing_key.mid(common + 1).to_stored(); let low = Node::NibbledBranch(nbranch_partial, children, stored_value); let ix = existing_key.at(common); - let mut children = empty_children::<_, L::N>(); + let mut children = empty_children::<_, L::Nibble>(); let alloc_storage = self.storage.alloc(Stored::New(low)); @@ -729,7 +729,7 @@ where ); // one of us isn't empty: transmute to branch here - let mut children = empty_children::<_, L::N>(); + let mut children = empty_children::<_, L::Nibble>(); let branch = if L::USE_EXTENSION && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) @@ -761,7 +761,7 @@ where // make a stub branch let branch = Node::NibbledBranch( existing_key.to_stored(), - empty_children::<_, L::N>(), + empty_children::<_, L::Nibble>(), Some(stored_value), ); // augment the new branch. @@ -777,7 +777,7 @@ where // fully-shared prefix for an extension. // make a stub branch and an extension. - let branch = Node::Branch(empty_children::<_, L::N>(), Some(stored_value)); + let branch = Node::Branch(empty_children::<_, L::Nibble>(), Some(stored_value)); // augment the new branch. key.advance(common); let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -832,7 +832,7 @@ where assert!(!existing_key.is_empty()); let idx = existing_key.at(0) as usize; - let mut children = empty_children::<_, L::N>(); + let mut children = empty_children::<_, L::Nibble>(); children[idx] = if existing_key.len() == 1 { // direct extension, just replace. Some(child_branch) @@ -900,7 +900,7 @@ where fn remove_at( &mut self, handle: NodeHandle>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result, TrieHash, CError> { let stored = match handle { @@ -924,7 +924,7 @@ where fn remove_inspector( &mut self, node: Node>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result>, TrieHash, CError> { let partial = key.clone(); @@ -1053,7 +1053,7 @@ where target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, - NibbleSlice::::from_stored(&encoded), + NibbleSlice::::from_stored(&encoded), ); Action::Restore(Node::Leaf(encoded, value)) } @@ -1105,7 +1105,7 @@ where fn fix( &mut self, node: Node>, - key: NibbleSlice, + key: NibbleSlice, ) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { @@ -1134,7 +1134,7 @@ where (UsedIndex::One(a), None) => { // only one onward node. make an extension. - let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); + let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take() .expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); @@ -1144,7 +1144,7 @@ where // make a leaf. #[cfg(feature = "std")] trace!(target: "trie", "fixing: branch -> leaf"); - Ok(Node::Leaf(NibbleSlice::::new(&[]).to_stored(), value)) + Ok(Node::Leaf(NibbleSlice::::new(&[]).to_stored(), value)) } (_, value) => { // all is well. @@ -1182,15 +1182,15 @@ where let child = children[a as usize].take() .expect("used_index only set if occupied; qed"); let mut kc = key.clone(); - kc.advance((enc_nibble.1.len() * L::N::NIBBLE_PER_BYTE) - enc_nibble.0); + kc.advance((enc_nibble.1.len() * L::Nibble::NIBBLE_PER_BYTE) - enc_nibble.0); let (st, ost, op) = match kc.left() { - (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), - (st, (i, v)) if i == L::N::LAST_NIBBLE_INDEX => { + (st, (0, _v)) => (st, None, (1, L::Nibble::push_at_left(0, a, 0))), + (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); - so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); + so.push(L::Nibble::masked_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0, 0)) }, - (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), + (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), }; let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); let stored = match child { @@ -1213,11 +1213,11 @@ where match child_node { Node::Leaf(sub_partial, value) => { let mut enc_nibble = enc_nibble; - combine_key::( + combine_key::( &mut enc_nibble, - (L::N::NIBBLE_PER_BYTE - 1, &[a][..]), + (L::Nibble::NIBBLE_PER_BYTE - 1, &[a][..]), ); - combine_key::( + combine_key::( &mut enc_nibble, (sub_partial.0, &sub_partial.1[..]), ); @@ -1225,11 +1225,11 @@ where }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let mut enc_nibble = enc_nibble; - combine_key::( + combine_key::( &mut enc_nibble, - (L::N::NIBBLE_PER_BYTE - 1, &[a][..]), + (L::Nibble::NIBBLE_PER_BYTE - 1, &[a][..]), ); - combine_key::( + combine_key::( &mut enc_nibble, (sub_partial.0, &sub_partial.1[..]), ); @@ -1257,16 +1257,16 @@ where // recursively, so there might be some prefix from branch. let a = partial.1[partial.1.len() - 1] & (255 >> 4); let mut kc = key.clone(); - kc.advance((partial.1.len() * L::N::NIBBLE_PER_BYTE) - partial.0 - 1); + kc.advance((partial.1.len() * L::Nibble::NIBBLE_PER_BYTE) - partial.0 - 1); let (st, ost, op) = match kc.left() { - (st, (0, _v)) => (st, None, (1, L::N::push_at_left(0, a, 0))), - (st, (i, v)) if i == L::N::LAST_NIBBLE_INDEX => { + (st, (0, _v)) => (st, None, (1, L::Nibble::push_at_left(0, a, 0))), + (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); // Complete last byte with `a`. - so.push(L::N::masked_left(L::N::LAST_NIBBLE_INDEX, v) | a); + so.push(L::Nibble::masked_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0, 0)) }, - (st, (ix, v)) => (st, None, (ix, L::N::push_at_left(ix, a, v))), + (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), }; let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); @@ -1294,7 +1294,7 @@ where } // subpartial let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] trace!( target: "trie", @@ -1311,7 +1311,7 @@ where } // subpartial oly let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] trace!( target: "trie", @@ -1360,7 +1360,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::C, L::H, L::N>( + let encoded_root = node.into_encoded::<_, L::Codec, L::Hash, L::Nibble>( |child, o_slice, o_index| { let mov = k.append_slice_nibble(o_slice, o_index); let cr = self.commit_child(child, &mut k); @@ -1393,7 +1393,7 @@ where fn commit_child( &mut self, handle: NodeHandle>, - prefix: &mut NibbleVec, + prefix: &mut NibbleVec, ) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), @@ -1404,7 +1404,7 @@ where let encoded = { let commit_child = | node_handle, - o_slice: Option<&NibbleSlice>, + o_slice: Option<&NibbleSlice>, o_index: Option | { let mov = prefix.append_slice_nibble(o_slice, o_index); @@ -1412,9 +1412,9 @@ where prefix.drop_lasts(mov); cr }; - node.into_encoded::<_, L::C, L::H, L::N>(commit_child) + node.into_encoded::<_, L::Codec, L::Hash, L::Nibble>(commit_child) }; - if encoded.len() >= L::H::LENGTH { + if encoded.len() >= L::Hash::LENGTH { let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) @@ -1453,7 +1453,7 @@ where fn is_empty(&self) -> bool { match self.root_handle { - NodeHandle::Hash(h) => h == L::C::hashed_null_node(), + NodeHandle::Hash(h) => h == L::Codec::hashed_null_node(), NodeHandle::InMemory(ref h) => match self.storage[h] { Node::Empty => true, _ => false, @@ -1511,8 +1511,8 @@ where None => { #[cfg(feature = "std")] trace!(target: "trie", "remove: obliterated trie"); - self.root_handle = NodeHandle::Hash(L::C::hashed_null_node()); - *self.root = L::C::hashed_null_node(); + self.root_handle = NodeHandle::Hash(L::Codec::hashed_null_node()); + *self.root = L::Codec::hashed_null_node(); } } @@ -1602,7 +1602,7 @@ mod tests { fn reference_hashed_null_node() -> ::Out { - as NodeCodec::N>>::hashed_null_node() + as NodeCodec::Nibble>>::hashed_null_node() } #[test] From b6e8f9c4cccb145a9e3c76ad72b078abae0ae291 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 29 Jul 2019 11:17:38 +0200 Subject: [PATCH 111/120] Replace 'masked_' by 'pad_'. --- test-support/reference-trie/src/lib.rs | 66 ++++++++++-------- trie-db/fuzz/Cargo.toml | 4 ++ trie-db/fuzz/src/lib.rs | 3 - trie-db/src/iter_build.rs | 97 +++++++++++++------------- trie-db/src/lib.rs | 13 ++-- trie-db/src/nibble/mod.rs | 16 ++--- trie-db/src/nibble/nibbleslice.rs | 10 +-- trie-db/src/nibble/nibblevec.rs | 10 +-- trie-db/src/triedbmut.rs | 6 +- 9 files changed, 115 insertions(+), 110 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 12ee1a30..c968a2f6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -36,7 +36,7 @@ use keccak_hasher::KeccakHasher; pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, ChildSliceIndex}; -pub use trie_db::{Record, TrieLayout, TrieOps, NibbleHalf, NibbleQuarter, NibbleOps}; +pub use trie_db::{Record, TrieLayout, TrieConfiguration, NibbleHalf, NibbleQuarter, NibbleOps}; pub use trie_root::TrieStream; /// Trie layout using extension nodes. @@ -50,33 +50,35 @@ impl TrieLayout for ExtensionLayout { type Cache = Cache16; } -impl TrieOps for ExtensionLayout { } +impl TrieConfiguration for ExtensionLayout { } -/// Trie layout without extension nodes. -pub struct NoExtensionLayout; -impl TrieLayout for NoExtensionLayout { +/// Trie layout without extension nodes, allowing +/// generic hasher. +pub struct GenericNoExtensionLayout(PhantomData); + +impl TrieLayout for GenericNoExtensionLayout { const USE_EXTENSION: bool = false; - type Hash = keccak_hasher::KeccakHasher; + type Hash = H; type Codec = ReferenceNodeCodecNoExt; type Nibble = NibbleHalf; type Cache = Cache16; } -/// trie layout similar to substrate one -pub struct SimpleNoExtensionLayout(PhantomData); +impl TrieConfiguration for GenericNoExtensionLayout { } + +/// Trie layout without extension nodes. +pub struct NoExtensionLayout; -impl TrieLayout for SimpleNoExtensionLayout { +impl TrieLayout for NoExtensionLayout { const USE_EXTENSION: bool = false; - type Hash = H; + type Hash = keccak_hasher::KeccakHasher; type Codec = ReferenceNodeCodecNoExt; type Nibble = NibbleHalf; type Cache = Cache16; } -impl TrieOps for SimpleNoExtensionLayout { } - -/// Test quarter nibble +/// Test Layout for quarter nibble (radix 4 trie). pub struct NoExtensionLayoutQuarter; impl TrieLayout for NoExtensionLayoutQuarter { @@ -87,9 +89,9 @@ impl TrieLayout for NoExtensionLayoutQuarter { type Cache = Cache4; } -impl TrieOps for NoExtensionLayoutQuarter { } +impl TrieConfiguration for NoExtensionLayoutQuarter { } -/// bitmap codec for radix 16 +/// Children bitmap codec for radix 16 trie. pub struct BitMap16(u16); impl BitMap for BitMap16 { @@ -119,7 +121,7 @@ impl BitMap for BitMap16 { } } -/// bitmap codec for radix 4 +/// Children bitmap codec for radix 4 trie. pub struct BitMap4(u8); impl BitMap for BitMap4 { @@ -239,7 +241,7 @@ enum NodeKindNoExt { BranchWithValue, } -/// Create a leaf/branch node, encoding a number of nibbles. +/// Create a leaf or branch node header followed by its encoded partial nibbles. fn fuse_nibbles_node_no_extension<'a>( nibbles: &'a [u8], kind: NodeKindNoExt, @@ -256,12 +258,16 @@ fn fuse_nibbles_node_no_extension<'a>( .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) } +/// Encoding of branch header and children bitmap (for trie stream radix 16). +/// For stream variant with extension. fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { let mut result = [0, 0, 0]; branch_node_buffered::(has_value, has_children, &mut result[..]); result } +/// Encoding of branch header and children bitmap for any radix. +/// For codec/stream variant with extension. fn branch_node_buffered>( has_value: bool, has_children: I, @@ -276,6 +282,8 @@ fn branch_node_buffered>( BITMAP::encode(has_children, &mut output[1..]); } +/// Encoding of children bitmap (for trie stream radix 16). +/// For stream variant without extension. fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { let mut bitmap: u16 = 0; let mut cursor: u16 = 1; @@ -286,7 +294,7 @@ fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { ((bitmap % 256 ) as u8, (bitmap / 256 ) as u8) } -/// Reference implementation of a `TrieStream`. +/// Reference implementation of a `TrieStream` with extension nodes. #[derive(Default, Clone)] pub struct ReferenceTrieStream { buffer: Vec @@ -339,7 +347,7 @@ impl TrieStream for ReferenceTrieStream { fn out(self) -> Vec { self.buffer } } -/// Reference implementation of a `TrieStream`. +/// Reference implementation of a `TrieStream` without extension. #[derive(Default, Clone)] pub struct ReferenceTrieStreamNoExt { buffer: Vec @@ -556,9 +564,9 @@ impl Decode for NodeHeaderNoExt { pub struct ReferenceNodeCodec(PhantomData); /// Simple reference implementation of a `NodeCodec`. -/// Implementation follows initial specification of -/// https://github.com/w3f/polkadot-re-spec/issues/8. -/// But it is mainly the testing codec without extension node. +/// Even if implementation follows initial specification of +/// https://github.com/w3f/polkadot-re-spec/issues/8, this may +/// not follow it in the future, it is mainly the testing codec without extension node. #[derive(Default, Clone)] pub struct ReferenceNodeCodecNoExt(PhantomData); @@ -596,13 +604,12 @@ fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec 0 { - output.push(N::masked_right(number_nibble_encoded as u8, (partial.0).1)); + output.push(N::pad_right(number_nibble_encoded as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output } - fn partial_from_iterator_to_key>( partial: I, nibble_count: usize, @@ -636,8 +643,6 @@ fn partial_from_iterator_encode>( output } - - fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { let number_nibble_encoded = (partial.0).0 as usize; let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; @@ -654,7 +659,7 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> V NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if number_nibble_encoded > 0 { - output.push(N::masked_right(number_nibble_encoded as u8, (partial.0).1)); + output.push(N::pad_right(number_nibble_encoded as u8, (partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -837,7 +842,7 @@ impl< let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; // check that the padding is valid (if any) - if nibble_with_padding > 0 && N::masked_left(padding_length as u8, input[0]) != 0 { + if nibble_with_padding > 0 && N::pad_left(padding_length as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take( @@ -877,7 +882,7 @@ impl< let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; // check that the padding is valid (if any) - if nibble_with_padding > 0 && N::masked_left(padding_length as u8, input[0]) != 0 { + if nibble_with_padding > 0 && N::pad_left(padding_length as u8, input[0]) != 0 { return Err(ReferenceError::BadFormat); } let nibble_data = take( @@ -1219,7 +1224,8 @@ pub fn compare_implementations_no_extension_q( assert_eq!(root, root_new); } -/// `compare_implementations_no_extension` for unordered input. +/// `compare_implementations_no_extension` for unordered input (trie_root does +/// ordering before running when trie_build expect correct ordering). pub fn compare_implementations_no_extension_unordered( data: Vec<(Vec, Vec)>, mut memdb: impl hash_db::HashDB, diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index d9f1c11b..68a8e71d 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -48,3 +48,7 @@ path = "fuzz_targets/no_ext_insert.rs" [[bin]] name = "no_ext_insert_rem" path = "fuzz_targets/no_ext_insert_rem.rs" + +[[bin]] +name = "hfuzz_trie_root_new" +path = "hongfuzz_targets/trie_root_new.rs" diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 1544b9e5..7b89e12f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -129,7 +129,6 @@ fn fuzz_to_data_fix_length(input: &[u8]) -> Vec<(Vec,Vec)> { result } - fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { let mut m = std::collections::BTreeMap::new(); for (k,v) in input.into_iter() { @@ -138,7 +137,6 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { m.into_iter().collect() } - pub fn fuzz_that_compare_implementations(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); //println!("data:{:?}", &data); @@ -152,7 +150,6 @@ pub fn fuzz_that_unhashed_no_extension(input: &[u8]) { reference_trie::compare_unhashed_no_extension(data); } - pub fn fuzz_that_no_extension_insert(input: &[u8]) { let data = fuzz_to_data(input); //println!("data{:?}", data); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 24e7eb06..0c25ad64 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -72,9 +72,6 @@ impl CacheBuilder for Cache4 { type ArrayNode = <::Cache as CacheBuilder>>::Cache; -// (64 * 16) aka 2*byte size of key * nb nibble value, 2 being byte/nible (8/4) -// first usize to get nb of added value, second usize last added index -// second str is in branch value /// Struct containing iteration cache, can be at most the length of the lowest nibble. /// /// Note that it is not memory optimal (all depth are allocated even if some are empty due @@ -157,7 +154,7 @@ impl CacheAccum fn flush_value ( &mut self, - cb_ext: &mut impl ProcessEncodedNode>, + callback: &mut impl ProcessEncodedNode>, target_depth: usize, (k2, v2): &(impl AsRef<[u8]>, impl AsRef<[u8]>), ) { @@ -169,7 +166,7 @@ impl CacheAccum &k2.as_ref()[..], k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), ); - let hash = cb_ext.process(pr.left(), encoded, false); + let hash = callback.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) self.set_node(target_depth, nibble_value as usize, Some(hash)); @@ -178,7 +175,7 @@ impl CacheAccum fn flush_branch( &mut self, no_extension: bool, - cb_ext: &mut impl ProcessEncodedNode>, + callback: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, new_depth: usize, is_last: bool, @@ -204,9 +201,9 @@ impl CacheAccum let h = if no_extension { // encode branch - self.no_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + self.no_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) } else { - self.standard_extension(&ref_branch.as_ref()[..], cb_ext, lix, is_root, nkey) + self.standard_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) }; if !is_root { // put hash in parent @@ -220,7 +217,7 @@ impl CacheAccum fn standard_extension( &mut self, key_branch: &[u8], - cb_ext: &mut impl ProcessEncodedNode>, + callback: &mut impl ProcessEncodedNode>, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, @@ -236,13 +233,13 @@ impl CacheAccum ); self.reset_depth(branch_d); let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); - let branch_hash = cb_ext.process(pr.left(), encoded, is_root && nkey.is_none()); + let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); let encoded = T::Codec::extension_node(nib, nkeyix.1, branch_hash); - let h = cb_ext.process(pr.left(), encoded, is_root); + let h = callback.process(pr.left(), encoded, is_root); h } else { branch_hash @@ -253,7 +250,7 @@ impl CacheAccum fn no_extension( &mut self, key_branch: &[u8], - cb_ext: &mut impl ProcessEncodedNode>, + callback: &mut impl ProcessEncodedNode>, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, @@ -274,15 +271,16 @@ impl CacheAccum &key_branch.as_ref()[..], branch_d - ext_length, ); - cb_ext.process(pr.left(), encoded, is_root) + callback.process(pr.left(), encoded, is_root) } } -/// Function visiting trie from key/value inputs with a `ProccessEncodedNode` callback. -/// Calls to each node occurs ordered but with longest depth first (from node to -/// branch to root), this differs form key ordering a bit. -pub fn trie_visit(input: I, cb_ext: &mut F) +/// Function visiting trie from key value inputs with a `ProccessEncodedNode` callback. +/// This is the main entry point of this module. +/// Calls to each node occurs ordered by byte key value but with longest keys first (from node to +/// branch to root), this differs from standard byte array ordering a bit. +pub fn trie_visit(input: I, callback: &mut F) where T: TrieLayout, I: IntoIterator, @@ -310,12 +308,12 @@ pub fn trie_visit(input: I, cb_ext: &mut F) depth_queue.set_cache_value(common_depth, Some(previous_value.1)); } else if depth_item >= last_depth { // put previous with next (common branch previous value can be flush) - depth_queue.flush_value(cb_ext, depth_item, &previous_value); + depth_queue.flush_value(callback, depth_item, &previous_value); } else if depth_item < last_depth { // do not put with next, previous is last of a branch - depth_queue.flush_value(cb_ext, last_depth, &previous_value); + depth_queue.flush_value(callback, last_depth, &previous_value); let ref_branches = previous_value.0; - depth_queue.flush_branch(no_extension, cb_ext, ref_branches, depth_item, false); + depth_queue.flush_branch(no_extension, callback, ref_branches, depth_item, false); } previous_value = (k, v); @@ -331,15 +329,15 @@ pub fn trie_visit(input: I, cb_ext: &mut F) &k2.as_ref()[..], k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), ); - cb_ext.process(pr.left(), encoded, true); + callback.process(pr.left(), encoded, true); } else { - depth_queue.flush_value(cb_ext, last_depth, &previous_value); + depth_queue.flush_value(callback, last_depth, &previous_value); let ref_branches = previous_value.0; - depth_queue.flush_branch(no_extension, cb_ext, ref_branches, 0, true); + depth_queue.flush_branch(no_extension, callback, ref_branches, 0, true); } } else { // nothing null root corner case - cb_ext.process(hash_db::EMPTY_PREFIX, T::Codec::empty_node().to_vec(), true); + callback.process(hash_db::EMPTY_PREFIX, T::Codec::empty_node().to_vec(), true); } } @@ -348,10 +346,11 @@ pub trait ProcessEncodedNode { /// Function call with prefix, encoded value and a boolean indicating if the /// node is the root for each node of the trie. /// - /// Note that the returned value can be change depending on implementation, - /// but usually it should be the Hash of encoded node. This is returned for - /// optimisation purpose only (for builder hash_db does return this value). - fn process(&mut self, encoded_prefix: Prefix, Vec, bool) -> ChildReference; + /// Note that the returned value can change depending on implementation, + /// but usually it should be the Hash of encoded node. + /// This is not something direcly related to encoding but is here for + /// optimisation purpose (builder hash_db does return this value). + fn process(&mut self, prefix: Prefix, encoded_node: Vec, is_root: bool) -> ChildReference; } /// Get trie root and insert visited node in a hash_db. @@ -373,18 +372,18 @@ impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { fn process( &mut self, - encoded_prefix: Prefix, - enc_ext: Vec, + prefix: Prefix, + encoded_node: Vec, is_root: bool, ) -> ChildReference<::Out> { - let len = enc_ext.len(); + let len = encoded_node.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); } - let hash = self.db.insert(encoded_prefix, &enc_ext[..]); + let hash = self.db.insert(prefix, &encoded_node[..]); if is_root { self.root = Some(hash.clone()); }; @@ -409,17 +408,17 @@ impl ProcessEncodedNode<::Out> for TrieRoot, + encoded_node: Vec, is_root: bool, ) -> ChildReference<::Out> { - let len = enc_ext.len(); + let len = encoded_node.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); } - let hash = ::hash(&enc_ext[..]); + let hash = ::hash(&encoded_node[..]); if is_root { self.root = Some(hash.clone()); }; @@ -461,20 +460,20 @@ impl ProcessEncodedNode<::Out> for TrieRootPrint, + encoded_node: Vec, is_root: bool, ) -> ChildReference<::Out> { - println!("Encoded node: {:x?}", &enc_ext); + println!("Encoded node: {:x?}", &encoded_node); println!(" with prefix: {:x?}", &p); - let len = enc_ext.len(); + let len = encoded_node.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); println!(" inline len {}", len); return ChildReference::Inline(h, len); } - let hash = ::hash(&enc_ext[..]); + let hash = ::hash(&encoded_node[..]); if is_root { self.root = Some(hash.clone()); }; @@ -487,19 +486,19 @@ impl ProcessEncodedNode<::Out> for TrieRootUnhashed { fn process( &mut self, _: Prefix, - enc_ext: Vec, + encoded_node: Vec, is_root: bool, ) -> ChildReference<::Out> { - let len = enc_ext.len(); + let len = encoded_node.len(); if !is_root && len < ::LENGTH { let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&enc_ext[..len]); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); } - let hash = ::hash(&enc_ext[..]); + let hash = ::hash(&encoded_node[..]); if is_root { - self.root = Some(enc_ext); + self.root = Some(encoded_node); }; ChildReference::Hash(hash) } @@ -638,6 +637,8 @@ mod test { reference_trie::compare_unhashed_no_extension(data); } + // Following tests are a bunch of detected issue here for non regression. + #[test] fn trie_middle_node1 () { compare_implementations(vec![ @@ -645,7 +646,6 @@ mod test { (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), ]); } - #[test] fn trie_middle_node2 () { compare_implementations(vec![ @@ -810,7 +810,6 @@ mod test { (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1], vec![0;32]), ]); } - #[test] fn polka_re_test () { compare_implementations(vec![ diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 4485d0d1..5748d705 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -415,11 +415,10 @@ pub trait TrieLayout { type Cache: CacheBuilder<::Out>; } -/// Trait with operation on key value iterator. -/// It associates trie definition with chosen methods. -/// This trait contains its own default implementations -/// and exists only to allow alternate algorithm usage. -pub trait TrieOps: Sized + TrieLayout { +/// This traits associates a trie definition with prefered methods. +/// It also contains own default implementations and can be +/// use to allow switching implementation. +pub trait TrieConfiguration: Sized + TrieLayout { /// Operation to build a trie db from its ordered iterator over its key/values. fn trie_build(db: &mut DB, input: I) -> ::Out where DB: HashDB, @@ -431,7 +430,7 @@ pub trait TrieOps: Sized + TrieLayout { trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - /// Determine a trie root given its ordered contents, closed form. + /// Determines a trie root given its ordered contents, closed form. fn trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, @@ -441,7 +440,7 @@ pub trait TrieOps: Sized + TrieLayout { trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) } - /// Determine a trie root node's data given its ordered contents, closed form. + /// Determines a trie root node's data given its ordered contents, closed form. fn trie_root_unhashed(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 99332c26..0048d3f0 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -22,7 +22,7 @@ use elastic_array::ElasticArray36; use crate::node::NodeKey; use super::MaybeDebug; -// Workaround no constant function for pow. +// Work-around absence of constant function for math pow. const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// This trait contain Trie nibble specific definitions. @@ -32,9 +32,9 @@ const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; /// purpose. pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + MaybeDebug { /// See [`ByteLayout`]. - const REPR : ByteLayout; + const LAYOUT : ByteLayout; /// Single nibble length in bit. - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::REPR as usize]; + const BIT_PER_NIBBLE : usize = TWO_EXP[Self::LAYOUT as usize]; /// Number of nibble per byte. const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; /// Number of child for a branch (trie radix). @@ -65,15 +65,15 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy /// Mask a byte from a `ix` > 0 (ix being content). /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. #[inline(always)] - fn masked_left(ix: u8, b: u8) -> u8 { + fn pad_left(ix: u8, b: u8) -> u8 { debug_assert!(ix > 0); b & !Self::PADDING_BITMASK[ix as usize].0 } - /// Mask a byte from a ix > 0 (ix being content) + /// Mask a byte from a ix > 0 (ix being content). /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. #[inline(always)] - fn masked_right(ix: u8, b: u8) -> u8 { + fn pad_right(ix: u8, b: u8) -> u8 { if ix > 0 { b & Self::PADDING_BITMASK[Self::NIBBLE_PER_BYTE - ix as usize].0 } else { @@ -202,7 +202,7 @@ pub enum ByteLayout { } impl NibbleOps for NibbleHalf { - const REPR: ByteLayout = ByteLayout::Half; + const LAYOUT: ByteLayout = ByteLayout::Half; const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; type ChildSliceIndex = ChildSliceIndex16; } @@ -214,7 +214,7 @@ pub struct NibbleQuarter; // new_padded_end merged impl NibbleOps for NibbleQuarter { - const REPR: ByteLayout = ByteLayout::Quarter; + const LAYOUT: ByteLayout = ByteLayout::Quarter; const PADDING_BITMASK: &'static [(u8, usize)] = &[ (0b1111_1111, 6), (0b0011_1111, 4), diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index aa85778c..52279a51 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -153,7 +153,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { let split = self.offset / N::NIBBLE_PER_BYTE; let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; if nb > 0 { - ((nb, N::masked_right(nb, self.data[split])), &self.data[split + 1 ..]) + ((nb, N::pad_right(nb, self.data[split])), &self.data[split + 1 ..]) } else { ((0, 0), &self.data[split..]) } @@ -166,7 +166,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { ::core_::iter::from_fn(move || { if first.0 > 0 { first.0 = 0; - Some(N::masked_right(first.0, first.1)) + Some(N::pad_right(first.0, first.1)) } else { if ix < sl.len() { ix += 1; @@ -189,7 +189,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { ::core_::iter::from_fn( move || { if aligned { if nib_res > 0 { - let v = N::masked_right(nib_res as u8, self.data[ix]); + let v = N::pad_right(nib_res as u8, self.data[ix]); nib_res = 0; ix += 1; Some(v) @@ -204,7 +204,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { // unaligned if nib_res > 0 { let v = self.data[ix] >> s1; - let v = N::masked_right(nib_res as u8, v); + let v = N::pad_right(nib_res as u8, v); nib_res = 0; Some(v) } else if ix < ix_lim { @@ -228,7 +228,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { if ix == 0 { (&self.data[..split], (0, 0)) } else { - (&self.data[..split], (ix, N::masked_left(ix, self.data[split]))) + (&self.data[..split], (ix, N::pad_left(ix, self.data[split]))) } } diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index b8e951b6..4fbb46a9 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -75,7 +75,7 @@ impl NibbleVec { self.len -= 1; let i_new = self.len % N::NIBBLE_PER_BYTE; if i_new != 0 { - self.inner.push(N::masked_left(i_new as u8, byte)); + self.inner.push(N::pad_left(i_new as u8, byte)); } Some(N::at_left(i_new as u8, byte)) } @@ -95,7 +95,7 @@ impl NibbleVec { let pos = self.len % N::NIBBLE_PER_BYTE; if pos != 0 { let kl = self.inner.len() - 1; - self.inner[kl] = N::masked_left(pos as u8, self.inner[kl]); + self.inner[kl] = N::pad_left(pos as u8, self.inner[kl]); } } @@ -106,7 +106,7 @@ impl NibbleVec { if pos == 0 { (&self.inner[..split], (0, 0)) } else { - (&self.inner[..split], (pos, N::masked_left(pos, self.inner[split]))) + (&self.inner[..split], (pos, N::pad_left(pos, self.inner[split]))) } } @@ -120,7 +120,7 @@ impl NibbleVec { let last_index = self.len / N::NIBBLE_PER_BYTE; if offset > 0 { let (s1, s2) = N::split_shifts(offset); - self.inner[last_index] = N::masked_left(offset as u8, self.inner[last_index]) + self.inner[last_index] = N::pad_left(offset as u8, self.inner[last_index]) | (v.inner[0] >> s2); (0..v.inner.len() - 1) .for_each(|i| self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); @@ -145,7 +145,7 @@ impl NibbleVec { } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - self.inner[kend] = N::masked_left( + self.inner[kend] = N::pad_left( (N::NIBBLE_PER_BYTE - pad) as u8, self.inner[kend], ); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index ad95a7c1..852e0610 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1187,7 +1187,7 @@ where (st, (0, _v)) => (st, None, (1, L::Nibble::push_at_left(0, a, 0))), (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); - so.push(L::Nibble::masked_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); + so.push(L::Nibble::pad_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0, 0)) }, (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), @@ -1263,7 +1263,7 @@ where (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { let mut so: ElasticArray36 = st.into(); // Complete last byte with `a`. - so.push(L::Nibble::masked_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); + so.push(L::Nibble::pad_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); (st, Some(so), (0, 0)) }, (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), @@ -1537,7 +1537,7 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { let _shifted = N::shift_key(start, final_ofset); let st = if end.0 > 0 { let sl = start.1.len(); - start.1[sl - 1] |= N::masked_right((N::NIBBLE_PER_BYTE - end.0) as u8, end.1[0]); + start.1[sl - 1] |= N::pad_right((N::NIBBLE_PER_BYTE - end.0) as u8, end.1[0]); 1 } else { 0 From 5ada0fe731223bc011be2704b11ab6839ebdadfd Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 29 Jul 2019 11:40:09 +0200 Subject: [PATCH 112/120] documentation small fixes. --- trie-db/src/nibble/nibblevec.rs | 8 ++++---- trie-db/src/node_codec.rs | 8 +++----- trie-db/src/triedb.rs | 20 +++++++++----------- trie-db/src/triedbmut.rs | 31 ++++++++++++++++--------------- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 4fbb46a9..cf1abbd6 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -161,7 +161,7 @@ impl NibbleVec { /// Utility function for chaining two optional appending /// of `NibbleSlice` and/or a byte. /// Can be slow. - pub(crate) fn append_slice_nibble( + pub(crate) fn append_optional_slice_and_nibble( &mut self, o_slice: Option<&NibbleSlice>, o_index: Option, @@ -178,15 +178,15 @@ impl NibbleVec { res } - /// Utility function for `append_slice_nibble` after a clone. + /// Utility function for `append_optional_slice_and_nibble` after a clone. /// Can be slow. - pub(crate) fn clone_append_slice_nibble( + pub(crate) fn clone_append_optional_slice_and_nibble( &self, o_slice: Option<&NibbleSlice>, o_index: Option, ) -> Self { let mut p = self.clone(); - p.append_slice_nibble(o_slice, o_index); + p.append_optional_slice_and_nibble(o_slice, o_index); p } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 58e7285b..5410502f 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -38,17 +38,15 @@ pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} -/// Immutable representation of a nible slice (right aligned). +/// Representation of a nible slice (right aligned). /// It contains a right aligned padded first byte (first pair element is the number of nibbles /// (0 to max nb nibble - 1), second pair element is the padded nibble), and a slice over /// the remaining bytes. pub type Partial<'a> = ((u8, u8), &'a[u8]); -/// Trait for trie node encoding/decoding -/// TODO add const MAX_NODE_LEN and run all encoding over a mutable buffer, returning size. -> -/// avoid Vec by all means. +/// Trait for trie node encoding/decoding. pub trait NodeCodec: Sized { - /// Codec error type + /// Codec error type. type Error: Error; /// Get the hashed null node. diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index dd1984f9..4fe95ec1 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -189,7 +189,7 @@ where trie: self.trie, node_key: item, partial_key: self.partial_key - .clone_append_slice_nibble(Some(&slice), None), + .clone_append_optional_slice_and_nibble(Some(&slice), None), index: None, }) .finish(), @@ -202,7 +202,7 @@ where index: Some(i as u8), node_key: n, partial_key: self.partial_key - .clone_append_slice_nibble(None, Some(i as u8)), + .clone_append_optional_slice_and_nibble(None, Some(i as u8)), }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -222,7 +222,7 @@ where index: Some(i as u8), node_key: n, partial_key: self.partial_key - .clone_append_slice_nibble(Some(&slice), Some(i as u8)), + .clone_append_optional_slice_and_nibble(Some(&slice), Some(i as u8)), }).collect(); match (f.debug_struct("Node::NibbledBranch"), self.index) { (ref mut d, Some(ref i)) => d.field("index", i), @@ -596,8 +596,9 @@ mod tests { assert_eq!(pairs, iter_pairs); } + #[test] - fn iterator_works_no_ext() { + fn iterator_works_without_extension() { let pairs = vec![ (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), @@ -624,7 +625,6 @@ mod tests { assert_eq!(pairs, iter_pairs); } - #[test] fn iterator_seek_works() { let pairs = vec![ @@ -669,7 +669,7 @@ mod tests { } #[test] - fn iterator_seek_works_no_ext() { + fn iterator_seek_works_without_extension() { let pairs = vec![ (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), @@ -704,7 +704,6 @@ mod tests { ); } - #[test] fn iterator() { let d = vec![ @@ -737,7 +736,7 @@ mod tests { } #[test] - fn iterator_no_ext() { + fn iterator_without_extension() { let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), @@ -762,7 +761,6 @@ mod tests { assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } - #[test] fn iterator_seek() { let d = vec![ @@ -810,7 +808,7 @@ mod tests { } #[test] - fn get_len_with_ext() { + fn get_length_with_extension() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { @@ -826,7 +824,7 @@ mod tests { } #[test] - fn get_len_no_ext() { + fn get_length_without_extension() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 852e0610..e6d87ab7 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -68,8 +68,8 @@ fn empty_children() -> Vec>> { res } -/// type alias to indicate the nible cover a full key, -/// and left side therefore is a full prefix. +/// Type alias to indicate the nible covers a full key, +/// therefore its left side is a full prefix. type NibbleFullKey<'key, N> = NibbleSlice<'key, N>; /// Node types in the Trie. @@ -88,7 +88,7 @@ enum Node { Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. Branch(Vec>>, Option), - /// Branch node with support for a nibble (to avoid extension node) + /// Branch node with support for a nibble (to avoid extension node). NibbledBranch(NodeKey, Vec>>, Option), } @@ -116,7 +116,7 @@ where }) } - // decode a node from encoded bytes without getting its children. + // Decode a node from encoded bytes. fn from_encoded<'a, 'b, C, H, N>( data: &'a[u8], db: &dyn HashDB, @@ -320,8 +320,10 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// /// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. /// Note that changes are not committed to the database until `commit` is called. +/// /// Querying the root or dropping the trie will commit automatically. /// +/// /// # Example /// ``` /// extern crate trie_db; @@ -412,7 +414,7 @@ where self.db } - // cache a node by hash + // Cache a node by hash. fn cache( &mut self, hash: TrieHash, @@ -428,8 +430,8 @@ where Ok(self.storage.alloc(Stored::Cached(node, hash))) } - // inspect a node, choosing either to replace, restore, or delete it. - // if restored or replaced, returns the new node along with a flag of whether it was changed. + // Inspect a node, choosing either to replace, restore, or delete it. + // If restored or replaced, returns the new node along with a flag of whether it was changed. fn inspect( &mut self, stored: Stored>, @@ -463,7 +465,7 @@ where }) } - // walk the trie, attempting to find the key's node. + // Walk the trie, attempting to find the key's node. fn lookup<'x, 'key>( &'x self, mut partial: NibbleSlice<'key, L::Nibble>, @@ -529,7 +531,7 @@ where } } - /// insert a key-value pair into the trie, creating new nodes if necessary. + /// Insert a key-value pair into the trie, creating new nodes if necessary. fn insert_at( &mut self, handle: NodeHandle>, @@ -550,7 +552,7 @@ where Ok((self.storage.alloc(new_stored), changed)) } - /// the insertion inspector. + /// The insertion inspector. fn insert_inspector( &mut self, node: Node>, @@ -896,7 +898,7 @@ where }) } - /// Remove a node from the trie based on key. + /// Removes a node from the trie based on key. fn remove_at( &mut self, handle: NodeHandle>, @@ -920,7 +922,7 @@ where Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } - /// the removal inspector + /// The removal inspector. fn remove_inspector( &mut self, node: Node>, @@ -1362,7 +1364,7 @@ where let mut k = NibbleVec::new(); let encoded_root = node.into_encoded::<_, L::Codec, L::Hash, L::Nibble>( |child, o_slice, o_index| { - let mov = k.append_slice_nibble(o_slice, o_index); + let mov = k.append_optional_slice_and_nibble(o_slice, o_index); let cr = self.commit_child(child, &mut k); k.drop_lasts(mov); cr @@ -1407,7 +1409,7 @@ where o_slice: Option<&NibbleSlice>, o_index: Option | { - let mov = prefix.append_slice_nibble(o_slice, o_index); + let mov = prefix.append_optional_slice_and_nibble(o_slice, o_index); let cr = self.commit_child(node_handle, prefix); prefix.drop_lasts(mov); cr @@ -1752,7 +1754,6 @@ mod tests { ])[..]); } - #[test] fn insert_replace_root() { let mut memdb = MemoryDB::, DBValue>::default(); From 2fe240ca7ba0b828fc83685b6629ad8b40c838cf Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 29 Jul 2019 12:00:16 +0200 Subject: [PATCH 113/120] Restore new line at end. --- trie-root/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index bffc85cc..74185656 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -351,4 +351,4 @@ fn build_trie_trampoline( let mut substream = S::new(); build_trie::(input, cursor, &mut substream, no_extension); stream.append_substream::(substream); -} \ No newline at end of file +} From 5a81906fccbecc5bd4f9009fd50ff4d922edc7bf Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 29 Jul 2019 17:31:57 +0200 Subject: [PATCH 114/120] Remove old feature. --- hash-db/src/lib.rs | 1 - hash256-std-hasher/src/lib.rs | 1 - trie-root/src/lib.rs | 2 -- 3 files changed, 4 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 06e82197..677f2f50 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -14,7 +14,6 @@ //! Database of byte-slices keyed to their hash. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #[cfg(feature = "std")] use std::fmt::Debug; diff --git a/hash256-std-hasher/src/lib.rs b/hash256-std-hasher/src/lib.rs index 849db0fc..22192293 100644 --- a/hash256-std-hasher/src/lib.rs +++ b/hash256-std-hasher/src/lib.rs @@ -13,7 +13,6 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #[macro_use] extern crate crunchy; diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 74185656..91fecda9 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -17,8 +17,6 @@ //! This module should be used to generate trie root hash. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] -#![cfg_attr(not(feature = "std"), feature(alloc))] extern crate hash_db; From 8a9cc47191fc26153bc44ed88c89a03cf0951250 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 31 Jul 2019 21:07:09 +0200 Subject: [PATCH 115/120] Remove multiple trie radix support. --- hash-db/src/lib.rs | 7 +- memory-db/src/lib.rs | 14 +- test-support/reference-trie/src/lib.rs | 281 +++++++----------------- trie-db/benches/bench.rs | 4 +- trie-db/src/iter_build.rs | 63 ++---- trie-db/src/lib.rs | 12 +- trie-db/src/lookup.rs | 9 +- trie-db/src/nibble/mod.rs | 282 ++++++------------------- trie-db/src/nibble/nibbleslice.rs | 146 +++++-------- trie-db/src/nibble/nibblevec.rs | 157 +++++--------- trie-db/src/node.rs | 107 ++++------ trie-db/src/node_codec.rs | 7 +- trie-db/src/triedb.rs | 32 +-- trie-db/src/triedbmut.rs | 237 ++++++++++----------- 14 files changed, 470 insertions(+), 888 deletions(-) diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index dced7c07..cfd2a067 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -40,14 +40,13 @@ impl MaybeDebug for T {} /// nibbles (the node key can be split into prefix and node partial). /// Therefore it is always the leftmost portion of the node key, so its internal representation /// is a non expanded byte slice followed by a last padded byte representation. -/// The padded byte is a pair of u8 containing the number of nibble, followed by -/// the left aligned padded value. -pub type Prefix<'a> = (&'a[u8], (u8, u8)); +/// The padded byte is an optional padded value. +pub type Prefix<'a> = (&'a[u8], Option); /// An empty prefix constant. /// Can be use when the prefix is not use internally /// or for root nodes. -pub static EMPTY_PREFIX: Prefix<'static> = (&[], (0, 0)); +pub static EMPTY_PREFIX: Prefix<'static> = (&[], None); /// Trait describing an object that can hash a slice of bytes. Used to abstract /// other types over the hashing algorithm. Defines a single `hash` method and an diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 638950e5..6749c205 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -196,8 +196,8 @@ impl KeyFunction for PrefixedKey { pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); prefixed_key.extend_from_slice(prefix.0); - if (prefix.1).0 > 0 { - prefixed_key.push((prefix.1).1); + if let Some(last) = prefix.1 { + prefixed_key.push(last); } prefixed_key.extend_from_slice(key.as_ref()); prefixed_key @@ -222,16 +222,16 @@ impl KeyFunction for LegacyPrefixedKey { /// Only for trie radix 16 trie. pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); - if (prefix.1).0 == 0 { - prefixed_key.push(0); - prefixed_key.extend_from_slice(prefix.0); - } else { + if let Some(last) = prefix.1 { let mut prev = 0x01u8; for i in prefix.0.iter() { prefixed_key.push((prev << 4) + (*i >> 4)); prev = *i; } - prefixed_key.push((prev << 4) + ((prefix.1).1 >> 4)); + prefixed_key.push((prev << 4) + (last >> 4)); + } else { + prefixed_key.push(0); + prefixed_key.extend_from_slice(prefix.0); } prefixed_key.extend_from_slice(key.as_ref()); prefixed_key diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index a7ad2eb2..6e3ac833 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -27,15 +27,12 @@ use trie_db::{ TrieBuilder, TrieRoot, Partial, - Cache16, - Cache4, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec, BitMap, - ChildSliceIndex}; -pub use trie_db::{Record, TrieLayout, TrieConfiguration, NibbleHalf, NibbleQuarter, NibbleOps}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; +pub use trie_db::{Record, TrieLayout, TrieConfiguration, nibble_ops}; pub use trie_root::TrieStream; /// Trie layout using extension nodes. @@ -44,14 +41,11 @@ pub struct ExtensionLayout; impl TrieLayout for ExtensionLayout { const USE_EXTENSION: bool = true; type Hash = keccak_hasher::KeccakHasher; - type Codec = ReferenceNodeCodec; - type Nibble = NibbleHalf; - type Cache = Cache16; + type Codec = ReferenceNodeCodec; } impl TrieConfiguration for ExtensionLayout { } - /// Trie layout without extension nodes, allowing /// generic hasher. pub struct GenericNoExtensionLayout(PhantomData); @@ -59,9 +53,7 @@ pub struct GenericNoExtensionLayout(PhantomData); impl TrieLayout for GenericNoExtensionLayout { const USE_EXTENSION: bool = false; type Hash = H; - type Codec = ReferenceNodeCodecNoExt; - type Nibble = NibbleHalf; - type Cache = Cache16; + type Codec = ReferenceNodeCodecNoExt; } impl TrieConfiguration for GenericNoExtensionLayout { } @@ -72,35 +64,19 @@ pub struct NoExtensionLayout; impl TrieLayout for NoExtensionLayout { const USE_EXTENSION: bool = false; type Hash = keccak_hasher::KeccakHasher; - type Codec = ReferenceNodeCodecNoExt; - type Nibble = NibbleHalf; - type Cache = Cache16; + type Codec = ReferenceNodeCodecNoExt; } -/// Test Layout for quarter nibble (radix 4 trie). -pub struct NoExtensionLayoutQuarter; - -impl TrieLayout for NoExtensionLayoutQuarter { - const USE_EXTENSION: bool = false; - type Hash = keccak_hasher::KeccakHasher; - type Codec = ReferenceNodeCodecNoExt; - type Nibble = NibbleQuarter; - type Cache = Cache4; -} - -impl TrieConfiguration for NoExtensionLayoutQuarter { } - /// Children bitmap codec for radix 16 trie. -pub struct BitMap16(u16); +pub struct Bitmap(u16); -impl BitMap for BitMap16 { - const ENCODED_LEN: usize = 2; - type Error = CodecError; - type Buffer = [u8;3]; // need a byte for header +const BITMAP_LENGTH: usize = 2; + +impl Bitmap { - fn decode(data: &[u8]) -> Result { + fn decode(data: &[u8]) -> Result { Ok(u16::decode(&mut &data[..]) - .map(|v| BitMap16(v))?) + .map(|v| Bitmap(v))?) } fn value_at(&self, i: usize) -> bool { @@ -119,51 +95,16 @@ impl BitMap for BitMap16 { } } -/// Children bitmap codec for radix 4 trie. -pub struct BitMap4(u8); - -impl BitMap for BitMap4 { - const ENCODED_LEN: usize = 1; - type Error = CodecError; - type Buffer = [u8;2]; // need a byte for header - - fn decode(data: &[u8]) -> Result { - if data.len() == 0 || data[0] & 0xf0 != 0 { - Err("Bad format".into()) - } else { - Ok(BitMap4(data[0])) - } - } - - fn value_at(&self, i: usize) -> bool { - self.0 & (1u8 << i) != 0 - } - - fn encode>(has_children: I , output: &mut [u8]) { - let mut bitmap: u8 = 0; - let mut cursor: u8 = 1; - for v in has_children { - if v { bitmap |= cursor } - cursor <<= 1; - } - output[0] = bitmap; - } - -} - pub type RefTrieDB<'a> = trie_db::TrieDB<'a, ExtensionLayout>; pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, NoExtensionLayout>; -pub type RefTrieDBNoExtQ<'a> = trie_db::TrieDB<'a, NoExtensionLayoutQuarter>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, ExtensionLayout>; pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, NoExtensionLayout>; -pub type RefTrieDBMutNoExtQ<'a> = trie_db::TrieDBMut<'a, NoExtensionLayoutQuarter>; pub type RefFatDB<'a> = trie_db::FatDB<'a, ExtensionLayout>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, ExtensionLayout>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, ExtensionLayout>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, ExtensionLayout>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, ExtensionLayout, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; -pub type RefLookupNoExtQ<'a, Q> = trie_db::Lookup<'a, NoExtensionLayoutQuarter, Q>; pub fn reference_trie_root(input: I) -> ::Out where I: IntoIterator, @@ -260,13 +201,13 @@ fn fuse_nibbles_node_no_extension<'a>( /// For stream variant with extension. fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { let mut result = [0, 0, 0]; - branch_node_buffered::(has_value, has_children, &mut result[..]); + branch_node_buffered(has_value, has_children, &mut result[..]); result } /// Encoding of branch header and children bitmap for any radix. /// For codec/stream variant with extension. -fn branch_node_buffered>( +fn branch_node_buffered>( has_value: bool, has_children: I, output: &mut[u8], @@ -277,7 +218,7 @@ fn branch_node_buffered>( BRANCH_NODE_NO_VALUE }; output[0] = first; - BITMAP::encode(has_children, &mut output[1..]); + Bitmap::encode(has_children, &mut output[1..]); } /// Encoding of children bitmap (for trie stream radix 16). @@ -559,14 +500,14 @@ impl Decode for NodeHeaderNoExt { /// Simple reference implementation of a `NodeCodec`. #[derive(Default, Clone)] -pub struct ReferenceNodeCodec(PhantomData); +pub struct ReferenceNodeCodec; /// Simple reference implementation of a `NodeCodec`. /// Even if implementation follows initial specification of /// https://github.com/w3f/polkadot-re-spec/issues/8, this may /// not follow it in the future, it is mainly the testing codec without extension node. #[derive(Default, Clone)] -pub struct ReferenceNodeCodecNoExt(PhantomData); +pub struct ReferenceNodeCodecNoExt; fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { if input.len() < count { @@ -577,39 +518,39 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { Some(r) } -fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { +fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { let number_nibble_encoded = (partial.0).0 as usize; - let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; + let nibble_count = partial.1.len() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; if number_nibble_encoded > 0 { - output.push(N::pad_right(number_nibble_encoded as u8, (partial.0).1)); + output.push(nibble_ops::pad_right((partial.0).1)); } output.extend_from_slice(&partial.1[..]); output } -fn partial_from_iterator_to_key>( +fn partial_from_iterator_to_key>( partial: I, nibble_count: usize, offset: u8, over: u8, ) -> Vec { assert!(nibble_count < over as usize); - let mut output = Vec::with_capacity(1 + (nibble_count / N::NIBBLE_PER_BYTE)); + let mut output = Vec::with_capacity(1 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); output.push(offset + nibble_count as u8); output.extend(partial); output } -fn partial_from_iterator_encode>( +fn partial_from_iterator_encode>( partial: I, nibble_count: usize, node_kind: NodeKindNoExt, ) -> Vec { let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); - let mut output = Vec::with_capacity(3 + (nibble_count / N::NIBBLE_PER_BYTE)); + let mut output = Vec::with_capacity(3 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); match node_kind { NodeKindNoExt::Leaf => NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), @@ -622,9 +563,9 @@ fn partial_from_iterator_encode>( output } -fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { +fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { let number_nibble_encoded = (partial.0).0 as usize; - let nibble_count = partial.1.len() * N::NIBBLE_PER_BYTE + number_nibble_encoded; + let nibble_count = partial.1.len() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); @@ -638,7 +579,7 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> V NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), }; if number_nibble_encoded > 0 { - output.push(N::pad_right(number_nibble_encoded as u8, (partial.0).1)); + output.push(nibble_ops::pad_right((partial.0).1)); } output.extend_from_slice(&partial.1[..]); output @@ -649,25 +590,21 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> V // but due to the current limitations of Rust const evaluation we can't do // `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. // Perhaps one day soon? -impl< - H: Hasher, - N: NibbleOps, - BITMAP: BitMap -> NodeCodec for ReferenceNodeCodec { +impl NodeCodec for ReferenceNodeCodec { type Error = CodecError; fn hashed_null_node() -> ::Out { - H::hash(>::empty_node()) + H::hash(>::empty_node()) } - fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { + fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; match NodeHeader::decode(input)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - let bitmap_slice = take(input, BITMAP::ENCODED_LEN) + let bitmap_slice = take(input, BITMAP_LENGTH) .ok_or(CodecError::from("Bad format"))?; - let bitmap = BITMAP::decode(&bitmap_slice[..])?; + let bitmap = Bitmap::decode(&bitmap_slice[..])?; let value = if has_value { let count = >::decode(input)?.0 as usize; @@ -675,27 +612,23 @@ impl< } else { None }; - let mut children: N::ChildSliceIndex = Default::default(); - let child_val = &**input; - let mut ix = 0; - children.as_mut()[0] = ix; - for i in 0..N::NIBBLE_LENGTH { + let mut children = [None; 16]; + + for i in 0..nibble_ops::NIBBLE_LENGTH { if bitmap.value_at(i) { let count = >::decode(input)?.0 as usize; - let _ = take(input, count); - ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; + children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); } - children.as_mut()[i + 1] = ix; } - Ok(Node::Branch((children, child_val), value)) + Ok(Node::Branch(children, value)) } NodeHeader::Extension(nibble_count) => { let nibble_data = take( input, - (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::number_padding(nibble_count)); + nibble_ops::number_padding(nibble_count)); let count = >::decode(input)?.0 as usize; Ok(Node::Extension(nibble_slice, take(input, count) .ok_or(CodecError::from("Bad format"))?)) @@ -703,11 +636,11 @@ impl< NodeHeader::Leaf(nibble_count) => { let nibble_data = take( input, - (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; let nibble_slice = NibbleSlice::new_offset( nibble_data, - N::number_padding(nibble_count), + nibble_ops::number_padding(nibble_count), ); let count = >::decode(input)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count) @@ -727,7 +660,7 @@ impl< } fn is_empty_node(data: &[u8]) -> bool { - data == >::empty_node() + data == >::empty_node() } fn empty_node() -> &'static[u8] { @@ -735,7 +668,7 @@ impl< } fn leaf_node(partial: Partial, value: &[u8]) -> Vec { - let mut output = partial_to_key::(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); + let mut output = partial_to_key(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); value.encode_to(&mut output); output } @@ -745,7 +678,7 @@ impl< number_nibble: usize, child: ChildReference<::Out>, ) -> Vec { - let mut output = partial_from_iterator_to_key::( + let mut output = partial_from_iterator_to_key( partial, number_nibble, EXTENSION_NODE_OFFSET, @@ -763,8 +696,8 @@ impl< children: impl Iterator::Out>>>>, maybe_value: Option<&[u8]>, ) -> Vec { - let mut output = vec![0; BITMAP::ENCODED_LEN + 1]; - let mut prefix: BITMAP::Buffer = Default::default(); + let mut output = vec![0; BITMAP_LENGTH + 1]; + let mut prefix: [u8; 3] = [0; 3]; let have_value = if let Some(value) = maybe_value { value.encode_to(&mut output); true @@ -782,8 +715,8 @@ impl< } None => false, }); - branch_node_buffered::(have_value, has_children, prefix.as_mut()); - output[0..BITMAP::ENCODED_LEN + 1].copy_from_slice(prefix.as_ref()); + branch_node_buffered(have_value, has_children, prefix.as_mut()); + output[0..BITMAP_LENGTH + 1].copy_from_slice(prefix.as_ref()); output } @@ -797,73 +730,63 @@ impl< } -impl< - H: Hasher, - N: NibbleOps, - BITMAP: BitMap, -> NodeCodec for ReferenceNodeCodecNoExt { +impl NodeCodec for ReferenceNodeCodecNoExt { type Error = CodecError; fn hashed_null_node() -> ::Out { - H::hash(>::empty_node()) + H::hash(>::empty_node()) } - fn decode(data: &[u8]) -> ::std::result::Result, Self::Error> { + fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; let head = NodeHeaderNoExt::decode(input)?; match head { NodeHeaderNoExt::Null => Ok(Node::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { - let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; - let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; // check that the padding is valid (if any) - if nibble_with_padding > 0 && N::pad_left(padding_length as u8, input[0]) != 0 { + if padding && nibble_ops::pad_left(input[0]) != 0 { return Err(CodecError::from("Bad format")); } let nibble_data = take( input, - (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::number_padding(nibble_count)); + nibble_ops::number_padding(nibble_count)); let bitmap_slice = take( input, - BITMAP::ENCODED_LEN, + BITMAP_LENGTH, ).ok_or(CodecError::from("Bad format"))?; - let bitmap = BITMAP::decode(&bitmap_slice[..])?; + let bitmap = Bitmap::decode(&bitmap_slice[..])?; let value = if has_value { let count = >::decode(input)?.0 as usize; Some(take(input, count).ok_or(CodecError::from("Bad format"))?) } else { None }; - let mut children: N::ChildSliceIndex = Default::default(); - let child_val = &**input; - let mut ix = 0; - children.as_mut()[0] = ix; - for i in 0..N::NIBBLE_LENGTH { + let mut children = [None; 16]; + + for i in 0..nibble_ops::NIBBLE_LENGTH { if bitmap.value_at(i) { let count = >::decode(input)?.0 as usize; - let _ = take(input, count); - ix += count + N::ChildSliceIndex::CONTENT_HEADER_SIZE; + children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); } - children.as_mut()[i + 1] = ix; } - Ok(Node::NibbledBranch(nibble_slice, (children, child_val), value)) + Ok(Node::NibbledBranch(nibble_slice, children, value)) } NodeHeaderNoExt::Leaf(nibble_count) => { - let nibble_with_padding = nibble_count % N::NIBBLE_PER_BYTE; - let padding_length = N::NIBBLE_PER_BYTE - nibble_with_padding; + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; // check that the padding is valid (if any) - if nibble_with_padding > 0 && N::pad_left(padding_length as u8, input[0]) != 0 { + if padding && nibble_ops::pad_left(input[0]) != 0 { return Err(CodecError::from("Bad format")); } let nibble_data = take( input, - (nibble_count + (N::NIBBLE_PER_BYTE - 1)) / N::NIBBLE_PER_BYTE, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; let nibble_slice = NibbleSlice::new_offset(nibble_data, - N::number_padding(nibble_count)); + nibble_ops::number_padding(nibble_count)); let count = >::decode(input)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(CodecError::from("Bad format"))?)) } @@ -871,11 +794,11 @@ impl< } fn try_decode_hash(data: &[u8]) -> Option<::Out> { - as NodeCodec>::try_decode_hash(data) + >::try_decode_hash(data) } fn is_empty_node(data: &[u8]) -> bool { - data == >::empty_node() + data == >::empty_node() } fn empty_node() -> &'static [u8] { @@ -883,7 +806,7 @@ impl< } fn leaf_node(partial: Partial, value: &[u8]) -> Vec { - let mut output = partial_encode::(partial, NodeKindNoExt::Leaf); + let mut output = partial_encode(partial, NodeKindNoExt::Leaf); value.encode_to(&mut output); output } @@ -910,25 +833,25 @@ impl< maybe_value: Option<&[u8]>, ) -> Vec { let mut output = if maybe_value.is_some() { - partial_from_iterator_encode::( + partial_from_iterator_encode( partial, number_nibble, NodeKindNoExt::BranchWithValue, ) } else { - partial_from_iterator_encode::( + partial_from_iterator_encode( partial, number_nibble, NodeKindNoExt::BranchNoValue, ) }; let bitmap_index = output.len(); - let mut bitmap: BITMAP::Buffer = Default::default(); - (0..BITMAP::ENCODED_LEN).for_each(|_| output.push(0)); + let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH]; + (0..BITMAP_LENGTH).for_each(|_| output.push(0)); if let Some(value) = maybe_value { value.encode_to(&mut output); }; - BITMAP::encode(children.map(|maybe_child| match maybe_child.borrow() { + Bitmap::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true @@ -939,8 +862,8 @@ impl< } None => false, }), bitmap.as_mut()); - output[bitmap_index..bitmap_index + BITMAP::ENCODED_LEN] - .copy_from_slice(&bitmap.as_ref()[..BITMAP::ENCODED_LEN]); + output[bitmap_index..bitmap_index + BITMAP_LENGTH] + .copy_from_slice(&bitmap.as_ref()[..BITMAP_LENGTH]); output } @@ -1146,56 +1069,6 @@ pub fn compare_implementations_no_extension( assert_eq!(root, root_new); } -/// Compare trie builder and in memory trie. -/// This uses the variant without extension nodes. -/// This uses a radix 4 trie. -pub fn compare_implementations_no_extension_q( - data: Vec<(Vec, Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, -) { - let root_new = { - let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) - }; - let root = { - let mut root = Default::default(); - let mut t = RefTrieDBMutNoExtQ::new(&mut memdb, &mut root); - for i in 0..data.len() { - t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); - } - t.root().clone() - }; - { - let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); - println!("{:?}", t); - } - - if root != root_new { - { - let db : &dyn hash_db::HashDB<_, _> = &hashdb; - let t = RefTrieDBNoExtQ::new(&db, &root_new).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - - { - let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDBNoExtQ::new(&db, &root).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - } - - assert_eq!(root, root_new); -} - /// `compare_implementations_no_extension` for unordered input (trie_root does /// ordering before running when trie_build expect correct ordering). pub fn compare_implementations_no_extension_unordered( @@ -1288,9 +1161,9 @@ pub fn compare_no_extension_insert_remove( fn too_big_nibble_length () { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1]; - let enc = as NodeCodec> + let enc = > ::leaf_node(((0, 0), &input), &[1]); - let dec = as NodeCodec> + let dec = > ::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl, _) = dec { Some(sl) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 29b88996..0033aa4c 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -39,7 +39,7 @@ extern crate trie_db; extern crate memory_db; extern crate rand; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; -use trie_db::{NibbleSlice, NibbleHalf}; +use trie_db::NibbleSlice; fn nibble_common_prefix(b: &mut Criterion) { let st = StandardMap { @@ -52,7 +52,7 @@ fn nibble_common_prefix(b: &mut Criterion) { let (keys, values): (Vec<_>, Vec<_>) = st.make().into_iter().unzip(); b.bench_function("nibble_common_prefix", move |b| { let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { - (NibbleSlice::::new(pair.0), NibbleSlice::::new(pair.1)) + (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) }).collect(); b.iter(&mut || { diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 0c25ad64..e1a4a5b1 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -22,7 +22,7 @@ use core_::marker::PhantomData; use core_::cmp::max; use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; -use crate::nibble::NibbleOps; +use crate::nibble::nibble_ops; use node_codec::NodeCodec; use crate::{TrieLayout, TrieHash}; @@ -52,25 +52,12 @@ pub struct Cache16; /// Cache builder for radix 4 trie. pub struct Cache4; -impl CacheBuilder for Cache16 { - const SIZE: usize = 16; - type Cache = [CacheNode; 16]; - #[inline(always)] - fn new_vec_slice_buffer() -> Self::Cache { - exponential_out!(@3, [None, None]) - } +#[inline(always)] +fn new_vec_slice_buffer() -> [CacheNode; 16] { + exponential_out!(@3, [None, None]) } -impl CacheBuilder for Cache4 { - const SIZE: usize = 4; - type Cache = [CacheNode; 4]; - #[inline(always)] - fn new_vec_slice_buffer() -> Self::Cache { - exponential_out!(@2, [None]) - } -} - -type ArrayNode = <::Cache as CacheBuilder>>::Cache; +type ArrayNode = [CacheNode>; 16]; /// Struct containing iteration cache, can be at most the length of the lowest nibble. /// @@ -96,7 +83,7 @@ impl CacheAccum #[inline(always)] fn set_cache_value(&mut self, depth:usize, value: Option) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::Cache::new_vec_slice_buffer(), None, depth)); + self.0.push((new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; debug_assert!(self.0[last].2 <= depth); @@ -106,7 +93,7 @@ impl CacheAccum #[inline(always)] fn set_node(&mut self, depth: usize, nibble_index: usize, node: CacheNode>) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { - self.0.push((T::Cache::new_vec_slice_buffer(), None, depth)); + self.0.push((new_vec_slice_buffer(), None, depth)); } let last = self.0.len() - 1; @@ -158,13 +145,13 @@ impl CacheAccum target_depth: usize, (k2, v2): &(impl AsRef<[u8]>, impl AsRef<[u8]>), ) { - let nibble_value = T::Nibble::left_nibble_at(&k2.as_ref()[..], target_depth); + let nibble_value = nibble_ops::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], target_depth + 1); + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], target_depth + 1); let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset( + let pr = NibbleSlice::new_offset( &k2.as_ref()[..], - k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), + k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); let hash = callback.process(pr.left(), encoded, false); @@ -207,7 +194,7 @@ impl CacheAccum }; if !is_root { // put hash in parent - let nibble: u8 = T::Nibble::left_nibble_at(&ref_branch.as_ref()[..], llix); + let nibble: u8 = nibble_ops::left_nibble_at(&ref_branch.as_ref()[..], llix); self.set_node(llix, nibble as usize, Some(h)); } } @@ -232,11 +219,11 @@ impl CacheAccum v.as_ref().map(|v| v.as_ref()), ); self.reset_depth(branch_d); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], branch_d); + let pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], branch_d); let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); let encoded = T::Codec::extension_node(nib, nkeyix.1, branch_hash); let h = callback.process(pr.left(), encoded, is_root); @@ -260,14 +247,14 @@ impl CacheAccum // encode branch let v = self.0[last].1.take(); let nkeyix = nkey.unwrap_or((0, 0)); - let pr = NibbleSlice::::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], nkeyix.0); let encoded = T::Codec::branch_node_nibbled( pr.right_range_iter(nkeyix.1), nkeyix.1, self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref())); self.reset_depth(branch_d); let ext_length = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); - let pr = NibbleSlice::::new_offset( + let pr = NibbleSlice::new_offset( &key_branch.as_ref()[..], branch_d - ext_length, ); @@ -299,10 +286,10 @@ pub fn trie_visit(input: I, callback: &mut F) let mut single = true; for (k, v) in iter_input { single = false; - let common_depth = T::Nibble::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); + let common_depth = nibble_ops::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); // 0 is a reserved value : could use option let depth_item = common_depth; - if common_depth == previous_value.0.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE { + if common_depth == previous_value.0.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE { // the new key include the previous one : branch value case // just stored value at branch depth depth_queue.set_cache_value(common_depth, Some(previous_value.1)); @@ -323,11 +310,11 @@ pub fn trie_visit(input: I, callback: &mut F) if single { // one single element corner case let (k2, v2) = previous_value; - let nkey = NibbleSlice::::new_offset(&k2.as_ref()[..], last_depth); + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], last_depth); let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); - let pr = NibbleSlice::::new_offset( + let pr = NibbleSlice::new_offset( &k2.as_ref()[..], - k2.as_ref().len() * T::Nibble::NIBBLE_PER_BYTE - nkey.len(), + k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); callback.process(pr.left(), encoded, true); } else { @@ -348,7 +335,7 @@ pub trait ProcessEncodedNode { /// /// Note that the returned value can change depending on implementation, /// but usually it should be the Hash of encoded node. - /// This is not something direcly related to encoding but is here for + /// This is not something direcly related to encoding but is here for /// optimisation purpose (builder hash_db does return this value). fn process(&mut self, prefix: Prefix, encoded_node: Vec, is_root: bool) -> ChildReference; } @@ -589,7 +576,6 @@ mod test { compare_implementations_prefixed(data.clone()); compare_implementations_no_extension(data.clone()); compare_implementations_no_extension_prefixed(data.clone()); - compare_implementations_no_extension_q(data.clone()); } fn compare_implementations_prefixed(data: Vec<(Vec, Vec)>) { @@ -607,11 +593,6 @@ mod test { let hashdb = MemoryDB::, DBValue>::default(); reference_trie::compare_implementations_no_extension(data, memdb, hashdb); } - fn compare_implementations_no_extension_q(data: Vec<(Vec, Vec)>) { - let memdb = MemoryDB::<_, HashKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations_no_extension_q(data, memdb, hashdb); - } fn compare_implementations_no_extension_prefixed(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); let hashdb = MemoryDB::, DBValue>::default(); diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 5748d705..d7cfb60d 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -94,8 +94,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibble::{NibbleSlice, NibbleOps, NibbleHalf, NibbleQuarter, - ChildSliceIndex}; +pub use self::nibble::{NibbleSlice, nibble_ops}; pub use node_codec::{NodeCodec, Partial, BitMap}; pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; @@ -407,12 +406,7 @@ pub trait TrieLayout { /// Hasher to use for this trie. type Hash: Hasher; /// Codec to use (needs to match hasher and nibble ops). - type Codec: NodeCodec; - /// Trie nibble constants. It defines trie radix. - type Nibble: NibbleOps; - /// Technical trait for cache, it should match the radix - /// of `NibbleOps`. - type Cache: CacheBuilder<::Out>; + type Codec: NodeCodec; } /// This traits associates a trie definition with prefered methods. @@ -475,5 +469,5 @@ pub trait TrieConfiguration: Sized + TrieLayout { pub type TrieHash = <::Hash as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = < - ::Codec as NodeCodec<::Hash, ::Nibble> + ::Codec as NodeCodec<::Hash> >::Error; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 11143ab1..5eb0ec06 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -18,7 +18,7 @@ use hash_db::HashDBRef; use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash, ChildSliceIndex}; +use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -42,7 +42,7 @@ where /// function to decode or copy. pub fn look_up( mut self, - key: NibbleSlice, + key: NibbleSlice, ) -> Result, TrieHash, CError> { let mut partial = key; let mut hash = self.hash; @@ -88,7 +88,7 @@ where } Node::Branch(children, value) => match partial.is_empty() { true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children.0.slice_at(partial.at(0) as usize, children.1) { + false => match children[partial.at(0) as usize] { Some(x) => { node_data = x; partial = partial.mid(1); @@ -104,8 +104,7 @@ where match partial.len() == slice.len() { true => return Ok(value.map(move |val| self.query.decode(val))), - false => match children.0 - .slice_at(partial.at(slice.len()) as usize, children.1) { + false => match children[partial.at(slice.len()) as usize] { Some(x) => { node_data = x; partial = partial.mid(slice.len() + 1); diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 0048d3f0..d16a63dd 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -16,161 +16,126 @@ mod nibblevec; mod nibbleslice; -use ::core_::cmp::*; -use ::core_::marker::PhantomData; use elastic_array::ElasticArray36; use crate::node::NodeKey; -use super::MaybeDebug; -// Work-around absence of constant function for math pow. -const TWO_EXP: [usize; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256]; +/// Utility methods to work on radix 16 nibble. +pub mod nibble_ops { + + use super::*; -/// This trait contain Trie nibble specific definitions. -/// This trait is mostly a collection of associated constant and some generic -/// methods. -/// Generic methods should not need redefinition except for optimization -/// purpose. -pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy + MaybeDebug { - /// See [`ByteLayout`]. - const LAYOUT : ByteLayout; /// Single nibble length in bit. - const BIT_PER_NIBBLE : usize = TWO_EXP[Self::LAYOUT as usize]; + pub const BIT_PER_NIBBLE : usize = 4; /// Number of nibble per byte. - const NIBBLE_PER_BYTE : usize = 8 / Self::BIT_PER_NIBBLE; + pub const NIBBLE_PER_BYTE : usize = 2; /// Number of child for a branch (trie radix). - const NIBBLE_LENGTH : usize = TWO_EXP[Self::BIT_PER_NIBBLE]; - /// Padding bitmasks, internally use for working on padding byte. - /// Length of this array is `Self::BIT_PER_NIBBLE`. - /// The first element of each pair is a bit mask to apply, - /// the second element is a right shift to apply in some case. - /// const PADDING_BITMASK: &'static [(u8, usize)] = &[ - /// Similar to following const function. - /// ```rust - /// const BIT_PER_NIBBLE: usize = 4; - /// const fn padding_bitmask(ix: usize) -> (u8, usize) { - /// //assert!(ix < 8 / BIT_PER_NIBBLE); - /// let offset = BIT_PER_NIBBLE * ix; - /// (1u8 >> offset, 8 - offset) - /// } - /// ``` - const PADDING_BITMASK: &'static [(u8, usize)]; - /// Last nibble index as u8, a convenience constant for iteration on all nibble. - const LAST_NIBBLE_INDEX: u8 = (Self::NIBBLE_PER_BYTE - 1) as u8; - - /// Buffer type for slice index store (we do not include - /// directly slice in it to avoid lifetime in - /// trait - type ChildSliceIndex: ChildSliceIndex; + pub const NIBBLE_LENGTH : usize = 16; + /// Nibble (half a byte). + pub const PADDING_BITMASK: u8 = 0x0F; + /// Size of header. + pub const CONTENT_HEADER_SIZE: u8 = 1; - /// Mask a byte from a `ix` > 0 (ix being content). - /// Result is a byte containing `ix` nibble of left aligned content and padded with 0. + /// Mask a byte, keeping left nibble. #[inline(always)] - fn pad_left(ix: u8, b: u8) -> u8 { - debug_assert!(ix > 0); - b & !Self::PADDING_BITMASK[ix as usize].0 + pub fn pad_left(b: u8) -> u8 { + b & !PADDING_BITMASK } - /// Mask a byte from a ix > 0 (ix being content). - /// Result is a byte containing `ix` nibble of right aligned content and padded with 0. + /// Mask a byte, keeping right byte. #[inline(always)] - fn pad_right(ix: u8, b: u8) -> u8 { - if ix > 0 { - b & Self::PADDING_BITMASK[Self::NIBBLE_PER_BYTE - ix as usize].0 - } else { - b - } + pub fn pad_right(b: u8) -> u8 { + b & PADDING_BITMASK } /// Get u8 nibble value at a given index of a byte. #[inline(always)] - fn at_left(ix: u8, b: u8) -> u8 { - (b & Self::PADDING_BITMASK[ix as usize].0) - >> Self::PADDING_BITMASK[ix as usize].1 + pub fn at_left(ix: u8, b: u8) -> u8 { + if ix == 1 { + b & PADDING_BITMASK + } else { + b >> BIT_PER_NIBBLE + } } /// Get u8 nibble value at a given index in a left aligned array. #[inline(always)] - fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { - Self::at_left( - (ix % Self::NIBBLE_PER_BYTE) as u8, - v1[ix / Self::NIBBLE_PER_BYTE] + pub fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { + at_left( + (ix % NIBBLE_PER_BYTE) as u8, + v1[ix / NIBBLE_PER_BYTE] ) } /// Get u8 nibble value at a given index in a `NibbleSlice`. #[inline(always)] - fn at(s: &NibbleSlice, i: usize) -> u8 { - let ix = (s.offset + i) / Self::NIBBLE_PER_BYTE; - let pad = (s.offset + i) % Self::NIBBLE_PER_BYTE; - Self::at_left(pad as u8, s.data[ix]) + pub fn at(s: &NibbleSlice, i: usize) -> u8 { + let ix = (s.offset + i) / NIBBLE_PER_BYTE; + let pad = (s.offset + i) % NIBBLE_PER_BYTE; + at_left(pad as u8, s.data[ix]) } /// Push u8 nibble value at a given index into an existing byte. #[inline(always)] - fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { - into | (v << Self::PADDING_BITMASK[ix as usize].1) + pub fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { + into | if ix == 1 { + v + } else { + v << BIT_PER_NIBBLE + } } #[inline] /// Calculate the number of needed padding a array of nibble length `i`. - fn number_padding(i: usize) -> usize { - (Self::NIBBLE_PER_BYTE - (i % Self::NIBBLE_PER_BYTE)) % Self::NIBBLE_PER_BYTE + pub fn number_padding(i: usize) -> usize { + i % NIBBLE_PER_BYTE } - /// Calculate the array nibble shifts needed - /// for alignment a given unaligned padding (pad != 0). - #[inline(always)] - fn split_shifts(pad: usize) -> (usize, usize) { - debug_assert!(pad > 0); - let s1 = Self::PADDING_BITMASK[pad - 1].1; - let s2 = 8 - s1; - (s1, s2) - } + /// The nibble shifts needed + /// for alignment a given unaligned padding. + /// We use two value for one is a left shift and + /// the other is a right shift. + pub const SPLIT_SHIFTS: (usize, usize) = (4, 4); /// Count the biggest common depth between two left aligned packed nibble slice. - fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { + pub fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { // sorted assertion preventing out of bound for a in 0..v1.len() { if v1[a] == v2[a] { } else { - return a * Self::NIBBLE_PER_BYTE + Self::left_common(v1[a], v2[a]); + return a * NIBBLE_PER_BYTE + left_common(v1[a], v2[a]); } } - return v1.len() * Self::NIBBLE_PER_BYTE; + return v1.len() * NIBBLE_PER_BYTE; } /// Calculate the number of common nibble between two left aligned bytes. #[inline(always)] - fn left_common(a: u8, b: u8) -> usize { - let mut i = 0; - while i < Self::NIBBLE_PER_BYTE { - if (a >> Self::PADDING_BITMASK[i].1) - != (b >> Self::PADDING_BITMASK[i].1) { - break; - } - i += 1; + pub fn left_common(a: u8, b: u8) -> usize { + if a == b { + 2 + } else if pad_left(a) == pad_left(b) { + 1 + } else { + 0 } - return i; } /// Shifts right aligned key to add a given left offset. /// Resulting in possibly padding at both left and right /// (example usage when combining two keys). - fn shift_key(key: &mut NodeKey, ofset: usize) -> bool { + pub fn shift_key(key: &mut NodeKey, offset: usize) -> bool { let old_offset = key.0; - key.0 = ofset; - if old_offset > ofset { + key.0 = offset; + if old_offset > offset { // shift left - let shift = old_offset - ofset; - let (s1, s2) = Self::split_shifts(shift); + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; let kl = key.1.len(); (0..kl - 1).for_each(|i| key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1); key.1[kl - 1] = key.1[kl - 1] << s2; true - } else if old_offset < ofset { + } else if old_offset < offset { // shift right - let shift = ofset - old_offset; - let (s1, s2) = Self::split_shifts(shift); + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; key.1.push(0); (1..key.1.len()).rev().for_each(|i| key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2); key.1[0] = key.1[0] >> s2; @@ -182,57 +147,14 @@ pub trait NibbleOps: Default + Clone + PartialEq + Eq + PartialOrd + Ord + Copy } -/// Radix 16 `NibbleOps` definition. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] -pub struct NibbleHalf; - -/// Ordered enumeration of the different possible number of nibble in -/// a byte. -#[repr(usize)] -pub enum ByteLayout { - /// Radix 2 trie. Eight nibble per byte. - Bit = 0, // 1, 8, 2 - /// Radix 4 trie. Four nibble per byte. - Quarter = 1, // 2, 4, 4 - /// Radix 16 trie. Two nibble per byte. - Half = 2, // 4, 2, 16 - /// Radix 256 trie. One nibble per byte. - Full = 3, // 8, 1, 256 -} - -impl NibbleOps for NibbleHalf { - const LAYOUT: ByteLayout = ByteLayout::Half; - const PADDING_BITMASK: &'static [(u8, usize)] = &[(0xFF, 4), (0x0F, 0)]; - type ChildSliceIndex = ChildSliceIndex16; -} - -/// Radix 4 `NibbleOps` definition. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] -pub struct NibbleQuarter; - -// new_padded_end merged -impl NibbleOps for NibbleQuarter { - const LAYOUT: ByteLayout = ByteLayout::Quarter; - const PADDING_BITMASK: &'static [(u8, usize)] = &[ - (0b1111_1111, 6), - (0b0011_1111, 4), - (0b0000_1111, 2), - (0b0000_0011, 0), - ]; - type ChildSliceIndex = ChildSliceIndex4; -} - /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. /// Nibbles are always left aligned, so making a `NibbleVec` from /// a `NibbleSlice` can get costy. #[cfg_attr(feature = "std", derive(Debug))] #[derive(Clone, PartialEq, Eq)] -pub struct NibbleVec { +pub struct NibbleVec { inner: ElasticArray36, len: usize, - marker: PhantomData, } /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. @@ -258,90 +180,14 @@ pub struct NibbleVec { /// } /// ``` #[derive(Copy, Clone)] -pub struct NibbleSlice<'a, N: NibbleOps> { +pub struct NibbleSlice<'a> { data: &'a [u8], offset: usize, - marker: PhantomData, } /// Iterator type for a nibble slice. -pub struct NibbleSliceIterator<'a, N: NibbleOps> { - p: &'a NibbleSlice<'a, N>, +pub struct NibbleSliceIterator<'a> { + p: &'a NibbleSlice<'a>, i: usize, } -/// Technical trait only to access child slice from an encoded -/// representation of a branch. -/// This is use instead of `&[&[u8]]` to allow associated type -/// with a constant lenght. -pub trait ChildSliceIndex: AsRef<[usize]> - + AsMut<[usize]> + Default + Eq + PartialEq + crate::MaybeDebug - + Clone { - - /// Constant length for the number of children. - const NIBBLE_LENGTH : usize; - /// Constant size of header - /// Should only be use for inner implementation. - const CONTENT_HEADER_SIZE: usize; - - /// Access a children slice at a given index. - fn slice_at<'a>(&self, ix: usize, data: &'a [u8]) -> Option<&'a[u8]> { - let b = (self.as_ref().get(ix), self.as_ref().get(ix + 1)); - if let (Some(s), Some(e)) = b { - let s = s + Self::CONTENT_HEADER_SIZE; - if s < *e { - Some(&data[s..*e]) - } else { - None - } - } else { - None - } - } - /// Iterator over the children slice. - fn iter<'a>(&'a self, data: &'a [u8]) -> IterChildSliceIndex<'a, Self> { - IterChildSliceIndex(self, 0, data) - } -} - -/// Iterator over `ChildSliceIndex` trait. -pub struct IterChildSliceIndex<'a, CS>(&'a CS, usize, &'a[u8]); - -impl<'a, CS: ChildSliceIndex> Iterator for IterChildSliceIndex<'a, CS> { - type Item = Option<&'a[u8]>; - fn next(&mut self) -> Option { - if self.1 == CS::NIBBLE_LENGTH { - return None; - } - self.1 += 1; - Some(self.0.slice_at(self.1 - 1, self.2)) - } -} - -macro_rules! child_slice_index { - ($me: ident, $size: expr, $pre: expr) => { - #[cfg_attr(feature = "std", derive(Debug))] - #[derive(Default, Eq, PartialEq, Clone)] - /// Child slice indexes for radix $size. - pub struct $me([usize; $size + 1]); - - impl AsRef<[usize]> for $me { - fn as_ref(&self) -> &[usize] { - &self.0[..] - } - } - - impl AsMut<[usize]> for $me { - fn as_mut(&mut self) -> &mut [usize] { - &mut self.0[..] - } - } - - impl ChildSliceIndex for $me { - const CONTENT_HEADER_SIZE: usize = $pre; - const NIBBLE_LENGTH: usize = $size; - } - } -} -child_slice_index!(ChildSliceIndex16, 16, 1); -child_slice_index!(ChildSliceIndex4, 4, 1); diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 52279a51..e544cdf5 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -16,14 +16,13 @@ use ::core_::cmp::*; use ::core_::fmt; -use ::core_::marker::PhantomData; -use super::{NibbleOps, NibbleSlice, NibbleSliceIterator}; +use super::{nibble_ops, NibbleSlice, NibbleSliceIterator}; use elastic_array::ElasticArray36; use node::NodeKey; use node_codec::Partial; use hash_db::Prefix; -impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { +impl<'a> Iterator for NibbleSliceIterator<'a> { type Item = u8; fn next(&mut self) -> Option { self.i += 1; @@ -34,7 +33,7 @@ impl<'a, N: NibbleOps> Iterator for NibbleSliceIterator<'a, N> { } } -impl<'a, N: NibbleOps> NibbleSlice<'a, N> { +impl<'a> NibbleSlice<'a> { /// Create a new nibble slice with the given byte-slice. pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0) } @@ -47,24 +46,23 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { NibbleSlice { data, offset, - marker: PhantomData, } } /// Get an iterator for the series of nibbles. - pub fn iter(&'a self) -> NibbleSliceIterator<'a, N> { + pub fn iter(&'a self) -> NibbleSliceIterator<'a> { NibbleSliceIterator { p: self, i: 0 } } /// Get nibble slice from a `NodeKey`. - pub fn from_stored(i: &NodeKey) -> NibbleSlice { - NibbleSlice::::new_offset(&i.1[..], i.0) + pub fn from_stored(i: &NodeKey) -> NibbleSlice { + NibbleSlice::new_offset(&i.1[..], i.0) } /// Helper function to create a owned `NodeKey` from this `NibbleSlice`. pub fn to_stored(&self) -> NodeKey { - let split = self.offset / N::NIBBLE_PER_BYTE; - let offset = self.offset % N::NIBBLE_PER_BYTE; + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; (offset, self.data[split..].into()) } @@ -74,23 +72,23 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// original padding). pub fn to_stored_range(&self, nb: usize) -> NodeKey { if nb >= self.len() { return self.to_stored() } - if (self.offset + nb) % N::NIBBLE_PER_BYTE == 0 { + if (self.offset + nb) % nibble_ops::NIBBLE_PER_BYTE == 0 { // aligned - let start = self.offset / N::NIBBLE_PER_BYTE; - let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE; ( - self.offset % N::NIBBLE_PER_BYTE, + self.offset % nibble_ops::NIBBLE_PER_BYTE, ElasticArray36::from_slice(&self.data[start..end]), ) } else { // unaligned - let start = self.offset / N::NIBBLE_PER_BYTE; - let end = (self.offset + nb) / N::NIBBLE_PER_BYTE; + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE; let ea = ElasticArray36::from_slice(&self.data[start..=end]); - let ea_offset = self.offset % N::NIBBLE_PER_BYTE; - let n_offset = N::number_padding(nb); + let ea_offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; + let n_offset = nibble_ops::number_padding(nb); let mut result = (ea_offset, ea); - N::shift_key(&mut result, n_offset); + nibble_ops::shift_key(&mut result, n_offset); result.1.pop(); result } @@ -101,20 +99,19 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Get the length (in nibbles, naturally) of this slice. #[inline] - pub fn len(&self) -> usize { self.data.len() * N::NIBBLE_PER_BYTE - self.offset } + pub fn len(&self) -> usize { self.data.len() * nibble_ops::NIBBLE_PER_BYTE - self.offset } /// Get the nibble at position `i`. #[inline(always)] pub fn at(&self, i: usize) -> u8 { - N::at(&self, i) + nibble_ops::at(&self, i) } /// Return object which represents a view on to this slice (further) offset by `i` nibbles. - pub fn mid(&self, i: usize) -> NibbleSlice<'a, N> { + pub fn mid(&self, i: usize) -> NibbleSlice<'a> { NibbleSlice { data: self.data, offset: self.offset + i, - marker: PhantomData, } } @@ -125,11 +122,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { } /// Move back to a previously valid fix offset position. - pub fn back(&self, i: usize) -> NibbleSlice<'a, N> { + pub fn back(&self, i: usize) -> NibbleSlice<'a> { NibbleSlice { data: self.data, offset: i, - marker: PhantomData, } } @@ -150,10 +146,10 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Return `Partial` representation of this slice: /// first encoded byte and following slice. pub fn right(&'a self) -> Partial { - let split = self.offset / N::NIBBLE_PER_BYTE; - let nb = (self.len() % N::NIBBLE_PER_BYTE) as u8; + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let nb = (self.len() % nibble_ops::NIBBLE_PER_BYTE) as u8; if nb > 0 { - ((nb, N::pad_right(nb, self.data[split])), &self.data[split + 1 ..]) + ((nb, nibble_ops::pad_right(self.data[split])), &self.data[split + 1 ..]) } else { ((0, 0), &self.data[split..]) } @@ -166,7 +162,7 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { ::core_::iter::from_fn(move || { if first.0 > 0 { first.0 = 0; - Some(N::pad_right(first.0, first.1)) + Some(nibble_ops::pad_right(first.1)) } else { if ix < sl.len() { ix += 1; @@ -181,15 +177,15 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// Return `Partial` bytes iterator over a range of byte.. /// Warning can be slow when unaligned (similar to `to_stored_range`). pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { - let mut nib_res = to % N::NIBBLE_PER_BYTE; - let aligned_i = (self.offset + to) % N::NIBBLE_PER_BYTE; + let mut nib_res = to % nibble_ops::NIBBLE_PER_BYTE; + let aligned_i = (self.offset + to) % nibble_ops::NIBBLE_PER_BYTE; let aligned = aligned_i == 0; - let mut ix = self.offset / N::NIBBLE_PER_BYTE; - let ix_lim = (self.offset + to) / N::NIBBLE_PER_BYTE; + let mut ix = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let ix_lim = (self.offset + to) / nibble_ops::NIBBLE_PER_BYTE; ::core_::iter::from_fn( move || { if aligned { if nib_res > 0 { - let v = N::pad_right(nib_res as u8, self.data[ix]); + let v = nibble_ops::pad_right(self.data[ix]); nib_res = 0; ix += 1; Some(v) @@ -200,11 +196,11 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { None } } else { - let (s1, s2) = N::split_shifts(aligned_i); + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; // unaligned if nib_res > 0 { let v = self.data[ix] >> s1; - let v = N::pad_right(nib_res as u8, v); + let v = nibble_ops::pad_right(v); nib_res = 0; Some(v) } else if ix < ix_lim { @@ -223,43 +219,43 @@ impl<'a, N: NibbleOps> NibbleSlice<'a, N> { /// originates from a full key it will be the `Prefix of /// the node`. pub fn left(&'a self) -> Prefix { - let split = self.offset / N::NIBBLE_PER_BYTE; - let ix = (self.offset % N::NIBBLE_PER_BYTE) as u8; + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let ix = (self.offset % nibble_ops::NIBBLE_PER_BYTE) as u8; if ix == 0 { - (&self.data[..split], (0, 0)) + (&self.data[..split], None) } else { - (&self.data[..split], (ix, N::pad_left(ix, self.data[split]))) + (&self.data[..split], Some(nibble_ops::pad_left(self.data[split]))) } } /// Owned version of a `Prefix` from a `left` method call. - pub fn left_owned(&'a self) -> (ElasticArray36, (u8, u8)) { + pub fn left_owned(&'a self) -> (ElasticArray36, Option) { let (a, b) = self.left(); (a.into(), b) } } -impl<'a, N: NibbleOps> Into for NibbleSlice<'a, N> { +impl<'a> Into for NibbleSlice<'a> { fn into(self) -> NodeKey { (self.offset, self.data.into()) } } -impl<'a, N: NibbleOps> PartialEq for NibbleSlice<'a, N> { +impl<'a> PartialEq for NibbleSlice<'a> { fn eq(&self, them: &Self) -> bool { self.len() == them.len() && self.starts_with(them) } } -impl<'a, N: NibbleOps> Eq for NibbleSlice<'a, N> { } +impl<'a> Eq for NibbleSlice<'a> { } -impl<'a, N: NibbleOps> PartialOrd for NibbleSlice<'a, N> { +impl<'a> PartialOrd for NibbleSlice<'a> { fn partial_cmp(&self, them: &Self) -> Option { Some(self.cmp(them)) } } -impl<'a, N: NibbleOps> Ord for NibbleSlice<'a, N> { +impl<'a> Ord for NibbleSlice<'a> { fn cmp(&self, them: &Self) -> Ordering { let s = min(self.len(), them.len()); let mut i = 0usize; @@ -275,7 +271,7 @@ impl<'a, N: NibbleOps> Ord for NibbleSlice<'a, N> { } #[cfg(feature = "std")] -impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { +impl<'a> fmt::Debug for NibbleSlice<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in 0..self.len() { match i { @@ -290,26 +286,19 @@ impl<'a, N: NibbleOps> fmt::Debug for NibbleSlice<'a, N> { #[cfg(test)] mod tests { use crate::nibble::NibbleSlice; - use crate::nibble::NibbleHalf; - use crate::nibble::NibbleQuarter; - use crate::nibble::NibbleOps; use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; #[test] fn basics() { - basics_inner::(); - } - - fn basics_inner() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); assert_eq!(n.len(), 6); assert!(!n.is_empty()); - let n = NibbleSlice::::new_offset(D, 6); + let n = NibbleSlice::new_offset(D, 6); assert!(n.is_empty()); - let n = NibbleSlice::::new_offset(D, 3); + let n = NibbleSlice::new_offset(D, 3); assert_eq!(n.len(), 3); for i in 0..3 { assert_eq!(n.at(i), i as u8 + 3); @@ -318,11 +307,7 @@ mod tests { #[test] fn iterator() { - iterator_inner::(); - } - - fn iterator_inner() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); let mut nibbles: Vec = vec![]; nibbles.extend(n.iter()); assert_eq!(nibbles, (0u8..6).collect::>()) @@ -330,11 +315,7 @@ mod tests { #[test] fn mid() { - mid_inner::(); - } - - fn mid_inner() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); let m = n.mid(2); for i in 0..4 { assert_eq!(m.at(i), i as u8 + 2); @@ -347,7 +328,7 @@ mod tests { #[test] fn encoded_pre() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); assert_eq!(n.to_stored(), (0, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(1).to_stored(), (1, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); assert_eq!(n.mid(2).to_stored(), (0, ElasticArray36::from_slice(&[0x23, 0x45]))); @@ -356,7 +337,7 @@ mod tests { #[test] fn from_encoded_pre() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); let stored: ElasticArray36 = [0x01, 0x23, 0x45][..].into(); assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); @@ -364,8 +345,7 @@ mod tests { #[test] fn range_iter() { - let n = NibbleSlice::::new(D); - let n2 = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); for i in [ vec![], vec![0x00], @@ -375,8 +355,7 @@ mod tests { vec![0x00, 0x12, 0x34], vec![0x01, 0x23, 0x45], ].iter().enumerate() { - range_iter_test::(n, i.0, None, &i.1[..]); - range_iter_test::(n2, i.0 * 2, None, &i.1[..]); + range_iter_test(n, i.0, None, &i.1[..]); } for i in [ vec![], @@ -386,8 +365,7 @@ mod tests { vec![0x12, 0x34], vec![0x01, 0x23, 0x45], ].iter().enumerate() { - range_iter_test::(n, i.0, Some(1), &i.1[..]); - range_iter_test::(n2, i.0 * 2, Some(2), &i.1[..]); + range_iter_test(n, i.0, Some(1), &i.1[..]); } for i in [ vec![], @@ -396,8 +374,7 @@ mod tests { vec![0x02, 0x34], vec![0x23, 0x45], ].iter().enumerate() { - range_iter_test::(n, i.0, Some(2), &i.1[..]); - range_iter_test::(n2, i.0 * 2, Some(4), &i.1[..]); + range_iter_test(n, i.0, Some(2), &i.1[..]); } for i in [ vec![], @@ -405,12 +382,11 @@ mod tests { vec![0x34], vec![0x03, 0x45], ].iter().enumerate() { - range_iter_test::(n, i.0, Some(3), &i.1[..]); - range_iter_test::(n2, i.0 * 2, Some(6), &i.1[..]); + range_iter_test(n, i.0, Some(3), &i.1[..]); } } - fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { + fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { let n = if let Some(i) = mid { n.mid(i) } else { n }; @@ -419,10 +395,7 @@ mod tests { #[test] fn shared() { - shared_inner::(); - } - fn shared_inner() { - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; let m = NibbleSlice::new(other); @@ -438,11 +411,8 @@ mod tests { #[test] fn compare() { - compare_inner::(); - } - fn compare_inner() { let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; - let n = NibbleSlice::::new(D); + let n = NibbleSlice::new(D); let m = NibbleSlice::new(other); assert!(n != m); diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index cf1abbd6..fc77ee22 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -15,25 +15,23 @@ //! An owning, nibble-oriented byte vector. use elastic_array::ElasticArray36; use nibble::NibbleSlice; -use nibble::NibbleOps; +use nibble::nibble_ops; use hash_db::Prefix; use node_codec::Partial; -use ::core_::marker::PhantomData; use super::NibbleVec; -impl Default for NibbleVec { +impl Default for NibbleVec { fn default() -> Self { - NibbleVec::::new() + NibbleVec::new() } } -impl NibbleVec { +impl NibbleVec { /// Make a new `NibbleVec`. pub fn new() -> Self { NibbleVec { inner: ElasticArray36::new(), len: 0, - marker: PhantomData, } } @@ -47,21 +45,21 @@ impl NibbleVec { /// Try to get the nibble at the given offset. #[inline] pub fn at(&self, idx: usize) -> u8 { - let ix = idx / N::NIBBLE_PER_BYTE; - let pad = idx % N::NIBBLE_PER_BYTE; - N::at_left(pad as u8, self.inner[ix]) + let ix = idx / nibble_ops::NIBBLE_PER_BYTE; + let pad = idx % nibble_ops::NIBBLE_PER_BYTE; + nibble_ops::at_left(pad as u8, self.inner[ix]) } /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. pub fn push(&mut self, nibble: u8) { - let i = self.len % N::NIBBLE_PER_BYTE; + let i = self.len % nibble_ops::NIBBLE_PER_BYTE; if i == 0 { - self.inner.push(N::push_at_left(0, nibble, 0)); + self.inner.push(nibble_ops::push_at_left(0, nibble, 0)); } else { let output = self.inner.last_mut() .expect("len != 0 since len % 2 != 0; inner has a last element; qed"); - *output = N::push_at_left(i as u8, nibble, *output); + *output = nibble_ops::push_at_left(i as u8, nibble, *output); } self.len += 1; } @@ -73,11 +71,11 @@ impl NibbleVec { } let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); self.len -= 1; - let i_new = self.len % N::NIBBLE_PER_BYTE; + let i_new = self.len % nibble_ops::NIBBLE_PER_BYTE; if i_new != 0 { - self.inner.push(N::pad_left(i_new as u8, byte)); + self.inner.push(nibble_ops::pad_left(byte)); } - Some(N::at_left(i_new as u8, byte)) + Some(nibble_ops::at_left(i_new as u8, byte)) } /// Remove then n last nibbles in a faster way than popping n times. @@ -88,39 +86,39 @@ impl NibbleVec { return; } let end = self.len - n; - let end_index = end / N::NIBBLE_PER_BYTE - + if end % N::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; + let end_index = end / nibble_ops::NIBBLE_PER_BYTE + + if end % nibble_ops::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; (end_index..self.inner.len()).for_each(|_| { self.inner.pop(); }); self.len = end; - let pos = self.len % N::NIBBLE_PER_BYTE; + let pos = self.len % nibble_ops::NIBBLE_PER_BYTE; if pos != 0 { let kl = self.inner.len() - 1; - self.inner[kl] = N::pad_left(pos as u8, self.inner[kl]); + self.inner[kl] = nibble_ops::pad_left(self.inner[kl]); } } /// Get `Prefix` representation of this `NibbleVec`. pub fn as_prefix(&self) -> Prefix { - let split = self.len / N::NIBBLE_PER_BYTE; - let pos = (self.len % N::NIBBLE_PER_BYTE) as u8; + let split = self.len / nibble_ops::NIBBLE_PER_BYTE; + let pos = (self.len % nibble_ops::NIBBLE_PER_BYTE) as u8; if pos == 0 { - (&self.inner[..split], (0, 0)) + (&self.inner[..split], None) } else { - (&self.inner[..split], (pos, N::pad_left(pos, self.inner[split]))) + (&self.inner[..split], Some(nibble_ops::pad_left(self.inner[split]))) } } /// Append another `NibbleVec`. Can be slow (alignement of second vec). - pub fn append(&mut self, v: &NibbleVec) { + pub fn append(&mut self, v: &NibbleVec) { if v.len == 0 { return; } let final_len = self.len + v.len; - let offset = self.len % N::NIBBLE_PER_BYTE; - let final_offset = final_len % N::NIBBLE_PER_BYTE; - let last_index = self.len / N::NIBBLE_PER_BYTE; + let offset = self.len % nibble_ops::NIBBLE_PER_BYTE; + let final_offset = final_len % nibble_ops::NIBBLE_PER_BYTE; + let last_index = self.len / nibble_ops::NIBBLE_PER_BYTE; if offset > 0 { - let (s1, s2) = N::split_shifts(offset); - self.inner[last_index] = N::pad_left(offset as u8, self.inner[last_index]) + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + self.inner[last_index] = nibble_ops::pad_left(self.inner[last_index]) | (v.inner[0] >> s2); (0..v.inner.len() - 1) .for_each(|i| self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); @@ -134,28 +132,24 @@ impl NibbleVec { } /// Append a `Partial`. Can be slow (alignement of partial). - pub fn append_partial(&mut self, (o_n, sl): Partial) { - for i in (1..=o_n.0).rev() { - let ix = N::NIBBLE_PER_BYTE - i as usize; - self.push(N::at_left(ix as u8, o_n.1)); + pub fn append_partial(&mut self, (start_byte, sl): Partial) { + if start_byte.0 == 1 { + self.push(nibble_ops::at_left(1, start_byte.1)); } - let pad = self.inner.len() * N::NIBBLE_PER_BYTE - self.len; + let pad = self.inner.len() * nibble_ops::NIBBLE_PER_BYTE - self.len; if pad == 0 { self.inner.append_slice(&sl[..]); } else { let kend = self.inner.len() - 1; if sl.len() > 0 { - self.inner[kend] = N::pad_left( - (N::NIBBLE_PER_BYTE - pad) as u8, - self.inner[kend], - ); - let (s1, s2) = N::split_shifts(pad); + self.inner[kend] = nibble_ops::pad_left(self.inner[kend]); + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; self.inner[kend] |= sl[0] >> s1; (0..sl.len() - 1).for_each(|i| self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); self.inner.push(sl[sl.len() - 1] << s2); } } - self.len += sl.len() * N::NIBBLE_PER_BYTE; + self.len += sl.len() * nibble_ops::NIBBLE_PER_BYTE; } /// Utility function for chaining two optional appending @@ -163,7 +157,7 @@ impl NibbleVec { /// Can be slow. pub(crate) fn append_optional_slice_and_nibble( &mut self, - o_slice: Option<&NibbleSlice>, + o_slice: Option<&NibbleSlice>, o_index: Option, ) -> usize { let mut res = 0; @@ -182,7 +176,7 @@ impl NibbleVec { /// Can be slow. pub(crate) fn clone_append_optional_slice_and_nibble( &self, - o_slice: Option<&NibbleSlice>, + o_slice: Option<&NibbleSlice>, o_index: Option, ) -> Self { let mut p = self.clone(); @@ -202,8 +196,8 @@ impl NibbleVec { } /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if there is no padding. - pub fn as_nibbleslice(&self) -> Option> { - if self.len % N::NIBBLE_PER_BYTE == 0 { + pub fn as_nibbleslice(&self) -> Option { + if self.len % nibble_ops::NIBBLE_PER_BYTE == 0 { Some(NibbleSlice::new(self.inner())) } else { None @@ -212,8 +206,8 @@ impl NibbleVec { } -impl<'a, N: NibbleOps> From> for NibbleVec { - fn from(s: NibbleSlice<'a, N>) -> Self { +impl<'a> From> for NibbleVec { + fn from(s: NibbleSlice<'a>) -> Self { let mut v = NibbleVec::new(); for i in 0..s.len() { v.push(s.at(i)); @@ -225,26 +219,21 @@ impl<'a, N: NibbleOps> From> for NibbleVec { #[cfg(test)] mod tests { use crate::nibble::NibbleVec; - use crate::nibble::{NibbleHalf, NibbleOps, NibbleQuarter}; + use crate::nibble::nibble_ops; #[test] fn push_pop() { - push_pop_inner::(); - push_pop_inner::(); - } - - fn push_pop_inner() { - let mut v = NibbleVec::::new(); + let mut v = NibbleVec::new(); - for i in 0..(N::NIBBLE_PER_BYTE * 3) { - let iu8 = (i % N::NIBBLE_PER_BYTE) as u8; + for i in 0..(nibble_ops::NIBBLE_PER_BYTE * 3) { + let iu8 = (i % nibble_ops::NIBBLE_PER_BYTE) as u8; v.push(iu8); assert_eq!(v.len() - 1, i); assert_eq!(v.at(i), iu8); } - for i in (0..(N::NIBBLE_PER_BYTE * 3)).rev() { - let iu8 = (i % N::NIBBLE_PER_BYTE) as u8; + for i in (0..(nibble_ops::NIBBLE_PER_BYTE * 3)).rev() { + let iu8 = (i % nibble_ops::NIBBLE_PER_BYTE) as u8; let a = v.pop(); assert_eq!(a, Some(iu8)); assert_eq!(v.len(), i); @@ -253,55 +242,15 @@ mod tests { #[test] fn append_partial() { - append_partial_inner::(&[1, 2, 3], &[], ((1, 1), &[0x23])); - append_partial_inner::(&[1, 2, 3], &[1], ((0, 0), &[0x23])); - append_partial_inner::(&[0, 1, 2, 3], &[0], ((1, 1), &[0x23])); - append_partial_inner::(&[1, 0, 2, 0, 3], &[], ((1, 1), &[0x23])); - append_partial_inner::( - &[1, 0, 2, 0, 3, 0, 1, 0, 2], - &[], - ((1, 1), &[0x23, 0x12]), - ); - append_partial_inner::( - &[2, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[], - ((2, 0b1001), &[0x23, 0x12]), - ); - append_partial_inner::( - &[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[], - ((3, 0b111001), &[0x23, 0x12])); - append_partial_inner::( - &[3, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[3], - ((1, 1), &[0x23, 0x12]), - ); - append_partial_inner::( - &[3, 2, 3, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[3, 2, 3], - ((1, 1), &[0x23, 0x12]), - ); - append_partial_inner::( - &[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[3, 2, 3], - ((2, 0b1001), &[0x23, 0x12]), - ); - append_partial_inner::( - &[3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[3, 2], - ((1, 1), &[0x23, 0x12]), - ); - append_partial_inner::( - &[3, 2, 3, 2, 1, 0, 2, 0, 3, 0, 1, 0, 2], - &[3, 2], - ((3, 0b111001), &[0x23, 0x12]), - ); + append_partial_inner(&[1, 2, 3], &[], ((1, 1), &[0x23])); + append_partial_inner(&[1, 2, 3], &[1], ((0, 0), &[0x23])); + append_partial_inner(&[0, 1, 2, 3], &[0], ((1, 1), &[0x23])); } - fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8, u8), &[u8])) { - let mut resv = NibbleVec::::new(); + fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8, u8), &[u8])) { + let mut resv = NibbleVec::new(); res.iter().for_each(|r| resv.push(*r)); - let mut initv = NibbleVec::::new(); + let mut initv = NibbleVec::new(); init.iter().for_each(|r| initv.push(*r)); initv.append_partial(partial); assert_eq!(resv, initv); @@ -310,7 +259,7 @@ mod tests { #[test] fn drop_lasts_test() { let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { - let mut k = NibbleVec::::new(); + let mut k = NibbleVec::new(); for v in a { k.push(*v); } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 5c5488f4..416095e4 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -14,7 +14,7 @@ use elastic_array::ElasticArray36; use nibble::NibbleSlice; -use nibble::{NibbleOps, ChildSliceIndex}; +use nibble::nibble_ops; use nibble::NibbleVec; use super::DBValue; @@ -25,69 +25,56 @@ use alloc::vec::Vec; /// Offset is applied on first byte of array (bytes are right aligned). pub type NodeKey = (usize, ElasticArray36); -/// Alias to branch children slice, it is equivalent to '&[&[u8]]'. -/// Reason for using it is https://github.com/rust-lang/rust/issues/43408. -pub type BranchChildrenSlice<'a, N> = (::ChildSliceIndex, &'a[u8]); - /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] -pub enum Node<'a, N: NibbleOps> { +pub enum Node<'a> { /// Null trie node; could be an empty root or an empty branch entry. Empty, /// Leaf node; has key slice and value. Value may not be empty. - Leaf(NibbleSlice<'a, N>, &'a [u8]), + Leaf(NibbleSlice<'a>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. - Extension(NibbleSlice<'a, N>, &'a [u8]), + Extension(NibbleSlice<'a>, &'a [u8]), /// Branch node; has slice of child nodes (each possibly null) /// and an optional immediate node data. - Branch(BranchChildrenSlice<'a, N>, Option<&'a [u8]>), + Branch([Option<&'a [u8]>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), /// Branch node with support for a nibble (when extension nodes are not used). - NibbledBranch(NibbleSlice<'a, N>, BranchChildrenSlice<'a, N>, Option<&'a [u8]>), + NibbledBranch(NibbleSlice<'a>, [Option<&'a [u8]>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), } /// A Sparse (non mutable) owned vector struct to hold branch keys and value #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq, Clone)] pub struct Branch { - /// All data in branch as a single byte buffer. - /// It contains children slice, then value, then data: Vec, - /// Start of data/value index. - data_index: usize, - /// Start of ubounds, ubounds are the index of every children. - ubounds_index: usize, - /// Size of a child header (a constant value). - child_head: usize, + ubounds: [usize; 18], + has_value: bool, } impl Branch { - fn new( - children_slice: &BranchChildrenSlice, - maybe_value: Option<&[u8]>, - ) -> Self { - let mut data: Vec = children_slice.1.into(); - let data_index = data.len(); - let ubounds_index = data_index + maybe_value.map(|v| { - data.extend_from_slice(v); - v.len() - }).unwrap_or(0); - let mut i = 0; - while let Some(ix) = children_slice.0.as_ref().get(i) { - i += 1; - data.extend_from_slice(&ix.to_ne_bytes()[..]); + fn new(children: [Option<&[u8]>; 16], maybe_value: Option<&[u8]>) -> Self { + let mut data = Vec::with_capacity(children.iter() + .filter_map(|n| n.clone()) + .map(|child| child.len()) + .sum() + ); + let mut ubounds = [0; 18]; + for (maybe_child, ub) in children.iter().zip(ubounds.iter_mut().skip(1)) { + if let Some(child) = maybe_child { + data.extend_from_slice(child); + } + *ub = data.len(); } - Branch { - data, - data_index, - ubounds_index, - child_head: N::ChildSliceIndex::CONTENT_HEADER_SIZE, + if let Some(value) = maybe_value { + data.extend_from_slice(value); + ubounds[17] = data.len(); } + Branch { data, ubounds, has_value: maybe_value.is_some() } } /// Get the node value, if any. pub fn get_value(&self) -> Option<&[u8]> { - if self.has_value() { - Some(&self.data[self.data_index..self.ubounds_index]) + if self.has_value { + Some(&self.data[self.ubounds[16]..self.ubounds[17]]) } else { None } @@ -95,53 +82,37 @@ impl Branch { /// Test if the node has a value. pub fn has_value(&self) -> bool { - self.data_index < self.ubounds_index + self.has_value } - fn index_bound(&self, index: usize) -> Option { - use core_::convert::TryInto; - use core_::mem; - let usize_len = mem::size_of::(); - let s = self.ubounds_index + index * usize_len; - let e = s + usize_len; - if self.data.len() < e { + pub fn index(&self, index: usize) -> Option<&[u8]> { + assert!(index < 16); + if self.ubounds[index] == self.ubounds[index + 1] { None } else { - self.data[s..e].try_into().ok().map(usize::from_ne_bytes) - } - } - - /// Get the children encoded value at index, if any. - pub fn index(&self, index: usize) -> Option<&[u8]> { - let b = (self.index_bound(index), self.index_bound(index + 1)); - if let (Some(s), Some(e)) = b { - let s = s + self.child_head; - if s < e { - return Some(&self.data[s..e]) - } + Some(&self.data[self.ubounds[index]..self.ubounds[index + 1]]) } - None } } /// An owning node type. Useful for trie iterators. #[cfg_attr(feature = "std", derive(Debug))] #[derive(PartialEq, Eq)] -pub enum OwnedNode { +pub enum OwnedNode { /// Empty trie node. Empty, /// Leaf node: partial key and value. - Leaf(NibbleVec, DBValue), + Leaf(NibbleVec, DBValue), /// Extension node: partial key and child node. - Extension(NibbleVec, DBValue), + Extension(NibbleVec, DBValue), /// Branch node: children and an optional value. Branch(Branch), /// Branch node: children and an optional value. - NibbledBranch(NibbleVec, Branch), + NibbledBranch(NibbleVec, Branch), } -impl<'a, N: NibbleOps> From> for OwnedNode { - fn from(node: Node<'a, N>) -> Self { +impl<'a> From> for OwnedNode { + fn from(node: Node<'a>) -> Self { match node { Node::Empty => OwnedNode::Empty, Node::Leaf(k, v) => @@ -149,9 +120,9 @@ impl<'a, N: NibbleOps> From> for OwnedNode { Node::Extension(k, child) => OwnedNode::Extension(k.into(), DBValue::from_slice(child)), Node::Branch(c, val) => - OwnedNode::Branch(Branch::new::(&c, val)), + OwnedNode::Branch(Branch::new(c, val)), Node::NibbledBranch(k, c, val) => - OwnedNode::NibbledBranch(k.into(), Branch::new::(&c, val)), + OwnedNode::NibbledBranch(k.into(), Branch::new(c, val)), } } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 5410502f..7339ce3d 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -17,7 +17,6 @@ use hash_db::Hasher; use node::Node; -use nibble::NibbleOps; use ChildReference; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -45,7 +44,7 @@ impl Error for T {} pub type Partial<'a> = ((u8, u8), &'a[u8]); /// Trait for trie node encoding/decoding. -pub trait NodeCodec: Sized { +pub trait NodeCodec: Sized { /// Codec error type. type Error: Error; @@ -53,7 +52,7 @@ pub trait NodeCodec: Sized { fn hashed_null_node() -> H::Out; /// Decode bytes to a `Node`. Returns `Self::E` on failure. - fn decode(data: &[u8]) -> Result, Self::Error>; + fn decode(data: &[u8]) -> Result; /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. fn try_decode_hash(data: &[u8]) -> Option; @@ -109,7 +108,7 @@ pub trait BitMap: Sized { fn decode(data: &[u8]) -> Result; /// Return wether the bitmap registered a value for a branch - /// child index. + /// child index. fn value_at(&self, i: usize) -> bool; /// Encode bitmap, output slice must be of right length. diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 4fe95ec1..b2add430 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; -use nibble::{NibbleSlice, NibbleOps, ChildSliceIndex}; +use nibble::{NibbleSlice, nibble_ops}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; @@ -109,7 +109,7 @@ where &'db self, node: &[u8], partial_key: Prefix, ) -> Result, TrieHash, CError> { - match (partial_key.0.is_empty() && (partial_key.1).0 == 0, L::Codec::try_decode_hash(node)) { + match (partial_key.0.is_empty() && partial_key.1.is_none(), L::Codec::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) @@ -159,7 +159,7 @@ where { trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: NibbleVec, + partial_key: NibbleVec, index: Option, } @@ -194,7 +194,7 @@ where }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { - let nodes: Vec> = nodes.0.iter(nodes.1) + let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { @@ -214,7 +214,7 @@ where .finish() }, Ok(Node::NibbledBranch(slice, nodes, value)) => { - let nodes: Vec> = nodes.0.iter(nodes.1) + let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { @@ -282,12 +282,12 @@ enum Status { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq)] -struct Crumb { - node: OwnedNode, +struct Crumb { + node: OwnedNode, status: Status, } -impl Crumb { +impl Crumb { /// Move on to next status in the node's sequence. fn increment(&mut self) { self.status = match (&self.status, &self.node) { @@ -297,7 +297,7 @@ impl Crumb { | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), (&Status::AtChild(x), &OwnedNode::Branch(_)) | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) - if x < N::NIBBLE_LENGTH => Status::AtChild(x + 1), + if x < (nibble_ops::NIBBLE_LENGTH - 1) => Status::AtChild(x + 1), _ => Status::Exiting, } } @@ -306,8 +306,8 @@ impl Crumb { /// Iterator for going through all values in the trie. pub struct TrieDBIterator<'a, L: TrieLayout> { db: &'a TrieDB<'a, L>, - trail: Vec>, - key_nibbles: NibbleVec, + trail: Vec, + key_nibbles: NibbleVec, } impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { @@ -325,7 +325,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { fn seek<'key>( &mut self, node_data: &DBValue, - key: NibbleSlice<'key, L::Nibble>, + key: NibbleSlice<'key>, ) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; @@ -384,7 +384,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { node: node.clone().into(), }); self.key_nibbles.push(i); - if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { + if let Some(ref child) = nodes[i as usize] { full_key_nibbles += 1; partial = partial.mid(1); let child = self.db @@ -416,7 +416,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { }); self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push(i); - if let Some(ref child) = nodes.0.slice_at(i as usize, nodes.1) { + if let Some(ref child) = nodes[i as usize] { full_key_nibbles += slice.len() + 1; partial = partial.mid(slice.len() + 1); let child = self.db @@ -445,7 +445,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { } /// Descend into a payload. - fn descend_into_node(&mut self, node: OwnedNode) { + fn descend_into_node(&mut self, node: OwnedNode) { let trail = &mut self.trail; let key_nibbles = &mut self.key_nibbles; trail.push(Crumb { status: Status::Entering, node }); @@ -460,7 +460,7 @@ impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// The present key. This can only be called on valued node (key is therefore /// aligned to byte). - fn key(&self) -> NibbleSlice { + fn key(&self) -> NibbleSlice { self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index e6d87ab7..a69c8a37 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -21,7 +21,7 @@ use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; -use nibble::{NibbleVec, NibbleSlice, NibbleOps, ChildSliceIndex, IterChildSliceIndex}; +use nibble::{NibbleVec, NibbleSlice, nibble_ops}; use elastic_array::ElasticArray36; use ::core_::mem; use ::core_::ops::Index; @@ -62,15 +62,16 @@ impl From for NodeHandle { } } -fn empty_children() -> Vec>> { - let mut res = Vec::with_capacity(N::NIBBLE_LENGTH); - (0..N::NIBBLE_LENGTH).for_each(|_| res.push(None)); - res +fn empty_children() -> Box<[Option>; 16]> { + Box::new([ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ]) } /// Type alias to indicate the nible covers a full key, /// therefore its left side is a full prefix. -type NibbleFullKey<'key, N> = NibbleSlice<'key, N>; +type NibbleFullKey<'key> = NibbleSlice<'key>; /// Node types in the Trie. #[cfg_attr(feature = "std", derive(Debug))] @@ -87,9 +88,9 @@ enum Node { /// The child node is always a branch. Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Vec>>, Option), + Branch(Box<[Option>; 16]>, Option), /// Branch node with support for a nibble (to avoid extension node). - NibbledBranch(NodeKey, Vec>>, Option), + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } impl Node @@ -98,86 +99,90 @@ where + PartialEq + Eq + Hash + Send + Sync + Clone + Copy { // load an inline node into memory or get the hash to do the lookup later. - fn inline_or_hash( + fn inline_or_hash( node: &[u8], db: &dyn HashDB, storage: &mut NodeStorage ) -> NodeHandle where - N: NibbleOps, - C: NodeCodec, + C: NodeCodec, H: Hasher, { C::try_decode_hash(&node) .map(NodeHandle::Hash) .unwrap_or_else(|| { - let child = Node::from_encoded::(node, db, storage); + let child = Node::from_encoded::(node, db, storage); NodeHandle::InMemory(storage.alloc(Stored::New(child))) }) } // Decode a node from encoded bytes. - fn from_encoded<'a, 'b, C, H, N>( + fn from_encoded<'a, 'b, C, H>( data: &'a[u8], db: &dyn HashDB, storage: &'b mut NodeStorage, ) -> Self where - N: NibbleOps, C: NodeCodec, H: Hasher, + C: NodeCodec, H: Hasher, { - let decode_children = | - encoded_children: IterChildSliceIndex, - storage: &'b mut NodeStorage, - | { - let mut res = Vec::with_capacity(N::ChildSliceIndex::NIBBLE_LENGTH); - encoded_children.for_each(|o_data| { - let v = o_data.map(|data| Self::inline_or_hash::(data, db, storage)); - res.push(v) - }); - res - }; - match C::decode(data).unwrap_or(EncodedNode::Empty) { EncodedNode::Empty => Node::Empty, EncodedNode::Leaf(k, v) => Node::Leaf(k.into(), DBValue::from_slice(&v)), EncodedNode::Extension(key, cb) => { Node::Extension( key.into(), - Self::inline_or_hash::(cb, db, storage)) - }, - EncodedNode::Branch(encoded_children, val) => { - let children = decode_children( - encoded_children.0.iter(encoded_children.1), - storage, - ); - Node::Branch(children, val.map(DBValue::from_slice)) - }, - EncodedNode::NibbledBranch(k, encoded_children, val) => { - let children = decode_children( - encoded_children.0.iter(encoded_children.1), - storage, - ); - Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) + Self::inline_or_hash::(cb, db, storage)) }, + EncodedNode::Branch(encoded_children, val) => { + let mut child = |i:usize| { + encoded_children[i].map(|data| + Self::inline_or_hash::(data, db, storage) + ) + }; + + let children = Box::new([ + child(0), child(1), child(2), child(3), + child(4), child(5), child(6), child(7), + child(8), child(9), child(10), child(11), + child(12), child(13), child(14), child(15), + ]); + + Node::Branch(children, val.map(DBValue::from_slice)) + }, + EncodedNode::NibbledBranch(k, encoded_children, val) => { + let mut child = |i:usize| { + encoded_children[i].map(|data| + Self::inline_or_hash::(data, db, storage) + ) + }; + + let children = Box::new([ + child(0), child(1), child(2), child(3), + child(4), child(5), child(6), child(7), + child(8), child(9), child(10), child(11), + child(12), child(13), child(14), child(15), + ]); + + Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) + }, } } // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec + fn into_encoded(self, mut child_cb: F) -> Vec where - N: NibbleOps, - C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + C: NodeCodec, + F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => { - let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); C::leaf_node(pr.right(), &value) }, Node::Extension(partial, child) => { - let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); let c = child_cb(child, Some(&pr), None); C::extension_node( @@ -199,7 +204,7 @@ where ) }, Node::NibbledBranch(partial, mut children, value) => { - let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); C::branch_node_nibbled( it, @@ -211,7 +216,7 @@ where .map(|(i, maybe_child)| { //let branch_index = [i as u8]; maybe_child.map(|child| { - let pr = NibbleSlice::::new_offset(&partial.1[..], partial.0); + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); child_cb(child, Some(&pr), Some(i as u8)) }) }), @@ -359,7 +364,7 @@ where db: &'a mut dyn HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, - death_row: HashSet<(TrieHash, (ElasticArray36, (u8, u8)))>, + death_row: HashSet<(TrieHash, (ElasticArray36, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, @@ -422,7 +427,7 @@ where ) -> Result, CError> { let node_encoded = self.db.get(&hash, key) .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + let node = Node::from_encoded::( &node_encoded, &*self.db, &mut self.storage @@ -435,14 +440,14 @@ where fn inspect( &mut self, stored: Stored>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, inspector: F, ) -> Result>, bool)>, TrieHash, CError> where F: FnOnce( &mut Self, Node>, - &mut NibbleFullKey, + &mut NibbleFullKey, ) -> Result>, TrieHash, CError>, { Ok(match stored { @@ -468,7 +473,7 @@ where // Walk the trie, attempting to find the key's node. fn lookup<'x, 'key>( &'x self, - mut partial: NibbleSlice<'key, L::Nibble>, + mut partial: NibbleSlice<'key>, handle: &NodeHandle>, ) -> Result, TrieHash, CError> where 'x: 'key @@ -535,7 +540,7 @@ where fn insert_at( &mut self, handle: NodeHandle>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, ) -> Result<(StorageHandle, bool), TrieHash, CError> { @@ -556,7 +561,7 @@ where fn insert_inspector( &mut self, node: Node>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, ) -> Result>, TrieHash, CError> { @@ -642,7 +647,7 @@ where let nbranch_partial = existing_key.mid(common + 1).to_stored(); let low = Node::NibbledBranch(nbranch_partial, children, stored_value); let ix = existing_key.at(common); - let mut children = empty_children::<_, L::Nibble>(); + let mut children = empty_children(); let alloc_storage = self.storage.alloc(Stored::New(low)); @@ -731,7 +736,7 @@ where ); // one of us isn't empty: transmute to branch here - let mut children = empty_children::<_, L::Nibble>(); + let mut children = empty_children(); let branch = if L::USE_EXTENSION && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) @@ -763,7 +768,7 @@ where // make a stub branch let branch = Node::NibbledBranch( existing_key.to_stored(), - empty_children::<_, L::Nibble>(), + empty_children(), Some(stored_value), ); // augment the new branch. @@ -779,7 +784,7 @@ where // fully-shared prefix for an extension. // make a stub branch and an extension. - let branch = Node::Branch(empty_children::<_, L::Nibble>(), Some(stored_value)); + let branch = Node::Branch(empty_children(), Some(stored_value)); // augment the new branch. key.advance(common); let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); @@ -834,7 +839,7 @@ where assert!(!existing_key.is_empty()); let idx = existing_key.at(0) as usize; - let mut children = empty_children::<_, L::Nibble>(); + let mut children = empty_children(); children[idx] = if existing_key.len() == 1 { // direct extension, just replace. Some(child_branch) @@ -902,7 +907,7 @@ where fn remove_at( &mut self, handle: NodeHandle>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result, TrieHash, CError> { let stored = match handle { @@ -926,7 +931,7 @@ where fn remove_inspector( &mut self, node: Node>, - key: &mut NibbleFullKey, + key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result>, TrieHash, CError> { let partial = key.clone(); @@ -1055,7 +1060,7 @@ where target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, - NibbleSlice::::from_stored(&encoded), + NibbleSlice::from_stored(&encoded), ); Action::Restore(Node::Leaf(encoded, value)) } @@ -1107,7 +1112,7 @@ where fn fix( &mut self, node: Node>, - key: NibbleSlice, + key: NibbleSlice, ) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { @@ -1136,7 +1141,7 @@ where (UsedIndex::One(a), None) => { // only one onward node. make an extension. - let new_partial = NibbleSlice::::new_offset(&[a], 1).to_stored(); + let new_partial = NibbleSlice::new_offset(&[a], 1).to_stored(); let child = children[a as usize].take() .expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); @@ -1146,7 +1151,7 @@ where // make a leaf. #[cfg(feature = "std")] trace!(target: "trie", "fixing: branch -> leaf"); - Ok(Node::Leaf(NibbleSlice::::new(&[]).to_stored(), value)) + Ok(Node::Leaf(NibbleSlice::new(&[]).to_stored(), value)) } (_, value) => { // all is well. @@ -1183,22 +1188,21 @@ where // only one onward node. use child instead let child = children[a as usize].take() .expect("used_index only set if occupied; qed"); - let mut kc = key.clone(); - kc.advance((enc_nibble.1.len() * L::Nibble::NIBBLE_PER_BYTE) - enc_nibble.0); - let (st, ost, op) = match kc.left() { - (st, (0, _v)) => (st, None, (1, L::Nibble::push_at_left(0, a, 0))), - (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { - let mut so: ElasticArray36 = st.into(); - so.push(L::Nibble::pad_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); - (st, Some(so), (0, 0)) + let mut key2 = key.clone(); + key2.advance((enc_nibble.1.len() * nibble_ops::NIBBLE_PER_BYTE) - enc_nibble.0); + let (start, alloc_start, prefix_end) = match key2.left() { + (start, None) => (start, None, Some(nibble_ops::push_at_left(0, a, 0))), + (start, Some(v)) => { + let mut so: ElasticArray36 = start.into(); + so.push(nibble_ops::pad_left(v) | a); + (start, Some(so), None) }, - (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), }; - let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); + let child_prefix = (alloc_start.as_ref().map(|start| &start[..]).unwrap_or(start), prefix_end); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, child_pref)?; + let handle = self.cache(h, child_prefix)?; self.storage.destroy(handle) } }; @@ -1207,7 +1211,7 @@ where Stored::Cached(node, hash) => { self.death_row.insert(( hash, - (child_pref.0[..].into(), child_pref.1), + (child_prefix.0[..].into(), child_prefix.1), )); node }, @@ -1215,11 +1219,11 @@ where match child_node { Node::Leaf(sub_partial, value) => { let mut enc_nibble = enc_nibble; - combine_key::( + combine_key( &mut enc_nibble, - (L::Nibble::NIBBLE_PER_BYTE - 1, &[a][..]), + (nibble_ops::NIBBLE_PER_BYTE - 1, &[a][..]), ); - combine_key::( + combine_key( &mut enc_nibble, (sub_partial.0, &sub_partial.1[..]), ); @@ -1227,11 +1231,11 @@ where }, Node::NibbledBranch(sub_partial, ch_children, ch_value) => { let mut enc_nibble = enc_nibble; - combine_key::( + combine_key( &mut enc_nibble, - (L::Nibble::NIBBLE_PER_BYTE - 1, &[a][..]), + (nibble_ops::NIBBLE_PER_BYTE - 1, &[a][..]), ); - combine_key::( + combine_key( &mut enc_nibble, (sub_partial.0, &sub_partial.1[..]), ); @@ -1257,25 +1261,24 @@ where Node::Extension(partial, child) => { // We could advance key, but this code can also be called // recursively, so there might be some prefix from branch. - let a = partial.1[partial.1.len() - 1] & (255 >> 4); - let mut kc = key.clone(); - kc.advance((partial.1.len() * L::Nibble::NIBBLE_PER_BYTE) - partial.0 - 1); - let (st, ost, op) = match kc.left() { - (st, (0, _v)) => (st, None, (1, L::Nibble::push_at_left(0, a, 0))), - (st, (i, v)) if i == L::Nibble::LAST_NIBBLE_INDEX => { - let mut so: ElasticArray36 = st.into(); - // Complete last byte with `a`. - so.push(L::Nibble::pad_left(L::Nibble::LAST_NIBBLE_INDEX, v) | a); - (st, Some(so), (0, 0)) + let last = partial.1[partial.1.len() - 1] & (255 >> 4); + let mut key2 = key.clone(); + key2.advance((partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0 - 1); + let (start, alloc_start, prefix_end) = match key2.left() { + (start, None) => (start, None, Some(nibble_ops::push_at_left(0, last, 0))), + (start, Some(v)) => { + let mut so: ElasticArray36 = start.into(); + // Complete last byte with `last`. + so.push(nibble_ops::pad_left(v) | last); + (start, Some(so), None) }, - (st, (ix, v)) => (st, None, (ix, L::Nibble::push_at_left(ix, a, v))), }; - let child_pref = (ost.as_ref().map(|st| &st[..]).unwrap_or(st), op); + let child_prefix = (alloc_start.as_ref().map(|start| &start[..]).unwrap_or(start), prefix_end); let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, child_pref)?; + let handle = self.cache(h, child_prefix)?; self.storage.destroy(handle) } }; @@ -1291,12 +1294,12 @@ where if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. self.death_row.insert( - (hash, (child_pref.0[..].into(), child_pref.1)), + (hash, (child_prefix.0[..].into(), child_prefix.1)), ); } // subpartial let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + combine_key(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] trace!( target: "trie", @@ -1309,11 +1312,11 @@ where // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, (child_pref.0[..].into(), child_pref.1))); + self.death_row.insert((hash, (child_prefix.0[..].into(), child_prefix.1))); } // subpartial oly let mut partial = partial; - combine_key::(&mut partial, (sub_partial.0, &sub_partial.1[..])); + combine_key(&mut partial, (sub_partial.0, &sub_partial.1[..])); #[cfg(feature = "std")] trace!( target: "trie", @@ -1362,7 +1365,7 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::Codec, L::Hash, L::Nibble>( + let encoded_root = node.into_encoded::<_, L::Codec, L::Hash>( |child, o_slice, o_index| { let mov = k.append_optional_slice_and_nibble(o_slice, o_index); let cr = self.commit_child(child, &mut k); @@ -1395,7 +1398,7 @@ where fn commit_child( &mut self, handle: NodeHandle>, - prefix: &mut NibbleVec, + prefix: &mut NibbleVec, ) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), @@ -1406,7 +1409,7 @@ where let encoded = { let commit_child = | node_handle, - o_slice: Option<&NibbleSlice>, + o_slice: Option<&NibbleSlice>, o_index: Option | { let mov = prefix.append_optional_slice_and_nibble(o_slice, o_index); @@ -1414,7 +1417,7 @@ where prefix.drop_lasts(mov); cr }; - node.into_encoded::<_, L::Codec, L::Hash, L::Nibble>(commit_child) + node.into_encoded::<_, L::Codec, L::Hash>(commit_child) }; if encoded.len() >= L::Hash::LENGTH { let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); @@ -1532,14 +1535,14 @@ where } /// combine two NodeKeys -fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { - debug_assert!(start.0 < N::NIBBLE_PER_BYTE); - debug_assert!(end.0 < N::NIBBLE_PER_BYTE); - let final_ofset = (start.0 + end.0) % N::NIBBLE_PER_BYTE; - let _shifted = N::shift_key(start, final_ofset); +fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { + debug_assert!(start.0 < nibble_ops::NIBBLE_PER_BYTE); + debug_assert!(end.0 < nibble_ops::NIBBLE_PER_BYTE); + let final_offset = (start.0 + end.0) % nibble_ops::NIBBLE_PER_BYTE; + let _shifted = nibble_ops::shift_key(start, final_offset); let st = if end.0 > 0 { let sl = start.1.len(); - start.1[sl - 1] |= N::pad_right((N::NIBBLE_PER_BYTE - end.0) as u8, end.1[0]); + start.1[sl - 1] |= nibble_ops::pad_right(end.1[0]); 1 } else { 0 @@ -1556,9 +1559,8 @@ mod tests { use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; use elastic_array::ElasticArray36; - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, TrieLayout, NodeCodec, - ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension, - ExtensionLayout, BitMap16}; + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, NodeCodec, + ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension}; fn populate_trie<'db>( db: &'db mut dyn HashDB, @@ -1603,8 +1605,7 @@ mod tests { } fn reference_hashed_null_node() -> ::Out { - - as NodeCodec::Nibble>>::hashed_null_node() + >::hashed_null_node() } #[test] @@ -2016,7 +2017,7 @@ mod tests { let b: &[u8] = [0x56, 0x78][..].into(); let test_comb = |a: (_, &ElasticArray36<_>), b, c| { let mut a = (a.0, a.1.clone()); - super::combine_key::(&mut a, b); + super::combine_key(&mut a, b); assert_eq!((a.0, &a.1[..]), c); }; test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); From d9a12d0636a3751096a6bc619d14cb0b4059969c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 31 Jul 2019 21:18:02 +0200 Subject: [PATCH 116/120] Comment. --- trie-db/src/nibble/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index d16a63dd..0b9cbbbf 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -90,9 +90,8 @@ pub mod nibble_ops { i % NIBBLE_PER_BYTE } - /// The nibble shifts needed - /// for alignment a given unaligned padding. - /// We use two value for one is a left shift and + /// The nibble shifts needed to align. + /// We use two value, one is a left shift and /// the other is a right shift. pub const SPLIT_SHIFTS: (usize, usize) = (4, 4); From 869b4ce1e789e8a2f159719518f889aa49b474d2 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 1 Aug 2019 12:10:18 +0200 Subject: [PATCH 117/120] formatting, and remove unused BitMap trait. --- test-support/reference-trie/src/lib.rs | 12 ++++++++---- trie-db/src/lib.rs | 2 +- trie-db/src/node_codec.rs | 21 --------------------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 6e3ac833..04c0a08d 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -752,8 +752,10 @@ impl NodeCodec for ReferenceNodeCodecNoExt { input, (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_ops::number_padding(nibble_count)); + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + nibble_ops::number_padding(nibble_count), + ); let bitmap_slice = take( input, BITMAP_LENGTH, @@ -785,8 +787,10 @@ impl NodeCodec for ReferenceNodeCodecNoExt { input, (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, ).ok_or(CodecError::from("Bad format"))?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, - nibble_ops::number_padding(nibble_count)); + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + nibble_ops::number_padding(nibble_count), + ); let count = >::decode(input)?.0 as usize; Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(CodecError::from("Bad format"))?)) } diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index d7cfb60d..fb26f548 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -95,7 +95,7 @@ pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, nibble_ops}; -pub use node_codec::{NodeCodec, Partial, BitMap}; +pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index 7339ce3d..afa8cb20 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -93,24 +93,3 @@ pub trait NodeCodec: Sized { ) -> Vec; } -/// Bitmap encoder for the number of children nodes. -/// This would be useless with https://github.com/rust-lang/rust/issues/43408. -pub trait BitMap: Sized { - /// length to encode the bitmap - const ENCODED_LEN: usize; - /// Codec error type. - type Error: Error; - - /// Codec buffer to use. - type Buffer: AsRef<[u8]> + AsMut<[u8]> + Default; - - /// Decode bitmap from its encoded full slice. - fn decode(data: &[u8]) -> Result; - - /// Return wether the bitmap registered a value for a branch - /// child index. - fn value_at(&self, i: usize) -> bool; - - /// Encode bitmap, output slice must be of right length. - fn encode>(has_children: I , output: &mut [u8]); -} From 9b944826a10ce9f9dec57530985f14ca443af85f Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 1 Aug 2019 14:20:57 +0200 Subject: [PATCH 118/120] Removing: radix related cache struct from iter_build. Using 'GenericNoExtensionLayout' for 'NoExtensionLayout'. --- test-support/reference-trie/src/lib.rs | 8 +------- trie-db/src/iter_build.rs | 15 --------------- trie-db/src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 04c0a08d..a81bd582 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -59,13 +59,7 @@ impl TrieLayout for GenericNoExtensionLayout { impl TrieConfiguration for GenericNoExtensionLayout { } /// Trie layout without extension nodes. -pub struct NoExtensionLayout; - -impl TrieLayout for NoExtensionLayout { - const USE_EXTENSION: bool = false; - type Hash = keccak_hasher::KeccakHasher; - type Codec = ReferenceNodeCodecNoExt; -} +pub type NoExtensionLayout = GenericNoExtensionLayout; /// Children bitmap codec for radix 16 trie. pub struct Bitmap(u16); diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index e1a4a5b1..97778059 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -37,21 +37,6 @@ macro_rules! exponential_out { type CacheNode = Option>; -/// A builder for fix constant length cache, should match `NibbleOps` `NIBBLE_LENGTH`. -pub trait CacheBuilder { - /// Size of cache. - const SIZE: usize; - /// The type of the cache. - type Cache: AsRef<[CacheNode]> + AsMut<[CacheNode]>; - /// Create a new cache. - fn new_vec_slice_buffer() -> Self::Cache; -} - -/// Cache builder for radix 16 trie. -pub struct Cache16; -/// Cache builder for radix 4 trie. -pub struct Cache4; - #[inline(always)] fn new_vec_slice_buffer() -> [CacheNode; 16] { exponential_out!(@3, [None, None]) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index fb26f548..3309c146 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,7 @@ pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, nibble_ops}; pub use node_codec::{NodeCodec, Partial}; pub use iter_build::{trie_visit, ProcessEncodedNode, - TrieBuilder, TrieRoot, TrieRootUnhashed, CacheBuilder, Cache16, Cache4}; + TrieBuilder, TrieRoot, TrieRootUnhashed}; #[cfg(feature = "std")] pub use iter_build::TrieRootPrint; From 5b9b02235d1dd290526cacfc32bfcc85f90a1720 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 1 Aug 2019 16:20:14 +0200 Subject: [PATCH 119/120] No assumption on biggest_depth. --- trie-db/src/iter_build.rs | 2 +- trie-db/src/nibble/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 97778059..cf6f65ee 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -259,7 +259,7 @@ pub fn trie_visit(input: I, callback: &mut F) A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, F: ProcessEncodedNode>, - { +{ let no_extension = !T::USE_EXTENSION; let mut depth_queue = CacheAccum::::new(); // compare iter ordering diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 0b9cbbbf..ce37f4d0 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -18,6 +18,7 @@ mod nibblevec; mod nibbleslice; use elastic_array::ElasticArray36; use crate::node::NodeKey; +use core_::cmp; /// Utility methods to work on radix 16 nibble. pub mod nibble_ops { @@ -97,8 +98,7 @@ pub mod nibble_ops { /// Count the biggest common depth between two left aligned packed nibble slice. pub fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { - // sorted assertion preventing out of bound - for a in 0..v1.len() { + for a in 0 .. cmp::min(v1.len(), v2.len()) { if v1[a] == v2[a] { } else { return a * NIBBLE_PER_BYTE + left_common(v1[a], v2[a]); From 79d71b6b30aedf90393be029721c1c85db0a112f Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 1 Aug 2019 16:25:46 +0200 Subject: [PATCH 120/120] fixing biggest depth correctly. --- trie-db/src/nibble/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index ce37f4d0..cf0e95ae 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -98,13 +98,13 @@ pub mod nibble_ops { /// Count the biggest common depth between two left aligned packed nibble slice. pub fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { - for a in 0 .. cmp::min(v1.len(), v2.len()) { - if v1[a] == v2[a] { - } else { + let upper_bound = cmp::min(v1.len(), v2.len()); + for a in 0 .. upper_bound { + if v1[a] != v2[a] { return a * NIBBLE_PER_BYTE + left_common(v1[a], v2[a]); } } - return v1.len() * NIBBLE_PER_BYTE; + upper_bound * NIBBLE_PER_BYTE } /// Calculate the number of common nibble between two left aligned bytes.