diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index c0fa1048..3c546e47 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -17,10 +17,11 @@ use std::fmt; use std::iter::once; use std::marker::PhantomData; +use std::ops::Range; use parity_scale_codec::{Decode, Input, Output, Encode, Compact, Error as CodecError}; use trie_root::Hasher; use trie_db::{ - node::Node, + node::{NibbleSlicePlan, NodePlan}, triedbmut::ChildReference, DBValue, trie_visit, @@ -38,7 +39,7 @@ pub use trie_db::{ pub use trie_db::{Record, TrieLayout, TrieConfiguration, nibble_ops}; pub use trie_root::TrieStream; pub mod node { - pub use trie_db::node::OwnedNode; + pub use trie_db::node::Node; } /// Trie layout using extension nodes. @@ -431,26 +432,6 @@ fn decode_size(first: u8, input: &mut I) -> Result Err("Size limit reached for a nibble slice".into()) } -#[test] -fn test_encoding_simple_trie() { - for prefix in [ - LEAF_PREFIX_MASK_NO_EXT, - 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) { - let mut output = Vec::new(); - 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 = decode_size(first, input); - assert_eq!(Ok(std::cmp::min(i, NIBBLE_SIZE_BOUND_NO_EXT)), v); - } - - } -} - impl Encode for NodeHeaderNoExt { fn encode_to(&self, output: &mut T) { match self { @@ -509,15 +490,6 @@ pub struct ReferenceNodeCodec; #[derive(Default, Clone)] pub struct ReferenceNodeCodecNoExt; -fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { - if input.len() < count { - return None - } - let r = &(*input)[..count]; - *input = &(*input)[count..]; - Some(r) -} - 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() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; @@ -585,6 +557,57 @@ fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { output } +struct ByteSliceInput<'a> { + data: &'a [u8], + offset: usize, +} + +impl<'a> ByteSliceInput<'a> { + fn new(data: &'a [u8]) -> Self { + ByteSliceInput { + data, + offset: 0, + } + } + + fn take(&mut self, count: usize) -> Result, CodecError> { + if self.offset + count > self.data.len() { + return Err("out of data".into()); + } + + let range = self.offset..(self.offset + count); + self.offset += count; + Ok(range) + } +} + +impl<'a> Input for ByteSliceInput<'a> { + fn remaining_len(&mut self) -> Result, CodecError> { + let remaining = if self.offset <= self.data.len() { + Some(self.data.len() - self.offset) + } else { + None + }; + Ok(remaining) + } + + fn read(&mut self, into: &mut [u8]) -> Result<(), CodecError> { + let range = self.take(into.len())?; + into.copy_from_slice(&self.data[range]); + Ok(()) + } + + fn read_byte(&mut self) -> Result { + if self.offset + 1 > self.data.len() { + return Err("out of data".into()); + } + + let byte = self.data[self.offset]; + self.offset += 1; + Ok(byte) + } +} + // 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 @@ -597,54 +620,55 @@ impl NodeCodec for ReferenceNodeCodec { H::hash(>::empty_node()) } - fn decode(data: &[u8]) -> ::std::result::Result { - let input = &mut &*data; - match NodeHeader::decode(input)? { - NodeHeader::Null => Ok(Node::Empty), + fn decode_plan(data: &[u8]) -> ::std::result::Result { + let mut input = ByteSliceInput::new(data); + match NodeHeader::decode(&mut input)? { + NodeHeader::Null => Ok(NodePlan::Empty), NodeHeader::Branch(has_value) => { - let bitmap_slice = take(input, BITMAP_LENGTH) - .ok_or(CodecError::from("Bad format"))?; - let bitmap = Bitmap::decode(&bitmap_slice[..])?; + let bitmap_range = input.take(BITMAP_LENGTH)?; + let bitmap = Bitmap::decode(&data[bitmap_range])?; let value = if has_value { - let count = >::decode(input)?.0 as usize; - Some(take(input, count).ok_or(CodecError::from("Bad format"))?) + let count = >::decode(&mut input)?.0 as usize; + Some(input.take(count)?) } else { None }; - let mut children = [None; 16]; - + let mut children = [ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ]; for i in 0..nibble_ops::NIBBLE_LENGTH { if bitmap.value_at(i) { - let count = >::decode(input)?.0 as usize; - children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); + let count = >::decode(&mut input)?.0 as usize; + children[i] = Some(input.take(count)?); } } - Ok(Node::Branch(children, value)) + Ok(NodePlan::Branch { value, children }) } NodeHeader::Extension(nibble_count) => { - let nibble_data = take( - 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 count = >::decode(input)?.0 as usize; - Ok(Node::Extension(nibble_slice, take(input, count) - .ok_or(CodecError::from("Bad format"))?)) + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let count = >::decode(&mut input)?.0 as usize; + let child = input.take(count)?; + Ok(NodePlan::Extension { + partial: NibbleSlicePlan::new(partial, partial_padding), + child + }) } NodeHeader::Leaf(nibble_count) => { - let nibble_data = take( - 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 count = >::decode(input)?.0 as usize; - Ok(Node::Leaf(nibble_slice, take(input, count) - .ok_or(CodecError::from("Bad format"))?)) + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let count = >::decode(&mut input)?.0 as usize; + let value = input.take(count)?; + Ok(NodePlan::Leaf { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + }) } } } @@ -737,62 +761,60 @@ impl NodeCodec for ReferenceNodeCodecNoExt { H::hash(>::empty_node()) } - fn decode(data: &[u8]) -> ::std::result::Result { - let input = &mut &*data; - let head = NodeHeaderNoExt::decode(input)?; - match head { - NodeHeaderNoExt::Null => Ok(Node::Empty), + fn decode_plan(data: &[u8]) -> ::std::result::Result { + let mut input = ByteSliceInput::new(data); + match NodeHeaderNoExt::decode(&mut input)? { + NodeHeaderNoExt::Null => Ok(NodePlan::Empty), NodeHeaderNoExt::Branch(has_value, nibble_count) => { let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; // check that the padding is valid (if any) - if padding && nibble_ops::pad_left(input[0]) != 0 { + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { return Err(CodecError::from("Bad format")); } - let nibble_data = take( - 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 bitmap_slice = take( - input, - BITMAP_LENGTH, - ).ok_or(CodecError::from("Bad format"))?; - let bitmap = Bitmap::decode(&bitmap_slice[..])?; + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let bitmap_range = input.take(BITMAP_LENGTH)?; + let bitmap = Bitmap::decode(&data[bitmap_range])?; let value = if has_value { - let count = >::decode(input)?.0 as usize; - Some(take(input, count).ok_or(CodecError::from("Bad format"))?) + let count = >::decode(&mut input)?.0 as usize; + Some(input.take(count)?) } else { None }; - let mut children = [None; 16]; - + let mut children = [ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ]; for i in 0..nibble_ops::NIBBLE_LENGTH { if bitmap.value_at(i) { - let count = >::decode(input)?.0 as usize; - children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); + let count = >::decode(&mut input)?.0 as usize; + children[i] = Some(input.take(count)?); } } - Ok(Node::NibbledBranch(nibble_slice, children, value)) + Ok(NodePlan::NibbledBranch { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + children, + }) } NodeHeaderNoExt::Leaf(nibble_count) => { let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; // check that the padding is valid (if any) - if padding && nibble_ops::pad_left(input[0]) != 0 { + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { return Err(CodecError::from("Bad format")); } - let nibble_data = take( - 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 count = >::decode(input)?.0 as usize; - Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(CodecError::from("Bad format"))?)) + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let count = >::decode(&mut input)?.0 as usize; + let value = input.take(count)?; + Ok(NodePlan::Leaf { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + }) } } } @@ -1161,41 +1183,66 @@ pub fn compare_no_extension_insert_remove( assert_eq!(*t.root(), calc_root_no_extension(data2)); } -#[test] -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 = > +#[cfg(test)] +mod tests { + use super::*; + use trie_db::node::Node; + + #[test] + fn test_encoding_simple_trie() { + for prefix in [ + LEAF_PREFIX_MASK_NO_EXT, + 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) { + let mut output = Vec::new(); + 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 = decode_size(first, input); + assert_eq!(Ok(std::cmp::min(i, NIBBLE_SIZE_BOUND_NO_EXT)), v); + } + } + } + + #[test] + 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 = > ::leaf_node(((0, 0), &input), &[1]); - let dec = > + let dec = > ::decode(&enc).unwrap(); - let o_sl = if let Node::Leaf(sl, _) = dec { - Some(sl) - } else { None }; - assert!(o_sl.is_some()); -} + let o_sl = if let Node::Leaf(sl, _) = dec { + Some(sl) + } 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(); - encode_size_and_prefix(sizes[i], 0, &mut enc); - assert_eq!(enc, encs[i]); - let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); - assert_eq!(s_dec, Ok(sizes[i])); + #[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(); + encode_size_and_prefix(sizes[i], 0, &mut enc); + assert_eq!(enc, encs[i]); + let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); + assert_eq!(s_dec, Ok(sizes[i])); + } } } diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 0033aa4c..b9ee6d56 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, + trie_iteration, nibble_common_prefix, ); criterion_main!(benches); @@ -445,3 +446,20 @@ fn trie_mut_build_b(c: &mut Criterion) { }), data); } + +fn trie_iteration(c: &mut Criterion) { + use memory_db::HashKey; + + let input = input2(29, 204800, 32); + + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + let root = reference_trie::calc_root_build(input, &mut mdb); + + c.bench_function("trie_iteration", move |b: &mut Bencher| + b.iter(|| { + let trie = reference_trie::RefTrieDB::new(&mdb, &root).unwrap(); + let mut iter = trie_db::TrieDBNodeIterator::new(&trie).unwrap(); + assert!(iter.all(|result| result.is_ok())); + }) + ); +} diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index 6ec91ab4..d9af230f 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{CError, DBValue, Result, Trie, TrieError, TrieHash, TrieIterator, TrieLayout}; -use hash_db::Hasher; +use super::{CError, DBValue, Result, Trie, TrieHash, TrieIterator, TrieLayout}; +use hash_db::{Hasher, EMPTY_PREFIX}; use triedb::TrieDB; -use node::{Node, OwnedNode}; -use node_codec::NodeCodec; +use node::{NodePlan, OwnedNode}; use nibble::{NibbleSlice, NibbleVec, nibble_ops}; #[cfg(feature = "std")] @@ -29,7 +28,7 @@ use alloc::boxed::Box; use alloc::vec::Vec; #[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq)] enum Status { Entering, At, @@ -41,21 +40,21 @@ enum Status { #[derive(Eq, PartialEq)] struct Crumb { hash: Option, - node: Rc, + node: Rc>, status: Status, } impl Crumb { /// Move on to next status in the node's sequence. fn increment(&mut self) { - self.status = match (&self.status, self.node.as_ref()) { - (&Status::Entering, &OwnedNode::Extension(..)) => Status::At, - (&Status::Entering, &OwnedNode::Branch(..)) - | (&Status::Entering, &OwnedNode::NibbledBranch(..)) => Status::At, - (&Status::At, &OwnedNode::Branch(..)) - | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), - (&Status::AtChild(x), &OwnedNode::Branch(..)) - | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) + self.status = match (self.status, self.node.node_plan()) { + (Status::Entering, NodePlan::Extension { .. }) => Status::At, + (Status::Entering, NodePlan::Branch { .. }) + | (Status::Entering, NodePlan::NibbledBranch { .. }) => Status::At, + (Status::At, NodePlan::Branch { .. }) + | (Status::At, NodePlan::NibbledBranch { .. }) => Status::AtChild(0), + (Status::AtChild(x), NodePlan::Branch { .. }) + | (Status::AtChild(x), NodePlan::NibbledBranch { .. }) if x < (nibble_ops::NIBBLE_LENGTH - 1) => Status::AtChild(x + 1), _ => Status::Exiting, } @@ -77,37 +76,51 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { trail: Vec::with_capacity(8), key_nibbles: NibbleVec::new(), }; - db.root_data().and_then(|root_data| { - r.descend(&root_data, Some(db.root().clone())).map(|_| ()) - })?; + let (root_node, root_hash) = db.get_raw_or_lookup(db.root().as_ref(), EMPTY_PREFIX)?; + r.descend(root_node, root_hash); Ok(r) } - fn seek( - &mut self, - mut node_data: DBValue, - mut node_hash: Option>, - key: NibbleSlice, - ) -> Result<(), TrieHash, CError> { + /// Descend into a payload. + fn descend(&mut self, node: OwnedNode, node_hash: Option>) { + self.trail.push(Crumb { + hash: node_hash, + status: Status::Entering, + node: Rc::new(node), + }); + } +} + +impl<'a, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, L> { + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + self.trail.clear(); + self.key_nibbles.clear(); + let key = NibbleSlice::new(key); + + let (mut node, mut node_hash) = + self.db.get_raw_or_lookup(self.db.root().as_ref(), EMPTY_PREFIX)?; let mut partial = key; let mut full_key_nibbles = 0; loop { - let (next_node_data, next_node_hash) = { - let node = self.descend(&node_data, node_hash)?; + let (next_node, next_node_hash) = { + self.descend(node, node_hash); let crumb = self.trail.last_mut() .expect( "descend_into_node pushes a crumb onto the trial; \ thus the trail is non-empty; qed" ); + let node_data = crumb.node.data(); - match node { - Node::Leaf(slice, _) => { + match crumb.node.node_plan() { + NodePlan::Leaf { partial: partial_plan, .. } => { + let slice = partial_plan.build(node_data); if slice < partial { crumb.status = Status::Exiting; } return Ok(()) }, - Node::Extension(slice, item) => { + NodePlan::Extension { partial: partial_plan, child } => { + let slice = partial_plan.build(node_data); if !partial.starts_with(&slice) { if slice < partial { crumb.status = Status::Exiting; @@ -122,9 +135,9 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { self.key_nibbles.append_partial(slice.right()); let prefix = key.back(full_key_nibbles); - self.db.get_raw_or_lookup(item, prefix.left())? + self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())? }, - Node::Branch(nodes, _) => { + NodePlan::Branch { value: _, children } => { if partial.is_empty() { return Ok(()) } @@ -133,17 +146,18 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { crumb.status = Status::AtChild(i as usize); self.key_nibbles.push(i); - if let Some(child) = nodes[i as usize] { + if let Some(child) = &children[i as usize] { full_key_nibbles += 1; partial = partial.mid(1); let prefix = key.back(full_key_nibbles); - self.db.get_raw_or_lookup(child, prefix.left())? + self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())? } else { return Ok(()) } }, - Node::NibbledBranch(slice, nodes, _) => { + NodePlan::NibbledBranch { partial: partial_plan, value: _, children } => { + let slice = partial_plan.build(node_data); if !partial.starts_with(&slice) { if slice < partial { crumb.status = Status::Exiting; @@ -165,17 +179,17 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { self.key_nibbles.append_partial(slice.right()); self.key_nibbles.push(i); - if let Some(child) = nodes[i as usize] { + if let Some(child) = &children[i as usize] { full_key_nibbles += 1; partial = partial.mid(1); let prefix = key.back(full_key_nibbles); - self.db.get_raw_or_lookup(child, prefix.left())? + self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())? } else { return Ok(()) } }, - Node::Empty => { + NodePlan::Empty => { if !partial.is_empty() { crumb.status = Status::Exiting; } @@ -184,93 +198,80 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { } }; - node_data = next_node_data; + node = next_node; node_hash = next_node_hash; } } - - /// Descend into a payload. - fn descend<'b, 'c>(&'b mut self, node_data: &'c [u8], node_hash: Option>) - -> Result, TrieHash, CError> - { - let node = L::Codec::decode(&node_data) - .map_err(|e| Box::new(TrieError::DecoderError(node_hash.unwrap_or_default(), e)))?; - self.trail.push(Crumb { - hash: node_hash, - status: Status::Entering, - node: Rc::new(node.clone().into()), - }); - Ok(node) - } -} - -impl<'a, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, L> { - fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { - self.trail.clear(); - self.key_nibbles.clear(); - let root_node = self.db.root_data()?; - let root_hash = self.db.root().clone(); - self.seek(root_node, Some(root_hash), NibbleSlice::new(key.as_ref())) - } } impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> { - type Item = Result<(NibbleVec, Option>, Rc), TrieHash, CError>; + type Item = Result<(NibbleVec, Option>, Rc>), TrieHash, CError>; fn next(&mut self) -> Option { enum IterStep { YieldNode, - Continue, PopTrail, - Descend(Result<(DBValue, Option), O, E>), + Continue, + Descend(Result<(OwnedNode, Option), O, E>), } loop { let iter_step = { - let b = self.trail.last()?; + let b = self.trail.last_mut()?; + let node_data = b.node.data(); - match (b.status.clone(), b.node.as_ref()) { + match (b.status, b.node.node_plan()) { (Status::Entering, _) => IterStep::YieldNode, - (Status::Exiting, n) => { - match *n { - OwnedNode::Empty | OwnedNode::Leaf(_, _) => {}, - OwnedNode::Extension(ref n, _) => - self.key_nibbles.drop_lasts(n.len()), - OwnedNode::Branch(_) => { self.key_nibbles.pop(); }, - OwnedNode::NibbledBranch(ref n, _) => - self.key_nibbles.drop_lasts(n.len() + 1), + (Status::Exiting, node) => { + match node { + NodePlan::Empty | NodePlan::Leaf { .. } => {}, + NodePlan::Extension { partial, .. } => { + self.key_nibbles.drop_lasts(partial.len()); + }, + NodePlan::Branch { .. } => { self.key_nibbles.pop(); }, + NodePlan::NibbledBranch { partial, .. } => { + self.key_nibbles.drop_lasts(partial.len() + 1); + }, } IterStep::PopTrail }, - (Status::At, &OwnedNode::Extension(ref partial, ref d)) => { - self.key_nibbles.append(partial); + (Status::At, NodePlan::Extension { partial: partial_plan, child }) => { + let partial = partial_plan.build(node_data); + self.key_nibbles.append_partial(partial.right()); IterStep::Descend::, CError>( - self.db.get_raw_or_lookup(&*d, self.key_nibbles.as_prefix()) + self.db.get_raw_or_lookup( + &node_data[child.clone()], + self.key_nibbles.as_prefix() + ) ) }, - (Status::At, &OwnedNode::Branch(_)) => { + (Status::At, NodePlan::Branch { .. }) => { self.key_nibbles.push(0); IterStep::Continue }, - (Status::At, &OwnedNode::NibbledBranch(ref partial, _)) => { - self.key_nibbles.append(partial); + (Status::At, NodePlan::NibbledBranch { partial: partial_plan, .. }) => { + let partial = partial_plan.build(node_data); + self.key_nibbles.append_partial(partial.right()); self.key_nibbles.push(0); IterStep::Continue }, - (Status::AtChild(i), &OwnedNode::Branch(ref branch)) - | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, ref branch)) => { - if let Some(child) = branch.index(i) { + (Status::AtChild(i), NodePlan::Branch { children, .. }) + | (Status::AtChild(i), NodePlan::NibbledBranch { children, .. }) => { + if let Some(child) = &children[i] { self.key_nibbles.pop(); self.key_nibbles.push(i as u8); IterStep::Descend::, CError>( - self.db.get_raw_or_lookup(child, self.key_nibbles.as_prefix()) + self.db.get_raw_or_lookup( + &node_data[child.clone()], + self.key_nibbles.as_prefix() + ) ) } else { IterStep::Continue } }, _ => panic!( - "Crumb::increment and TrieDBNodeIterator are implemented so that the above \ - arms are the only possible states" + "Crumb::increment and TrieDBNodeIterator are implemented so that \ + the above arms are the only possible states" ), } }; @@ -300,21 +301,19 @@ impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> { self.trail.last_mut()? .increment(); }, - IterStep::Descend::, CError>(next) => { - let node_result = next.and_then(|(node_data, node_hash)| { - self.descend(&node_data, node_hash).map(|_| ()) - }); - if let Err(err) = node_result { - // Increment here as there is an implicit PopTrail. - self.trail.last_mut() - .expect( - "method would have exited at top of previous block if trial were empty;\ + IterStep::Descend::, CError>(Ok((node, node_hash))) => { + self.descend(node, node_hash); + }, + IterStep::Descend::, CError>(Err(err)) => { + // Increment here as there is an implicit PopTrail. + self.trail.last_mut() + .expect( + "method would have exited at top of previous block if trial were empty;\ trial could not have been modified within the block since it was immutably borrowed;\ qed" - ) - .increment(); - return Some(Err(err)); - } + ) + .increment(); + return Some(Err(err)); }, IterStep::Continue => { self.trail.last_mut() @@ -338,7 +337,7 @@ mod tests { use reference_trie::{ RefTrieDB, RefTrieDBMut, TrieError, TrieMut, TrieIterator, TrieDBNodeIterator, NibbleSlice, NibbleVec, - node::OwnedNode, + node::Node, }; use reference_trie::{RefTrieDBNoExt, RefTrieDBMutNoExt}; @@ -397,9 +396,9 @@ mod tests { match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.as_ref() { - OwnedNode::Extension(partial, _) => - assert_eq!(*partial, nibble_vec(hex!("00"), 1)), + match node.node() { + Node::Extension(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), _ => panic!("unexpected node"), } } @@ -409,8 +408,8 @@ mod tests { match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!("00"), 1)); - match node.as_ref() { - OwnedNode::Branch(_) => {}, + match node.node() { + Node::Branch(_, _) => {}, _ => panic!("unexpected node"), } } @@ -420,8 +419,8 @@ mod tests { match iter.next() { Some(Ok((prefix, None, node))) => { assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.as_ref() { - OwnedNode::Branch(_) => {}, + match node.node() { + Node::Branch(_, _) => {}, _ => panic!("unexpected node"), } } @@ -431,9 +430,9 @@ mod tests { match iter.next() { Some(Ok((prefix, None, node))) => { assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); - match node.as_ref() { - OwnedNode::Leaf(partial, _) => - assert_eq!(*partial, nibble_vec(hex!("30"), 1)), + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), _ => panic!("unexpected node"), } } @@ -443,9 +442,9 @@ mod tests { match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!("02"), 2)); - match node.as_ref() { - OwnedNode::Leaf(partial, _) => - assert_eq!(*partial, nibble_vec(hex!(""), 0)), + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), _ => panic!("unexpected node"), } } @@ -470,9 +469,9 @@ mod tests { match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.as_ref() { - OwnedNode::NibbledBranch(partial, _) => - assert_eq!(*partial, nibble_vec(hex!("00"), 1)), + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), _ => panic!("unexpected node"), } } @@ -482,9 +481,9 @@ mod tests { match iter.next() { Some(Ok((prefix, None, node))) => { assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.as_ref() { - OwnedNode::NibbledBranch(partial, _) => - assert_eq!(*partial, nibble_vec(hex!(""), 0)), + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), _ => panic!("unexpected node"), } } @@ -494,21 +493,22 @@ mod tests { match iter.next() { Some(Ok((prefix, None, node))) => { assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); - match node.as_ref() { - OwnedNode::Leaf(partial, _) => - assert_eq!(*partial, nibble_vec(hex!("30"), 1)), + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), _ => panic!("unexpected node"), } } + _ => panic!("unexpected item"), } match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!("02"), 2)); - match node.as_ref() { - OwnedNode::Leaf(partial, _) => - assert_eq!(*partial, nibble_vec(hex!(""), 0)), + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), _ => panic!("unexpected node"), } } @@ -527,8 +527,8 @@ mod tests { match iter.next() { Some(Ok((prefix, Some(_), node))) => { assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.as_ref() { - OwnedNode::Empty => {}, + match node.node() { + Node::Empty => {}, _ => panic!("unexpected node"), } } @@ -637,8 +637,8 @@ mod tests { match iter.next() { Some(Ok((prefix, _, node))) => { assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.as_ref() { - OwnedNode::Empty => {}, + match node.node() { + Node::Empty => {}, _ => panic!("unexpected node"), } } @@ -667,8 +667,8 @@ mod tests { TrieIterator::seek(&mut iter, &hex!("02")[..]).unwrap(); match iter.next() { Some(Ok((_, Some(hash), node))) => { - match node.as_ref() { - OwnedNode::Leaf(_, _) => hash, + match node.node() { + Node::Leaf(_, _) => hash, _ => panic!("unexpected node"), } } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 416095e4..f126b4cc 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -13,11 +13,13 @@ // limitations under the License. use elastic_array::ElasticArray36; +use hash_db::Hasher; use nibble::NibbleSlice; use nibble::nibble_ops; -use nibble::NibbleVec; -use super::DBValue; +use node_codec::NodeCodec; +use core_::borrow::Borrow; +use core_::ops::Range; #[cfg(not(feature = "std"))] use alloc::vec::Vec; @@ -41,88 +43,132 @@ pub enum Node<'a> { /// Branch node with support for a nibble (when extension nodes are not used). 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))] + +/// A `NibbleSlicePlan` is a blueprint for decoding a nibble slice from a byte slice. The +/// `NibbleSlicePlan` is created by parsing a byte slice and can be reused multiple times. #[derive(Eq, PartialEq, Clone)] -pub struct Branch { - data: Vec, - ubounds: [usize; 18], - has_value: bool, +#[cfg_attr(feature = "std", derive(Debug))] +pub struct NibbleSlicePlan { + bytes: Range, + offset: 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(); +impl NibbleSlicePlan { + /// Construct a nibble slice decode plan. + pub fn new(bytes: Range, offset: usize) -> Self { + NibbleSlicePlan { + bytes, + offset } - 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.ubounds[16]..self.ubounds[17]]) - } else { - None - } + /// Returns the nibble length of the slice. + pub fn len(&self) -> usize { + (self.bytes.end - self.bytes.start) * nibble_ops::NIBBLE_PER_BYTE - self.offset } - /// Test if the node has a value. - pub fn has_value(&self) -> bool { - self.has_value + /// Build a nibble slice by decoding a byte slice according to the plan. It is the + /// responsibility of the caller to ensure that the node plan was created for the argument + /// data, otherwise the call decode incorrectly or panic. + pub fn build<'a, 'b>(&'a self, data: &'b [u8]) -> NibbleSlice<'b> { + NibbleSlice::new_offset(&data[self.bytes.clone()], self.offset) } +} - pub fn index(&self, index: usize) -> Option<&[u8]> { - assert!(index < 16); - if self.ubounds[index] == self.ubounds[index + 1] { - None - } else { - Some(&self.data[self.ubounds[index]..self.ubounds[index + 1]]) +/// A `NodePlan` is a blueprint for decoding a node from a byte slice. The `NodePlan` is created +/// by parsing an encoded node and can be reused multiple times. This is useful as a `Node` borrows +/// from a byte slice and this struct does not. +/// +/// The enum values mirror those of `Node` except that instead of byte slices, this struct stores +/// ranges that can be used to index into a large byte slice. +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum NodePlan { + /// Null trie node; could be an empty root or an empty branch entry. + Empty, + /// Leaf node; has a partial key plan and value. + Leaf { + partial: NibbleSlicePlan, + value: Range, + }, + /// Extension node; has a partial key plan and child data. + Extension { + partial: NibbleSlicePlan, + child: Range, + }, + /// Branch node; has slice of child nodes (each possibly null) + /// and an optional immediate node data. + Branch { + value: Option>, + children: [Option>; nibble_ops::NIBBLE_LENGTH], + }, + /// Branch node with support for a nibble (when extension nodes are not used). + NibbledBranch { + partial: NibbleSlicePlan, + value: Option>, + children: [Option>; nibble_ops::NIBBLE_LENGTH], + }, +} + +impl NodePlan { + /// Build a node by decoding a byte slice according to the node plan. It is the responsibility + /// of the caller to ensure that the node plan was created for the argument data, otherwise the + /// call decode incorrectly or panic. + pub fn build<'a, 'b>(&'a self, data: &'b [u8]) -> Node<'b> { + match self { + NodePlan::Empty => Node::Empty, + NodePlan::Leaf { partial, value } => + Node::Leaf(partial.build(data), &data[value.clone()]), + NodePlan::Extension { partial, child } => + Node::Extension(partial.build(data), &data[child.clone()]), + NodePlan::Branch { value, children } => { + let mut child_slices = [None; nibble_ops::NIBBLE_LENGTH]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].clone().map(|child| &data[child]); + } + let value_slice = value.clone().map(|value| &data[value]); + Node::Branch(child_slices, value_slice) + }, + NodePlan::NibbledBranch { partial, value, children } => { + let mut child_slices = [None; nibble_ops::NIBBLE_LENGTH]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].clone().map(|child| &data[child]); + } + let value_slice = value.clone().map(|value| &data[value]); + Node::NibbledBranch(partial.build(data), child_slices, value_slice) + }, } } } -/// An owning node type. Useful for trie iterators. +/// An `OwnedNode` is an owned type from which a `Node` can be constructed which borrows data from +/// the `OwnedNode`. This is useful for trie iterators. #[cfg_attr(feature = "std", derive(Debug))] #[derive(PartialEq, Eq)] -pub enum OwnedNode { - /// Empty trie node. - Empty, - /// Leaf node: partial key and value. - Leaf(NibbleVec, DBValue), - /// Extension node: partial key and child node. - Extension(NibbleVec, DBValue), - /// Branch node: children and an optional value. - Branch(Branch), - /// Branch node: children and an optional value. - NibbledBranch(NibbleVec, Branch), +pub struct OwnedNode> { + data: D, + plan: NodePlan, } -impl<'a> From> for OwnedNode { - fn from(node: Node<'a>) -> 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)), - } +impl> OwnedNode { + /// Construct an `OwnedNode` by decoding an owned data source according to some codec. + pub fn new>(data: D) -> Result { + let plan = C::decode_plan(data.borrow())?; + Ok(OwnedNode { data, plan }) + } + + /// Returns a reference to the backing data. + pub fn data(&self) -> &[u8] { + self.data.borrow() + } + + /// Returns a reference to the node decode plan. + pub fn node_plan(&self) -> &NodePlan { + &self.plan + } + + /// Construct a `Node` by borrowing data from this struct. + pub fn node(&self) -> Node { + self.plan.build(self.data.borrow()) } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index afa8cb20..1bab5f55 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -16,7 +16,7 @@ //! to parametrize the hashes used in the codec. use hash_db::Hasher; -use node::Node; +use node::{Node, NodePlan}; use ChildReference; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -51,8 +51,13 @@ pub trait NodeCodec: Sized { /// Get the hashed null node. fn hashed_null_node() -> H::Out; + /// Decode bytes to a `NodePlan`. Returns `Self::E` on failure. + fn decode_plan(data: &[u8]) -> Result; + /// Decode bytes to a `Node`. Returns `Self::E` on failure. - fn decode(data: &[u8]) -> Result; + fn decode(data: &[u8]) -> Result { + Ok(Self::decode_plan(data)?.build(data)) + } /// 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/triedb.rs b/trie-db/src/triedb.rs index 63ce42d7..e17bb470 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -90,17 +90,9 @@ where /// Get the backing database. pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } - /// Get the data of the root node. - pub fn root_data(&self) -> Result, CError> { - self.db - .get(self.root, EMPTY_PREFIX) - .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) - } - /// 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 - /// is known to be literal. + /// but may require a database lookup. /// /// Return value is the node data and the node hash if the value was looked up in the database /// or None if it was returned raw. @@ -109,16 +101,24 @@ where pub(crate) fn get_raw_or_lookup( &self, node: &[u8], partial_key: Prefix, - ) -> Result<(DBValue, Option>), TrieHash, CError> { - match (partial_key.0.is_empty() && partial_key.1.is_none(), L::Codec::try_decode_hash(node)) { - (false, Some(key)) => { - let data = self.db - .get(&key, partial_key) - .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))?; - Ok((data, Some(key))) - } - _ => Ok((DBValue::from_slice(node), None)), - } + ) -> Result<(OwnedNode, Option>), TrieHash, CError> { + let node_hash = L::Codec::try_decode_hash(node); + let node_data = if let Some(key) = node_hash { + self.db + .get(&key, partial_key) + .ok_or_else(|| { + if partial_key == EMPTY_PREFIX { + Box::new(TrieError::InvalidStateRoot(key)) + } else { + Box::new(TrieError::IncompleteDatabase(key)) + } + })? + } else { + DBValue::from_slice(node) + }; + let owned_node = OwnedNode::new::(node_data) + .map_err(|e| Box::new(TrieError::DecoderError(node_hash.unwrap_or_default(), e)))?; + Ok((owned_node, node_hash)) } } @@ -170,9 +170,9 @@ where L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok((node, _inline)) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { - match L::Codec::decode(&node) { - Ok(Node::Leaf(slice, value)) => + match self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { + Ok((owned_node, _node_hash)) => match owned_node.node() { + Node::Leaf(slice, value) => match (f.debug_struct("Node::Leaf"), self.index) { (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, @@ -180,13 +180,13 @@ where .field("slice", &slice) .field("value", &value) .finish(), - Ok(Node::Extension(slice, item)) => + Node::Extension(slice, item) => match (f.debug_struct("Node::Extension"), self.index) { (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, } .field("slice", &slice) - .field("item", &TrieAwareDebugNode{ + .field("item", &TrieAwareDebugNode { trie: self.trie, node_key: item, partial_key: self.partial_key @@ -194,7 +194,7 @@ where index: None, }) .finish(), - Ok(Node::Branch(ref nodes, ref value)) => { + Node::Branch(ref nodes, ref value) => { let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) @@ -214,7 +214,7 @@ where .field("value", &value) .finish() }, - Ok(Node::NibbledBranch(slice, nodes, value)) => { + Node::NibbledBranch(slice, nodes, value) => { let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) @@ -234,20 +234,13 @@ where .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.node_key) - .field("error", &format!("ERROR decoding node branch Rlp: {}", e)) - .finish() - } - } else { - f.debug_struct("BROKEN_NODE") + Node::Empty => f.debug_struct("Node::Empty").finish(), + }, + Err(e) => f.debug_struct("BROKEN_NODE") .field("index", &self.index) .field("key", &self.node_key) - .field("error", &"Not found") - .finish() + .field("error", &format!("ERROR fetching node: {}", e)) + .finish(), } } } @@ -258,12 +251,11 @@ where L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let root_rlp = self.root_data().unwrap(); f.debug_struct("TrieDB") .field("hash_count", &self.hash_count) .field("root", &TrieAwareDebugNode { trie: self, - node_key: &root_rlp[..], + node_key: self.root().as_ref(), partial_key: NibbleVec::new(), index: None, }) @@ -298,16 +290,15 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { while let Some(item) = self.inner.next() { match item { Ok((mut prefix, _, node)) => { - let maybe_value = match node.as_ref() { - &OwnedNode::Leaf(ref partial, ref value) => { - prefix.append(partial); - Some(value.as_ref()) + let maybe_value = match node.node() { + Node::Leaf(partial, value) => { + prefix.append_partial(partial.right()); + Some(value) } - &OwnedNode::Branch(ref branch) => - branch.get_value(), - &OwnedNode::NibbledBranch(ref partial, ref branch) => { - prefix.append(partial); - branch.get_value() + Node::Branch(_, value) => value, + Node::NibbledBranch(partial, _, value) => { + prefix.append_partial(partial.right()); + value } _ => None, };