11//! Bulk-load: build a dense tree from sorted pairs without `CoW` overhead.
22
3+ use std:: sync:: Arc ;
4+
35use crate :: Result ;
46use crate :: errors:: PagedbError ;
57use crate :: vfs:: Vfs ;
68
79use crate :: btree:: leaf:: { Leaf , LeafValue } ;
810use crate :: btree:: node;
11+ use crate :: btree:: overflow;
912
1013use super :: core:: BTree ;
1114
1215impl < V : Vfs > BTree < V > {
1316 /// Bulk-load sorted `(key, value)` pairs into a freshly-created tree
1417 /// without any `CoW` overhead. The tree must be empty (`root_page_id == 0`).
1518 ///
16- /// All records are first placed into as few leaves as needed, then internal
17- /// nodes are built bottom-up. Each page is written exactly once; no freed
18- /// pages are generated. The resulting layout is dense and compact.
19- ///
20- /// Overflow values are NOT supported in bulk-load; callers must ensure all
21- /// values fit inline (`value.len()` ≤ `page_size` / 4). For compaction use-
22- /// cases, the pairs come from `collect_range` which already resolves
23- /// overflow chains into inline bytes.
19+ /// Values larger than the inline threshold (`page_size / 4`) are spilled to
20+ /// overflow chains, exactly as the `put` path does, so a dense repack
21+ /// reproduces the original storage shape. Records are then packed into as few
22+ /// leaves as needed and internal nodes are built bottom-up. Each leaf/internal
23+ /// page is written exactly once; the layout is dense and compact.
2424 ///
2525 /// Returns `Err` if the tree is not empty.
2626 #[ allow( clippy:: too_many_lines) ]
@@ -34,82 +34,70 @@ impl<V: Vfs> BTree<V> {
3434 return Ok ( ( ) ) ;
3535 }
3636
37- // Build leaves greedily: pack as many records as fit into each leaf.
38- // No sibling pointers yet — we'll wire them up at the end.
39- let mut leaves: Vec < ( u64 , Vec < u8 > ) > = Vec :: new ( ) ; // (page_id, first_key)
40-
41- let body_cap = node:: body_capacity ( self . page_size ) ;
42- let mut current_leaf = Leaf :: new ( ) ;
37+ // Resolve each value to its stored form, spilling values past the inline
38+ // threshold to overflow chains (same threshold as `put`). Inlining an
39+ // oversized value would exceed leaf capacity and fail the encode.
40+ let realm = self . realm_id ;
41+ let ps = self . page_size ;
42+ let pager = Arc :: clone ( & self . pager ) ;
43+ let inline_threshold = overflow:: inline_value_threshold ( ps) ;
44+ let mut records: Vec < ( Vec < u8 > , LeafValue ) > = Vec :: with_capacity ( pairs. len ( ) ) ;
45+ for ( k, v) in pairs {
46+ let value = if v. len ( ) > inline_threshold {
47+ let total_len = v. len ( ) as u64 ;
48+ let root_page_id =
49+ overflow:: write_chain ( & pager, realm, & v, ps, & mut || self . allocate_page ( ) )
50+ . await ?;
51+ LeafValue :: Overflow {
52+ total_len,
53+ root_page_id,
54+ }
55+ } else {
56+ LeafValue :: Inline ( v)
57+ } ;
58+ records. push ( ( k, value) ) ;
59+ }
4360
44- let flush_leaf = |leaf : & Leaf , page_id : u64 , next_id : u64 | {
45- let _ = ( leaf, page_id, next_id) ; // will write below
46- } ;
47- let _ = flush_leaf; // suppress unused-variable lint (closure is a placeholder)
61+ let body_cap = node:: body_capacity ( ps) ;
4862
49- // First pass: group records into leaves.
50- let mut leaf_groups: Vec < Vec < ( Vec < u8 > , Vec < u8 > ) > > = Vec :: new ( ) ;
51- for ( k, v) in & pairs {
52- let entry_size = {
53- let suffix_len = k. len ( ) ; // conservative: no prefix compression yet
54- 2 + suffix_len + 2 + v. len ( ) // slot entry (inline value)
55- } ;
56- // Rough check: header + slot_dir entry + record body
63+ // First pass: group records into leaves, sized by encoded record width.
64+ let mut leaf_groups: Vec < Vec < ( Vec < u8 > , LeafValue ) > > = Vec :: new ( ) ;
65+ let mut current: Vec < ( Vec < u8 > , LeafValue ) > = Vec :: new ( ) ;
66+ for ( k, value) in records {
67+ // New record's body contribution: suffix-len field + key + value.
68+ // (No prefix compression at build time, so suffix == full key.)
69+ let entry_size = 2 + k. len ( ) + value. encoded_size ( ) ;
5770 let projected = node:: HEADER_LEN
58- + ( current_leaf. records . len ( ) + 1 ) * 2
59- + current_leaf
60- . records
71+ + ( current. len ( ) + 1 ) * 2
72+ + current
6173 . iter ( )
6274 . map ( |( ck, cv) | 2 + ck. len ( ) + cv. encoded_size ( ) )
6375 . sum :: < usize > ( )
6476 + entry_size;
65- if projected > body_cap && !current_leaf. records . is_empty ( ) {
66- leaf_groups. push (
67- std:: mem:: take ( & mut current_leaf. records )
68- . into_iter ( )
69- . map ( |( lk, lv) | {
70- let vbytes = match lv {
71- LeafValue :: Inline ( b) => b,
72- LeafValue :: Overflow { .. } => Vec :: new ( ) ,
73- } ;
74- ( lk, vbytes)
75- } )
76- . collect ( ) ,
77- ) ;
78- current_leaf = Leaf :: new ( ) ;
77+ if projected > body_cap && !current. is_empty ( ) {
78+ leaf_groups. push ( std:: mem:: take ( & mut current) ) ;
7979 }
80- current_leaf . upsert ( k, LeafValue :: Inline ( v . clone ( ) ) ) ;
80+ current . push ( ( k, value ) ) ;
8181 }
82- if !current_leaf. records . is_empty ( ) {
83- leaf_groups. push (
84- std:: mem:: take ( & mut current_leaf. records )
85- . into_iter ( )
86- . map ( |( lk, lv) | {
87- let vbytes = match lv {
88- LeafValue :: Inline ( b) => b,
89- LeafValue :: Overflow { .. } => Vec :: new ( ) ,
90- } ;
91- ( lk, vbytes)
92- } )
93- . collect ( ) ,
94- ) ;
82+ if !current. is_empty ( ) {
83+ leaf_groups. push ( current) ;
9584 }
9685
97- // Second pass: allocate page_ids and write leaves with correct sibling pointers.
86+ // Second pass: allocate page ids and write leaves with sibling links.
87+ // Input is sorted and grouping preserves order, so each leaf's records
88+ // are already in key order.
89+ let mut leaves: Vec < ( u64 , Vec < u8 > ) > = Vec :: new ( ) ; // (page_id, first_key)
9890 let n_leaves = leaf_groups. len ( ) ;
9991 let page_ids: Vec < u64 > = ( 0 ..n_leaves) . map ( |_| self . allocate_page ( ) ) . collect ( ) ;
10092
101- for ( i, group) in leaf_groups. iter ( ) . enumerate ( ) {
102- let mut leaf = Leaf {
93+ for ( i, group) in leaf_groups. into_iter ( ) . enumerate ( ) {
94+ let first_key = group. first ( ) . map ( |( k, _) | k. clone ( ) ) . unwrap_or_default ( ) ;
95+ let leaf = Leaf {
10396 left_sibling : if i == 0 { 0 } else { page_ids[ i - 1 ] } ,
10497 right_sibling : if i + 1 < n_leaves { page_ids[ i + 1 ] } else { 0 } ,
105- records : group
106- . iter ( )
107- . map ( |( k, v) | ( k. clone ( ) , LeafValue :: Inline ( v. clone ( ) ) ) )
108- . collect ( ) ,
98+ records : group,
10999 } ;
110- leaf. records . sort_by ( |( a, _) , ( b, _) | a. cmp ( b) ) ; // already sorted
111100 self . write_leaf ( page_ids[ i] , & leaf) . await ?;
112- let first_key = group. first ( ) . map ( |( k, _) | k. clone ( ) ) . unwrap_or_default ( ) ;
113101 leaves. push ( ( page_ids[ i] , first_key) ) ;
114102 }
115103
0 commit comments