@@ -33,10 +33,10 @@ bitflags! {
3333 /// action.
3434 const MARKED = 0b00000010 ;
3535
36- /// A leaf with `REFERENCE` retention will not be considered prunable until the `REFERENCE`
37- /// flag is removed from the leaf. The `REFERENCE` flag will be removed at any point that
38- /// the leaf is overwritten without `REFERENCE` retention, and `REFERENCE` retention cannot
39- /// be added to an existing leaf .
36+ /// A left-hand leaf with `REFERENCE` retention will not be considered prunable until the
37+ /// corresponding right-hand sibling is prunable. When a `REFERENCE` node becomes prunable,
38+ /// the `REFERENCE` retention is propagated to the parent node that replaces the
39+ /// `REFERENCE` node and its sibling .
4040 const REFERENCE = 0b00000100 ;
4141 }
4242}
@@ -263,11 +263,26 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
263263 ann,
264264 left. as_ref ( ) . clone ( ) . prune ( level - 1 ) ,
265265 right. as_ref ( ) . clone ( ) . prune ( level - 1 ) ,
266+ true ,
266267 ) ,
267268 other => other,
268269 }
269270 }
270271
272+ fn is_right_reference_frontier ( & self ) -> bool {
273+ fn go < H : Hashable + Clone + PartialEq > (
274+ tree : & Tree < Option < Arc < H > > , ( H , RetentionFlags ) > ,
275+ ) -> bool {
276+ match & tree. 0 {
277+ Node :: Parent { left, right, .. } => go ( right) && left. is_nil ( ) ,
278+ Node :: Leaf { value : ( _, flags) } => * flags == RetentionFlags :: REFERENCE ,
279+ Node :: Nil => false ,
280+ }
281+ }
282+
283+ go ( self )
284+ }
285+
271286 /// Merge two subtrees having the same root address.
272287 ///
273288 /// The merge operation is checked to be strictly additive and returns an error if merging
@@ -287,22 +302,31 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
287302 let no_default_fill = addr. position_range_end ( ) ;
288303 match ( t0, t1) {
289304 ( Tree ( Node :: Nil ) , other) | ( other, Tree ( Node :: Nil ) ) => Ok ( other) ,
290- ( Tree ( Node :: Leaf { value : vl } ) , Tree ( Node :: Leaf { value : vr } ) ) => {
291- if vl. 0 == vr. 0 {
305+ ( Tree ( Node :: Leaf { value : ( vl , fl ) } ) , Tree ( Node :: Leaf { value : ( vr , fr ) } ) ) => {
306+ if vl == vr {
292307 // Merge the flags together.
293- Ok ( Tree :: leaf ( ( vl. 0 , vl . 1 | vr . 1 ) ) )
308+ Ok ( Tree :: leaf ( ( vl, fl | fr ) ) )
294309 } else {
295- trace ! ( left = ?vl. 0 , right = ?vr. 0 , "Merge conflict for leaves" ) ;
310+ trace ! ( left = ?vl, right = ?vr, "Merge conflict for leaves" ) ;
296311 Err ( MergeError :: Conflict ( addr) )
297312 }
298313 }
299- ( Tree ( Node :: Leaf { value } ) , parent @ Tree ( Node :: Parent { .. } ) )
300- | ( parent @ Tree ( Node :: Parent { .. } ) , Tree ( Node :: Leaf { value } ) ) => {
314+ ( Tree ( Node :: Leaf { value : ( v , flags ) } ) , parent @ Tree ( Node :: Parent { .. } ) )
315+ | ( parent @ Tree ( Node :: Parent { .. } ) , Tree ( Node :: Leaf { value : ( v , flags ) } ) ) => {
301316 let parent_hash = parent. root_hash ( addr, no_default_fill) ;
302- if parent_hash. iter ( ) . all ( |r| r == & value. 0 ) {
303- Ok ( parent. reannotate_root ( Some ( Arc :: new ( value. 0 ) ) ) )
317+ if parent_hash. iter ( ) . all ( |r| r == & v) {
318+ if parent. is_right_reference_frontier ( ) {
319+ // Since the `Parent` node being merged with the leaf is is a reference
320+ // frontier with only right-hand nodes, and is therefore only required
321+ // in the tree for making witnesses for nodes to the right of this
322+ // node, we can simply update the current leaf by adding the
323+ // `REFERENCE` flag to its current retention flags.
324+ Ok ( Tree :: leaf ( ( v, flags | RetentionFlags :: REFERENCE ) ) )
325+ } else {
326+ Ok ( parent. reannotate_root ( Some ( Arc :: new ( v) ) ) )
327+ }
304328 } else {
305- trace ! ( leaf = ?value , node = ?parent_hash, "Merge conflict for leaf into node" ) ;
329+ trace ! ( leaf = ?( v , flags ) , node = ?parent_hash, "Merge conflict for leaf into node" ) ;
306330 Err ( MergeError :: Conflict ( addr) )
307331 }
308332 }
@@ -335,6 +359,7 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
335359 lann. or ( rann) ,
336360 go ( l_addr, ll. as_ref ( ) . clone ( ) , rl. as_ref ( ) . clone ( ) ) ?,
337361 go ( r_addr, lr. as_ref ( ) . clone ( ) , rr. as_ref ( ) . clone ( ) ) ?,
362+ false ,
338363 ) )
339364 } else {
340365 unreachable ! ( )
@@ -350,22 +375,32 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
350375 go ( root_addr, self , other)
351376 }
352377
353- /// Unite two nodes by either constructing a new parent node, or, if both nodes are ephemeral
354- /// leaves or Nil, constructing a replacement parent by hashing leaf values together (or a
355- /// replacement `Nil` value).
378+ /// Unite two nodes by either constructing a new parent node with the supplied nodes as
379+ /// children, or, if both nodes are ephemeral leaves or Nil, constructing a replacement parent
380+ /// by hashing leaf values together (or a replacement `Nil` value).
356381 ///
357- /// `level` must be the level of the two nodes that are being joined.
358- pub ( crate ) fn unite ( level : Level , ann : Option < Arc < H > > , left : Self , right : Self ) -> Self {
382+ /// - `level`: the level of the two nodes that are being joined.
383+ /// - `pruning`: should be set to `true` in the case that left-hand `REFERENCE` nodes may be
384+ /// pruned so long as their right-hand sibling is not `MARKED`.
385+ pub ( crate ) fn unite (
386+ level : Level ,
387+ ann : Option < Arc < H > > ,
388+ left : Self ,
389+ right : Self ,
390+ pruning : bool ,
391+ ) -> Self {
359392 match ( left, right) {
360393 ( Tree ( Node :: Nil ) , Tree ( Node :: Nil ) ) if ann. is_none ( ) => Tree :: empty ( ) ,
361- ( Tree ( Node :: Leaf { value : lv } ) , Tree ( Node :: Leaf { value : rv } ) )
362- // we can prune right-hand leaves that are not marked or reference leaves; if a
363- // leaf is a checkpoint then that information will be propagated to the replacement
364- // leaf
365- if lv. 1 == RetentionFlags :: EPHEMERAL &&
366- ( rv. 1 & ( RetentionFlags :: MARKED | RetentionFlags :: REFERENCE ) ) == RetentionFlags :: EPHEMERAL =>
394+ ( Tree ( Node :: Leaf { value : ( lv, lf) } ) , Tree ( Node :: Leaf { value : ( rv, rf) } ) )
395+ if ( lf == RetentionFlags :: EPHEMERAL
396+ || ( pruning
397+ && ( lf | RetentionFlags :: REFERENCE ) == RetentionFlags :: REFERENCE ) )
398+ && ( rf & RetentionFlags :: MARKED ) == RetentionFlags :: EPHEMERAL =>
367399 {
368- Tree :: leaf ( ( H :: combine ( level, & lv. 0 , & rv. 0 ) , rv. 1 ) )
400+ // We can prune if the right-hand leaf is not a marked leaf, and the left-hand leaf
401+ // has `EPHEMERAL` retention. `REFERENCE` and `CHECKPOINT` reference flags will be
402+ // propagated to the replacement leaf.
403+ Tree :: leaf ( ( H :: combine ( level, & lv, & rv) , lf | rf) )
369404 }
370405 ( left, right) => Tree :: parent ( ann, left, right) ,
371406 }
@@ -615,14 +650,20 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
615650 // to the left to truncate the left child and then reconstruct the
616651 // node with `Nil` as the right sibling
617652 go ( position, l_child, left. as_ref ( ) ) . map ( |left| {
618- Tree :: unite ( l_child. level ( ) , ann. clone ( ) , left, Tree :: empty ( ) )
653+ Tree :: unite ( l_child. level ( ) , ann. clone ( ) , left, Tree :: empty ( ) , false )
619654 } )
620655 } else {
621656 // we are truncating within the range of the right node, so recurse
622657 // to the right to truncate the right child and then reconstruct the
623658 // node with the left sibling unchanged
624659 go ( position, r_child, right. as_ref ( ) ) . map ( |right| {
625- Tree :: unite ( r_child. level ( ) , ann. clone ( ) , left. as_ref ( ) . clone ( ) , right)
660+ Tree :: unite (
661+ r_child. level ( ) ,
662+ ann. clone ( ) ,
663+ left. as_ref ( ) . clone ( ) ,
664+ right,
665+ false ,
666+ )
626667 } )
627668 }
628669 }
@@ -651,7 +692,10 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
651692 /// to fill out the tree.
652693 ///
653694 /// In the case that a leaf node would be replaced by an incomplete subtree, the resulting
654- /// parent node will be annotated with the existing leaf value.
695+ /// parent node will be annotated with the existing leaf value, unless all of the leaves of the
696+ /// inserted tree are `REFERENCE` nodes and the inserted subtree corresponds to the right-hand
697+ /// edge of this tree; in this case, the existing leaf is simply reannotated with `REFERENCE`
698+ /// retention.
655699 ///
656700 /// Returns the updated tree, along with the addresses of any [`Node::Nil`] nodes that were
657701 /// inserted in the process of creating the parent nodes down to the insertion point, or an
@@ -716,37 +760,46 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
716760 if subtree. root . has_computable_root ( ) {
717761 Ok ( (
718762 if subtree. root . is_leaf ( ) {
719- // When replacing a leaf with a leaf, `REFERENCE` retention
720- // will be discarded unless both leaves have `REFERENCE`
721- // retention.
763+ // When replacing a leaf with a leaf, the retention flags
764+ // from both leaves are combined.
722765 subtree
723766 . root
724767 . try_map :: < ( H , RetentionFlags ) , InsertionError , _ > (
725768 & |( v0, ret0) | {
726769 if v0 == value {
727- let retention_result: RetentionFlags =
728- ( ( * retention | * ret0)
729- - RetentionFlags :: REFERENCE )
730- | ( RetentionFlags :: REFERENCE
731- & * retention
732- & * ret0) ;
733- Ok ( ( value. clone ( ) , retention_result) )
770+ Ok ( ( value. clone ( ) , * retention | * ret0) )
734771 } else {
735772 Err ( InsertionError :: Conflict ( root_addr) )
736773 }
737774 } ,
738775 ) ?
739776 } else {
740777 // It is safe to replace the existing root unannotated, because we
741- // can always recompute the root from the subtree.
778+ // can always recompute the root from the subtree; we
779+ // checked that the subtree has a computable root above.
742780 subtree. root
743781 } ,
744782 vec ! [ ] ,
745783 ) )
746784 } else if subtree. root . node_value ( ) . iter ( ) . all ( |v| v == & value) {
747785 Ok ( (
748- // at this point we statically know the root to be a parent
749- subtree. root . reannotate_root ( Some ( Arc :: new ( value. clone ( ) ) ) ) ,
786+ // At this point we know the root to be a parent. If the
787+ // inserted subtree is a reference frontier with only
788+ // right-hand nodes, then we can simply reannotate the current
789+ // leaf by adding the `REFERENCE` flag to its current retention
790+ // flags.
791+ if subtree. root ( ) . is_right_reference_frontier ( )
792+ && subtree. max_position ( ) == Some ( root_addr. max_position ( ) )
793+ {
794+ Tree :: leaf ( (
795+ value. clone ( ) ,
796+ * retention | RetentionFlags :: REFERENCE ,
797+ ) )
798+ } else {
799+ // Otherwise, we replace the node with the subtree,
800+ // reannotated to its value.
801+ subtree. root . reannotate_root ( Some ( Arc :: new ( value. clone ( ) ) ) )
802+ } ,
750803 vec ! [ ] ,
751804 ) )
752805 } else {
@@ -785,6 +838,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
785838 ann. clone ( ) ,
786839 new_left,
787840 right. as_ref ( ) . clone ( ) ,
841+ false ,
788842 ) ,
789843 incomplete,
790844 ) )
@@ -797,6 +851,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
797851 ann. clone ( ) ,
798852 left. as_ref ( ) . clone ( ) ,
799853 new_right,
854+ false ,
800855 ) ,
801856 incomplete,
802857 ) )
@@ -1001,6 +1056,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
10011056 ann. clone ( ) ,
10021057 go ( & to_clear[ 0 ..p] , l_addr, left) ,
10031058 go ( & to_clear[ p..] , r_addr, right) ,
1059+ false ,
10041060 )
10051061 }
10061062 Node :: Leaf { value : ( h, r) } => {
@@ -1214,6 +1270,21 @@ mod tests {
12141270 ) ;
12151271 }
12161272
1273+ #[ test]
1274+ fn is_right_reference_frontier ( ) {
1275+ let t = parent (
1276+ leaf ( ( "a" . to_string ( ) , RetentionFlags :: EPHEMERAL ) ) ,
1277+ parent ( nil ( ) , leaf ( ( "b" . to_string ( ) , RetentionFlags :: REFERENCE ) ) ) ,
1278+ ) ;
1279+ assert ! ( !t. is_right_reference_frontier( ) ) ;
1280+
1281+ let t = parent (
1282+ nil ( ) ,
1283+ parent ( nil ( ) , leaf ( ( "b" . to_string ( ) , RetentionFlags :: REFERENCE ) ) ) ,
1284+ ) ;
1285+ assert ! ( t. is_right_reference_frontier( ) ) ;
1286+ }
1287+
12171288 #[ test]
12181289 fn located_insert_subtree ( ) {
12191290 let t: LocatedPrunableTree < String > = LocatedTree {
0 commit comments