@@ -261,56 +261,29 @@ impl<const N: usize> ProllyNodeBuilder<N> {
261
261
}
262
262
}
263
263
264
- impl < const N : usize > ProllyNode < N > {
265
- pub fn init_root ( key : Vec < u8 > , value : Vec < u8 > ) -> Self {
266
- ProllyNode {
267
- keys : vec ! [ key] ,
268
- values : vec ! [ value] ,
269
- is_leaf : true ,
270
- level : INIT_LEVEL ,
271
- ..Default :: default ( )
272
- }
273
- }
274
-
275
- pub fn builder ( ) -> ProllyNodeBuilder < N > {
276
- ProllyNodeBuilder :: default ( )
277
- }
278
-
279
- pub fn formatted_traverse_3 < F > ( & self , storage : & impl NodeStorage < N > , formatter : F ) -> String
280
- where
281
- F : Fn ( & ProllyNode < N > , & str , bool ) -> String ,
282
- {
283
- fn traverse_node < const N : usize , S : NodeStorage < N > , F > (
284
- node : & ProllyNode < N > ,
285
- storage : & S ,
286
- formatter : & F ,
287
- prefix : & str ,
288
- is_last : bool ,
289
- output : & mut String ,
290
- ) where
291
- F : Fn ( & ProllyNode < N > , & str , bool ) -> String ,
292
- {
293
- * output += & formatter ( node, prefix, is_last) ;
264
+ /// Trait for balancing nodes in the tree.
265
+ /// This trait provides methods for splitting and merging nodes to maintain tree balance.
266
+ trait Balanced < const N : usize > {
267
+ /// Balances the node by splitting or merging it as needed.
268
+ fn balance < S : NodeStorage < N > > (
269
+ & mut self ,
270
+ storage : & mut S ,
271
+ is_root_node : bool ,
272
+ path_hashes : & [ ValueDigest < N > ] ,
273
+ ) ;
294
274
295
- let new_prefix = format ! ( "{}{}" , prefix, if is_last { " " } else { "│ " } ) ;
296
- let children = node. children ( storage) ;
297
- for ( i, child) in children. iter ( ) . enumerate ( ) {
298
- traverse_node (
299
- child,
300
- storage,
301
- formatter,
302
- & new_prefix,
303
- i == children. len ( ) - 1 ,
304
- output,
305
- ) ;
306
- }
307
- }
275
+ /// Gets the hash of the next sibling of the node.
276
+ fn get_next_sibling_hash < S : NodeStorage < N > > (
277
+ & self ,
278
+ storage : & S ,
279
+ path_hashes : & [ ValueDigest < N > ] ,
280
+ ) -> Option < Vec < u8 > > ;
308
281
309
- let mut output = String :: new ( ) ;
310
- traverse_node ( self , storage, & formatter, "" , true , & mut output) ;
311
- output
312
- }
282
+ /// Merges the node with its next sibling.
283
+ fn merge_with_next_sibling ( & mut self , next_sibling : & mut ProllyNode < N > ) ;
284
+ }
313
285
286
+ impl < const N : usize > Balanced < N > for ProllyNode < N > {
314
287
/// Attempts to balance the node by merging the next (right) neighbor
315
288
/// and then splitting it into smaller nodes if necessary.
316
289
fn balance < S : NodeStorage < N > > (
@@ -338,6 +311,9 @@ impl<const N: usize> ProllyNode<N> {
338
311
}
339
312
340
313
// Use chunk_content to determine split points
314
+ if self . keys . len ( ) < self . min_chunk_size {
315
+ return ;
316
+ }
341
317
let chunks = self . chunk_content ( ) ;
342
318
if chunks. len ( ) <= 1 {
343
319
// do not need to split the node
@@ -465,8 +441,62 @@ impl<const N: usize> ProllyNode<N> {
465
441
}
466
442
}
467
443
444
+ impl < const N : usize > ProllyNode < N > {
445
+ pub fn init_root ( key : Vec < u8 > , value : Vec < u8 > ) -> Self {
446
+ ProllyNode {
447
+ keys : vec ! [ key] ,
448
+ values : vec ! [ value] ,
449
+ is_leaf : true ,
450
+ level : INIT_LEVEL ,
451
+ ..Default :: default ( )
452
+ }
453
+ }
454
+
455
+ pub fn builder ( ) -> ProllyNodeBuilder < N > {
456
+ ProllyNodeBuilder :: default ( )
457
+ }
458
+
459
+ pub fn formatted_traverse_3 < F > ( & self , storage : & impl NodeStorage < N > , formatter : F ) -> String
460
+ where
461
+ F : Fn ( & ProllyNode < N > , & str , bool ) -> String ,
462
+ {
463
+ fn traverse_node < const N : usize , S : NodeStorage < N > , F > (
464
+ node : & ProllyNode < N > ,
465
+ storage : & S ,
466
+ formatter : & F ,
467
+ prefix : & str ,
468
+ is_last : bool ,
469
+ output : & mut String ,
470
+ ) where
471
+ F : Fn ( & ProllyNode < N > , & str , bool ) -> String ,
472
+ {
473
+ * output += & formatter ( node, prefix, is_last) ;
474
+
475
+ let new_prefix = format ! ( "{}{}" , prefix, if is_last { " " } else { "│ " } ) ;
476
+ let children = node. children ( storage) ;
477
+ for ( i, child) in children. iter ( ) . enumerate ( ) {
478
+ traverse_node (
479
+ child,
480
+ storage,
481
+ formatter,
482
+ & new_prefix,
483
+ i == children. len ( ) - 1 ,
484
+ output,
485
+ ) ;
486
+ }
487
+ }
488
+
489
+ let mut output = String :: new ( ) ;
490
+ traverse_node ( self , storage, & formatter, "" , true , & mut output) ;
491
+ output
492
+ }
493
+ }
494
+
468
495
impl < const N : usize > NodeChunk for ProllyNode < N > {
469
496
fn chunk_content ( & self ) -> Vec < ( usize , usize ) > {
497
+ if self . keys . len ( ) < self . min_chunk_size {
498
+ return Vec :: new ( ) ;
499
+ }
470
500
let mut chunks = Vec :: new ( ) ;
471
501
let mut start = 0 ;
472
502
let mut last_start = 0 ;
@@ -688,7 +718,7 @@ impl<const N: usize> Node<N> for ProllyNode<N> {
688
718
}
689
719
} else {
690
720
// Handle the case when the child node is not found
691
- println ! ( "Child node not found: {:?}" , child_hash ) ;
721
+ println ! ( "Child node not found: {child_hash :?}" ) ;
692
722
}
693
723
694
724
// Sort the keys and balance the node
@@ -815,7 +845,7 @@ impl<const N: usize> Node<N> for ProllyNode<N> {
815
845
true
816
846
} else {
817
847
// Handle the case when the child node is not found
818
- println ! ( "Child node not found: {:?}" , child_hash ) ;
848
+ println ! ( "Child node not found: {child_hash :?}" ) ;
819
849
false
820
850
}
821
851
}
@@ -869,7 +899,7 @@ impl<const N: usize> Node<N> for ProllyNode<N> {
869
899
. iter ( )
870
900
. map ( |key| {
871
901
key. iter ( )
872
- . map ( |byte| format ! ( "{:0}" , byte ) )
902
+ . map ( |byte| format ! ( "{byte :0}" ) )
873
903
. collect :: < Vec < String > > ( )
874
904
. join ( " " )
875
905
} )
@@ -886,16 +916,15 @@ impl<const N: usize> Node<N> for ProllyNode<N> {
886
916
)
887
917
} else {
888
918
format ! (
889
- "{}{}#({} \x1B [31m0x{ :?}\x1B [0m )[{}]\n " ,
919
+ "{}{}#({:?})[{}]\n " ,
890
920
prefix,
891
921
if is_last { "└── " } else { "├── " } ,
892
- "" ,
893
922
hash,
894
923
keys_str
895
924
)
896
925
}
897
926
} ) ;
898
- println ! ( "{}" , output ) ;
927
+ println ! ( "{output}" ) ;
899
928
println ! ( "Note: #[keys] indicates internal node, [keys] indicates leaf node" ) ;
900
929
}
901
930
}
@@ -962,6 +991,7 @@ impl<const N: usize> ProllyNode<N> {
962
991
/// * `storage` - The storage implementation to retrieve child nodes.
963
992
/// * `formatter` - A closure that takes a reference to a node and returns a string representation of the node.
964
993
///
994
+ ///
965
995
/// # Returns
966
996
/// A string representation of the tree nodes in a breadth-first order.
967
997
pub fn formatted_traverse < F > ( & self , storage : & impl NodeStorage < N > , formatter : F ) -> String
@@ -1471,4 +1501,43 @@ mod tests {
1471
1501
// Print chunk content
1472
1502
println ! ( "{:?}" , node. chunk_content( ) ) ;
1473
1503
}
1504
+
1505
+ /// This test verifies the balancing of the tree after multiple insertions.
1506
+ /// The test checks the tree structure and ensures that the root node is split correctly
1507
+ /// and the keys are promoted to the parent node.
1508
+ #[ test]
1509
+ fn test_balance_after_insertions ( ) {
1510
+ let mut storage = InMemoryNodeStorage :: < 32 > :: default ( ) ;
1511
+ let value_for_all = vec ! [ 100 ] ;
1512
+
1513
+ // Initialize the prolly tree with a small chunk size to trigger splits
1514
+ let mut node: ProllyNode < 32 > = ProllyNode :: builder ( )
1515
+ . pattern ( 0b1 )
1516
+ . min_chunk_size ( 4 )
1517
+ . max_chunk_size ( 8 )
1518
+ . build ( ) ;
1519
+
1520
+ // Insert key-value pairs to trigger a split
1521
+ for i in 0 ..=10 {
1522
+ node. insert ( vec ! [ i] , value_for_all. clone ( ) , & mut storage, Vec :: new ( ) ) ;
1523
+ storage. insert_node ( node. get_hash ( ) , node. clone ( ) ) ;
1524
+ }
1525
+
1526
+ // After 11 insertions, the root should not be a leaf node
1527
+ assert ! ( !node. is_leaf) ;
1528
+
1529
+ // Check that all keys can be found
1530
+ for i in 0 ..=10 {
1531
+ assert ! ( node. find( & [ i] , & storage) . is_some( ) ) ;
1532
+ }
1533
+
1534
+ // Insert one more key to trigger another split
1535
+ node. insert ( vec ! [ 11 ] , value_for_all. clone ( ) , & mut storage, Vec :: new ( ) ) ;
1536
+ storage. insert_node ( node. get_hash ( ) , node. clone ( ) ) ;
1537
+
1538
+ // Check that all keys can still be found
1539
+ for i in 0 ..=11 {
1540
+ assert ! ( node. find( & [ i] , & storage) . is_some( ) ) ;
1541
+ }
1542
+ }
1474
1543
}
0 commit comments