• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TyRoXx / NonlocalityOS / 20003570228

07 Dec 2025 11:31AM UTC coverage: 80.214% (+0.5%) from 79.756%
20003570228

push

github

TyRoXx
Design a new prolly tree API that will make normalization easier to implement

568 of 617 new or added lines in 4 files covered. (92.06%)

3 existing lines in 1 file now uncovered.

5400 of 6732 relevant lines covered (80.21%)

33555.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.3
/sorted_tree/src/prolly_tree_editable_node.rs
1
use crate::sorted_tree::{self, NodeValue, TreeReference};
2
use astraea::{
3
    storage::{LoadTree, StoreError, StoreTree},
4
    tree::BlobDigest,
5
};
6
use serde::{de::DeserializeOwned, Deserialize, Serialize};
7
use sha3::{Digest, Sha3_512};
8
use std::collections::BTreeMap;
9
use std::fmt::Debug;
10

11
#[derive(Debug, PartialEq)]
12
pub enum IntegrityCheckResult {
13
    Valid { depth: usize },
14
    Corrupted(String),
15
}
16

17
pub fn hash_key<Key: Serialize>(key: &Key) -> u8 {
590,672✔
18
    // TODO: use a better hash function (https://docs.dolthub.com/architecture/storage-engine/prolly-tree#controlling-chunk-size)
19
    let key_serialized = postcard::to_stdvec(key).expect("serializing key should succeed");
2,953,360✔
20
    let mut hasher = Sha3_512::new();
1,181,344✔
21
    hasher.update(&key_serialized);
1,772,016✔
22
    let result: [u8; 64] = hasher.finalize().into();
2,953,360✔
23
    result[0]
590,672✔
24
}
25

26
pub fn is_split_after_key<Key: Serialize>(key: &Key, chunk_size: usize) -> bool {
818,244✔
27
    if chunk_size < 10 {
818,244✔
28
        // No point in splitting small chunks.
29
        // TODO: use Tree efficiently
30
        return false;
227,572✔
31
    }
32
    let hash = hash_key(key);
1,772,016✔
33
    let chunk_boundary_threshold = 10;
1,181,344✔
34
    if hash < chunk_boundary_threshold {
590,672✔
35
        // written with an if expression so that we can see whether the tests cover both branches
36
        true
55,200✔
37
    } else {
38
        false
535,472✔
39
    }
40
}
41

42
#[derive(Serialize, Deserialize, Clone, Hash)]
43
pub struct Metadata {
44
    pub is_leaf: bool,
45
}
46

47
pub async fn store_node<Key: Serialize + Ord, Value: NodeValue>(
288✔
48
    store_tree: &dyn StoreTree,
49
    node: &sorted_tree::Node<Key, Value>,
50
    metadata: &Metadata,
51
) -> Result<BlobDigest, StoreError> {
52
    let metadata_serialized =
288✔
53
        postcard::to_stdvec(metadata).expect("serializing metadata should always succeed");
1,152✔
54
    crate::sorted_tree::store_node(store_tree, node, &bytes::Bytes::from(metadata_serialized)).await
1,152✔
55
}
56

57
#[derive(Debug, PartialEq)]
58
pub enum EitherNodeType<Key: Serialize + Ord, Value: NodeValue> {
59
    Leaf(sorted_tree::Node<Key, Value>),
60
    Internal(sorted_tree::Node<Key, TreeReference>),
61
}
62

63
#[derive(Debug, PartialEq, Eq, Clone)]
64
pub enum DeserializationError {
65
    MissingTree(BlobDigest),
66
    TreeHashMismatch(BlobDigest),
67
}
68

69
impl std::fmt::Display for DeserializationError {
NEW
70
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
NEW
71
        write!(f, "{self:?}")
×
72
    }
73
}
74

75
impl std::error::Error for DeserializationError {}
76

77
pub async fn load_node<
75✔
78
    Key: Serialize + DeserializeOwned + PartialEq + Ord,
79
    Value: NodeValue + Clone,
80
>(
81
    load_tree: &dyn LoadTree,
82
    root: &BlobDigest,
83
) -> Result<EitherNodeType<Key, Value>, Box<dyn std::error::Error>> {
84
    let loaded = match load_tree.load_tree(root).await {
375✔
85
        Some(loaded) => loaded,
150✔
NEW
86
        None => return Err(DeserializationError::MissingTree(*root).into()),
×
87
    };
88
    let hashed = match loaded.hash() {
150✔
89
        Some(hashed) => hashed,
150✔
NEW
90
        None => return Err(DeserializationError::TreeHashMismatch(*root).into()),
×
91
    };
92
    let tree = hashed.tree();
225✔
93
    let (metadata, sorted_tree_data) =
150✔
94
        postcard::take_from_bytes::<Metadata>(tree.blob().as_slice())?;
150✔
95
    if metadata.is_leaf {
75✔
96
        let node = sorted_tree::node_from_tree::<Key, Value>(
97
            tree,
73✔
98
            tree.blob().as_slice().len() - sorted_tree_data.len(),
219✔
99
        );
100
        Ok(EitherNodeType::Leaf(node))
73✔
101
    } else {
102
        let node = sorted_tree::node_from_tree::<Key, TreeReference>(
103
            tree,
2✔
104
            tree.blob().as_slice().len() - sorted_tree_data.len(),
6✔
105
        );
106
        Ok(EitherNodeType::Internal(node))
2✔
107
    }
108
}
109

110
#[derive(Debug, Clone)]
111
pub enum EditableNode<Key: std::cmp::Ord + Clone, Value: Clone> {
112
    Reference(TreeReference),
113
    Loaded(EditableLoadedNode<Key, Value>),
114
}
115

116
impl<
117
        Key: Serialize + DeserializeOwned + PartialEq + Ord + Clone + Debug,
118
        Value: NodeValue + Clone,
119
    > Default for EditableNode<Key, Value>
120
{
121
    fn default() -> Self {
1✔
122
        Self::new()
1✔
123
    }
124
}
125

126
impl<
127
        Key: Serialize + DeserializeOwned + PartialEq + Ord + Clone + Debug,
128
        Value: NodeValue + Clone,
129
    > EditableNode<Key, Value>
130
{
131
    pub fn new() -> Self {
33✔
132
        EditableNode::Loaded(EditableLoadedNode::Leaf(EditableLeafNode {
33✔
133
            entries: BTreeMap::new(),
33✔
134
        }))
135
    }
136

137
    pub async fn require_loaded(
4,520,288✔
138
        &mut self,
139
        load_tree: &dyn LoadTree,
140
    ) -> Result<&mut EditableLoadedNode<Key, Value>, Box<dyn std::error::Error>> {
141
        match self {
4,520,288✔
142
            EditableNode::Reference(tree_ref) => {
15✔
143
                let loaded: EitherNodeType<Key, Value> =
30✔
144
                    load_node(load_tree, tree_ref.reference()).await?;
45✔
145
                *self = EditableNode::Loaded(EditableLoadedNode::new(loaded));
30✔
146
            }
147
            EditableNode::Loaded(_loaded_node) => {}
9,040,546✔
148
        };
149
        let loaded = match self {
9,040,576✔
150
            EditableNode::Loaded(loaded_node) => loaded_node,
9,040,576✔
151
            _ => unreachable!(),
152
        };
153
        Ok(loaded)
4,520,288✔
154
    }
155

156
    pub async fn insert(
4,379✔
157
        &mut self,
158
        key: Key,
159
        value: Value,
160
        load_tree: &dyn LoadTree,
161
    ) -> Result<(), Box<dyn std::error::Error>> {
162
        let (self_top_key, nodes_split) = self.insert_impl(key, value, load_tree).await?;
30,653✔
163
        if nodes_split.is_empty() {
8,758✔
164
            return Ok(());
4,366✔
165
        }
166
        let mut entries = BTreeMap::new();
26✔
167
        entries.insert(self_top_key, self.clone());
65✔
168
        for node in nodes_split {
52✔
169
            entries.insert(
39✔
170
                node.top_key().expect("Node cannot be empty here").clone(),
52✔
171
                EditableNode::Loaded(node),
13✔
172
            );
173
        }
174
        *self = EditableNode::Loaded(EditableLoadedNode::Internal(EditableInternalNode {
26✔
175
            entries,
13✔
176
        }));
177
        Ok(())
13✔
178
    }
179

180
    pub async fn insert_impl(
10,151✔
181
        &mut self,
182
        key: Key,
183
        value: Value,
184
        load_tree: &dyn LoadTree,
185
    ) -> Result<(Key, Vec<EditableLoadedNode<Key, Value>>), Box<dyn std::error::Error>> {
186
        let loaded = self.require_loaded(load_tree).await?;
40,604✔
187
        let nodes_split = Box::pin(loaded.insert(key, value, load_tree)).await?;
71,057✔
188
        Ok((
10,151✔
189
            loaded.top_key().expect("Node cannot be empty here").clone(),
40,604✔
190
            nodes_split,
10,151✔
191
        ))
192
    }
193

194
    pub async fn remove(
1,014✔
195
        &mut self,
196
        key: &Key,
197
        load_tree: &dyn LoadTree,
198
    ) -> Result<Option<Value>, Box<dyn std::error::Error>> {
199
        let (maybe_top_key, maybe_removed) = self.remove_impl(key, load_tree).await?;
6,084✔
200
        if maybe_top_key.is_none() {
2,029✔
201
            *self = EditableNode::Loaded(EditableLoadedNode::Leaf(EditableLeafNode {
1✔
202
                entries: BTreeMap::new(),
1✔
203
            }));
204
        } else {
205
            let loaded = self.require_loaded(load_tree).await?;
4,052✔
206
            if let Some(simplified) = loaded.simplify() {
1,019✔
207
                *self = simplified;
3✔
208
            }
209
        }
210
        Ok(maybe_removed)
1,014✔
211
    }
212

213
    pub async fn remove_impl(
2,633✔
214
        &mut self,
215
        key: &Key,
216
        load_tree: &dyn LoadTree,
217
    ) -> Result<(Option<Key>, Option<Value>), Box<dyn std::error::Error>> {
218
        let loaded = self.require_loaded(load_tree).await?;
10,532✔
219
        let result = loaded.remove(key, load_tree).await?;
13,165✔
220
        Ok(result)
2,633✔
221
    }
222

223
    pub async fn find(
4,368,861✔
224
        &mut self,
225
        key: &Key,
226
        load_tree: &dyn LoadTree,
227
    ) -> Result<Option<Value>, Box<dyn std::error::Error>> {
228
        let loaded = self.require_loaded(load_tree).await?;
17,475,444✔
229
        loaded.find(key, load_tree).await
17,475,444✔
230
    }
231

232
    pub async fn size(
75,850✔
233
        &mut self,
234
        load_tree: &dyn LoadTree,
235
    ) -> Result<u64, Box<dyn std::error::Error>> {
236
        let loaded = self.require_loaded(load_tree).await?;
303,400✔
237
        Box::pin(loaded.size(load_tree)).await
303,400✔
238
    }
239

240
    pub async fn save(
302✔
241
        &mut self,
242
        store_tree: &dyn StoreTree,
243
    ) -> Result<BlobDigest, Box<dyn std::error::Error>> {
244
        match self {
302✔
245
            EditableNode::Reference(tree_ref) => Ok(*tree_ref.reference()),
28✔
246
            EditableNode::Loaded(loaded_node) => loaded_node.save(store_tree).await,
1,152✔
247
        }
248
    }
249

250
    pub async fn load(
60✔
251
        digest: &BlobDigest,
252
        load_tree: &dyn LoadTree,
253
    ) -> Result<Self, Box<dyn std::error::Error>> {
254
        let loaded: EitherNodeType<Key, Value> = load_node(load_tree, digest).await?;
300✔
255
        Ok(EditableNode::Loaded(EditableLoadedNode::new(loaded)))
60✔
256
    }
257

258
    pub async fn verify_integrity(
20,086✔
259
        &mut self,
260
        expected_top_key: Option<&Key>,
261
        load_tree: &dyn LoadTree,
262
    ) -> Result<IntegrityCheckResult, Box<dyn std::error::Error>> {
263
        let loaded = self.require_loaded(load_tree).await?;
80,344✔
264
        if loaded.top_key() != expected_top_key {
20,086✔
NEW
265
            return Ok(IntegrityCheckResult::Corrupted(
×
NEW
266
                "Top key mismatch".to_string(),
×
267
            ));
268
        }
269
        Box::pin(loaded.verify_integrity(load_tree)).await
80,344✔
270
    }
271

272
    pub async fn merge(
159✔
273
        &mut self,
274
        other: Self,
275
        load_tree: &dyn LoadTree,
276
    ) -> Result<(Key, Vec<EditableLoadedNode<Key, Value>>), Box<dyn std::error::Error>> {
277
        let loaded = self.require_loaded(load_tree).await?;
636✔
278
        let other_loaded = match other {
318✔
NEW
279
            EditableNode::Reference(tree_ref) => {
×
NEW
280
                let loaded: EitherNodeType<Key, Value> =
×
NEW
281
                    load_node(load_tree, tree_ref.reference()).await?;
×
NEW
282
                EditableLoadedNode::new(loaded)
×
283
            }
284
            EditableNode::Loaded(loaded_node) => loaded_node,
318✔
285
        };
286
        match (loaded, other_loaded) {
318✔
287
            (EditableLoadedNode::Leaf(self_leaf), EditableLoadedNode::Leaf(other_leaf)) => {
190✔
288
                for (key, value) in other_leaf.entries {
11,599✔
289
                    self_leaf.entries.insert(key, value);
8,628✔
290
                }
291
                let split_nodes = self_leaf.check_split();
285✔
NEW
292
                Ok((
×
293
                    self_leaf
190✔
294
                        .top_key()
190✔
295
                        .expect("Leaf cannot be empty here")
95✔
296
                        .clone(),
95✔
297
                    split_nodes
95✔
298
                        .into_iter()
95✔
299
                        .map(|n| EditableLoadedNode::Leaf(n))
118✔
300
                        .collect(),
95✔
301
                ))
302
            }
303
            (
304
                EditableLoadedNode::Internal(self_internal),
64✔
305
                EditableLoadedNode::Internal(other_internal),
64✔
NEW
306
            ) => {
×
307
                for (key, child_node) in other_internal.entries {
1,138✔
308
                    let previous_entry = self_internal.entries.insert(key, child_node);
1,790✔
309
                    if let Some(_existing_child) = previous_entry {
358✔
NEW
310
                        return Err(Box::new(std::io::Error::other("Merge node key collision")));
×
311
                    }
312
                }
313
                let split_nodes = self_internal.check_split();
192✔
NEW
314
                Ok((
×
315
                    self_internal
128✔
316
                        .top_key()
128✔
317
                        .expect("Internal node cannot be empty here")
64✔
318
                        .clone(),
64✔
319
                    split_nodes
64✔
320
                        .into_iter()
64✔
321
                        .map(|n| EditableLoadedNode::Internal(n))
89✔
322
                        .collect(),
64✔
323
                ))
324
            }
325
            _ => unreachable!(),
326
        }
327
    }
328

329
    pub async fn is_naturally_split(
41,518✔
330
        &mut self,
331
        load_tree: &dyn LoadTree,
332
    ) -> Result<bool, Box<dyn std::error::Error>> {
333
        let loaded = self.require_loaded(load_tree).await?;
166,072✔
334
        Ok(loaded.is_naturally_split())
41,518✔
335
    }
336
}
337

338
#[derive(Debug, Clone)]
339
pub struct EditableLeafNode<Key, Value> {
340
    entries: BTreeMap<Key, Value>,
341
}
342

343
impl<Key: std::cmp::Ord + Clone + Serialize, Value: Clone> EditableLeafNode<Key, Value> {
344
    pub fn create(entries: BTreeMap<Key, Value>) -> Option<Self> {
4,655✔
345
        if entries.is_empty() {
9,310✔
346
            None
1✔
347
        } else {
348
            Some(EditableLeafNode { entries })
4,654✔
349
        }
350
    }
351

352
    pub async fn insert(&mut self, key: Key, value: Value) -> Vec<EditableLeafNode<Key, Value>> {
8,758✔
353
        self.entries.insert(key, value);
17,516✔
354
        self.check_split()
8,758✔
355
    }
356

357
    pub async fn remove(
1,014✔
358
        &mut self,
359
        key: &Key,
360
    ) -> Result<(Option<Key>, Option<Value>), Box<dyn std::error::Error>> {
361
        let removed = self.entries.remove(key);
4,056✔
362
        let top_key = self.top_key().cloned();
4,056✔
363
        Ok((top_key, removed))
1,014✔
364
    }
365

366
    fn check_split(&mut self) -> Vec<EditableLeafNode<Key, Value>> {
4,474✔
367
        let mut result = Vec::new();
8,948✔
368
        let mut current_node = BTreeMap::new();
8,948✔
369
        for entry in self.entries.iter() {
208,929✔
370
            current_node.insert(entry.0.clone(), entry.1.clone());
1,199,886✔
371
            if is_split_after_key(entry.0, current_node.len()) {
803,298✔
372
                result.push(
10,122✔
373
                    EditableLeafNode::create(current_node)
10,122✔
374
                        .expect("Must succeed because list is not empty"),
10,122✔
375
                );
376
                current_node = BTreeMap::new();
3,374✔
377
            }
378
        }
379
        if !current_node.is_empty() {
5,754✔
380
            result.push(
3,840✔
381
                EditableLeafNode::create(current_node)
3,840✔
382
                    .expect("Must succeed because list is not empty"),
1,280✔
383
            );
384
        }
385
        *self = result.remove(0);
13,422✔
386
        result
4,474✔
387
    }
388

389
    pub fn top_key(&self) -> Option<&Key> {
22,784✔
390
        self.entries.keys().next_back()
45,568✔
391
    }
392

393
    pub fn find(&mut self, key: &Key) -> Option<Value> {
1,549,674✔
394
        self.entries.get(key).cloned()
6,198,696✔
395
    }
396

397
    pub fn verify_integrity(&mut self) -> Result<IntegrityCheckResult, Box<dyn std::error::Error>> {
17,116✔
398
        for (index, key) in self.entries.keys().enumerate() {
1,140,622✔
399
            let is_split = is_split_after_key(key, index + 1);
2,178,548✔
400
            if (index < self.entries.len() - 1) && is_split {
1,616,797✔
NEW
401
                return Ok(IntegrityCheckResult::Corrupted(format!(
×
NEW
402
                    "Leaf node integrity check failed: Key at index {} indicates split but node is not final (number of keys: {})",
×
NEW
403
                    index, self.entries.len()
×
404
                )));
405
            }
406
        }
407
        Ok(IntegrityCheckResult::Valid { depth: 0 })
17,116✔
408
    }
409

410
    pub fn is_naturally_split(&self) -> bool {
34,819✔
411
        is_split_after_key(
412
            self.entries.keys().last().expect("leaf node is not empty"),
139,276✔
413
            self.entries.len(),
69,638✔
414
        )
415
    }
416

417
    pub fn entries(&self) -> &BTreeMap<Key, Value> {
14✔
418
        &self.entries
14✔
419
    }
420
}
421

422
#[derive(Debug, Clone)]
423
pub struct EditableInternalNode<Key: std::cmp::Ord + Clone, Value: Clone> {
424
    entries: BTreeMap<Key, EditableNode<Key, Value>>,
425
}
426

427
impl<
428
        Key: Serialize + DeserializeOwned + PartialEq + Ord + Clone + Debug,
429
        Value: NodeValue + Clone,
430
    > EditableInternalNode<Key, Value>
431
{
432
    pub fn create(entries: BTreeMap<Key, EditableNode<Key, Value>>) -> Option<Self> {
5,905✔
433
        if entries.is_empty() {
11,810✔
NEW
434
            None
×
435
        } else {
436
            Some(EditableInternalNode { entries })
5,905✔
437
        }
438
    }
439

440
    pub async fn insert(
5,772✔
441
        &mut self,
442
        key: Key,
443
        value: Value,
444
        load_tree: &dyn LoadTree,
445
    ) -> Result<Vec<EditableInternalNode<Key, Value>>, Box<dyn std::error::Error>> {
446
        let last_index = self.entries.len() - 1;
11,544✔
447
        // TODO: optimize search
448
        for (index, (entry_key, entry_value)) in self.entries.iter_mut().enumerate() {
76,416✔
449
            if (index == last_index) || (key <= *entry_key) {
37,618✔
450
                let (updated_key, split_nodes) =
11,544✔
451
                    entry_value.insert_impl(key, value, load_tree).await?;
28,860✔
452
                if updated_key != *entry_key {
5,772✔
453
                    let old_key = entry_key.clone();
1,884✔
454
                    let old_value = self.entries.remove(&old_key).expect("key must exist");
3,768✔
455
                    let previous_entry = self.entries.insert(updated_key, old_value);
3,140✔
456
                    assert!(previous_entry.is_none(), "Split node key collision");
457
                }
458
                for node in split_nodes {
6,148✔
459
                    let previous_entry = self.entries.insert(
564✔
460
                        node.top_key().expect("Node cannot be empty here").clone(),
752✔
461
                        EditableNode::Loaded(node),
188✔
462
                    );
463
                    assert!(previous_entry.is_none(), "Split node key collision");
464
                }
465
                self.update_chunk_boundaries(load_tree).await?;
17,316✔
466
                break;
5,772✔
467
            }
468
        }
469
        Ok(self.check_split())
5,772✔
470
    }
471

472
    pub async fn remove(
1,619✔
473
        &mut self,
474
        key: &Key,
475
        load_tree: &dyn LoadTree,
476
    ) -> Result<(Option<Key>, Option<Value>), Box<dyn std::error::Error>> {
477
        // TODO: optimize search
478
        for (entry_key, entry_value) in self.entries.iter_mut() {
14,778✔
479
            if key <= entry_key {
5,770✔
480
                let (maybe_new_top_key, maybe_removed) =
3,238✔
481
                    Box::pin(entry_value.remove_impl(key, load_tree)).await?;
8,095✔
482
                match maybe_new_top_key {
1,619✔
483
                    Some(new_top_key) => {
1,619✔
484
                        if new_top_key != *entry_key {
1,651✔
485
                            let entry_key = entry_key.clone();
128✔
486
                            let entry_value_removed = self
96✔
487
                                .entries
64✔
488
                                .remove(&entry_key)
96✔
489
                                .expect("Must exist because we just iterated over the entries");
490
                            self.entries.insert(new_top_key, entry_value_removed);
96✔
491
                        }
492
                    }
NEW
493
                    None => {
×
NEW
494
                        let entry_key = entry_key.clone();
×
NEW
495
                        self.entries.remove(&entry_key);
×
496
                    }
497
                }
498
                let top_key = self.top_key().cloned();
6,476✔
499
                match maybe_removed {
1,619✔
500
                    Some(removed) => {
1,619✔
501
                        if !self.entries.is_empty() {
1,619✔
502
                            self.update_chunk_boundaries(load_tree).await?;
4,857✔
503
                        }
504
                        return Ok((top_key, Some(removed)));
1,619✔
505
                    }
NEW
506
                    None => {
×
NEW
507
                        return Ok((top_key, None));
×
508
                    }
509
                }
510
            }
511
        }
NEW
512
        Ok((self.top_key().cloned(), None))
×
513
    }
514

515
    async fn update_chunk_boundaries(
7,391✔
516
        &mut self,
517
        load_tree: &dyn LoadTree,
518
    ) -> Result<(), Box<dyn std::error::Error>> {
NEW
519
        loop {
×
520
            let merge_candidates = self.find_merge_candidates(load_tree).await?;
30,200✔
521
            match merge_candidates {
7,550✔
522
                Some((low_key, high_key)) => {
318✔
523
                    let mut low = self.entries.remove(&low_key).expect("key must exist");
954✔
524
                    let high = self.entries.remove(&high_key).expect("key must exist");
954✔
525
                    let (low_top_key, split_nodes) = low.merge(high, load_tree).await?;
954✔
526
                    assert!(split_nodes.is_empty() || low.is_naturally_split(load_tree).await?);
527
                    assert_ne!(low_key, low_top_key, "Merge did not change low key");
528
                    let previous_entry = self.entries.insert(low_top_key, low);
795✔
529
                    assert!(previous_entry.is_none(), "Merge node key collision");
530
                    let split_nodes_len = split_nodes.len();
477✔
531
                    for (index, node) in split_nodes.into_iter().enumerate() {
573✔
532
                        assert!((index == split_nodes_len - 1) || node.is_naturally_split());
533
                        let previous_entry = self.entries.insert(
144✔
534
                            node.top_key().expect("Node cannot be empty here").clone(),
192✔
535
                            EditableNode::Loaded(node),
48✔
536
                        );
537
                        assert!(previous_entry.is_none(), "Merge node key collision");
538
                    }
539
                }
540
                None => break,
7,391✔
541
            }
542
        }
543
        Ok(())
7,391✔
544
    }
545

546
    async fn find_merge_candidates(
7,550✔
547
        &mut self,
548
        load_tree: &dyn LoadTree,
549
    ) -> Result<Option<(Key, Key)>, Box<dyn std::error::Error>> {
550
        let last_index = self.entries.len() - 1;
15,100✔
551
        let mut needs_merge: Option<&Key> = None;
22,650✔
552
        // TODO: optimize search
553
        for (index, (entry_key, entry_value)) in self.entries.iter_mut().enumerate() {
147,537✔
554
            if let Some(merge_value) = needs_merge.take() {
41,788✔
555
                return Ok(Some((merge_value.clone(), entry_key.clone())));
477✔
556
            }
557
            let is_split = entry_value.is_naturally_split(load_tree).await?;
165,880✔
558
            if (index != last_index) && !is_split {
75,708✔
559
                needs_merge = Some(entry_key);
159✔
560
            }
561
        }
562
        Ok(None)
7,391✔
563
    }
564

565
    fn check_split(&mut self) -> Vec<EditableInternalNode<Key, Value>> {
5,836✔
566
        let mut result = Vec::new();
11,672✔
567
        let mut current_node = BTreeMap::new();
11,672✔
568
        for entry in self.entries.iter() {
43,780✔
569
            current_node.insert(entry.0.clone(), entry.1.clone());
192,648✔
570
            if is_split_after_key(entry.0, current_node.len()) {
129,456✔
571
                result.push(
3,072✔
572
                    EditableInternalNode::create(current_node)
3,072✔
573
                        .expect("Must succeed because list is not empty"),
3,072✔
574
                );
575
                current_node = BTreeMap::new();
1,024✔
576
            }
577
        }
578
        if !current_node.is_empty() {
10,717✔
579
            result.push(
14,643✔
580
                EditableInternalNode::create(current_node)
14,643✔
581
                    .expect("Must succeed because list is not empty"),
4,881✔
582
            );
583
        }
584
        *self = result.remove(0);
17,508✔
585
        result
5,836✔
586
    }
587

588
    pub fn top_key(&self) -> Option<&Key> {
10,494✔
589
        self.entries.keys().next_back()
20,988✔
590
    }
591

592
    pub async fn find(
2,819,187✔
593
        &mut self,
594
        key: &Key,
595
        _load_tree: &dyn LoadTree,
596
    ) -> Result<Option<Value>, Box<dyn std::error::Error>> {
597
        // TODO: optimize search
598
        for (entry_key, entry_value) in self.entries.iter_mut() {
24,437,798✔
599
            if key <= entry_key {
9,399,712✔
600
                return Box::pin(entry_value.find(key, _load_tree)).await;
14,095,785✔
601
            }
602
        }
603
        Ok(None)
30✔
604
    }
605

606
    pub async fn verify_integrity(
2,970✔
607
        &mut self,
608
        load_tree: &dyn LoadTree,
609
    ) -> Result<IntegrityCheckResult, Box<dyn std::error::Error>> {
610
        if self.entries.is_empty() {
5,940✔
NEW
611
            return Ok(IntegrityCheckResult::Corrupted(
×
NEW
612
                "Internal node integrity check failed: Node has no entries".to_string(),
×
613
            ));
614
        }
615
        let mut child_depth = None;
5,940✔
616
        for (index, (key, value)) in self.entries.iter_mut().enumerate() {
64,863✔
617
            match value.verify_integrity(Some(key), load_tree).await? {
74,604✔
618
                IntegrityCheckResult::Valid { depth } => {
18,651✔
619
                    if let Some(existing_depth) = child_depth {
34,332✔
620
                        if existing_depth != depth {
15,681✔
NEW
621
                            return Ok(IntegrityCheckResult::Corrupted(format!(
×
NEW
622
                                "Internal node integrity check failed at index {}: Child node depth mismatch (expected {}, found {})",
×
NEW
623
                                index, existing_depth, depth
×
624
                            )));
625
                        }
626
                    } else {
627
                        child_depth = Some(depth);
2,970✔
628
                    }
629
                }
NEW
630
                IntegrityCheckResult::Corrupted(reason) => {
×
NEW
631
                    return Ok(IntegrityCheckResult::Corrupted(format!(
×
NEW
632
                        "Internal node integrity check failed at index {}: {}",
×
NEW
633
                        index, reason
×
634
                    )));
635
                }
636
            }
637
        }
638
        Ok(IntegrityCheckResult::Valid {
2,970✔
639
            depth: child_depth.expect("Internal node has to have at least one child") + 1,
5,940✔
640
        })
641
    }
642

643
    pub fn is_naturally_split(&self) -> bool {
6,699✔
644
        let last_key = self
13,398✔
645
            .entries
6,699✔
646
            .keys()
647
            .last()
648
            .expect("internal node is not empty");
649
        is_split_after_key(last_key, self.entries.len())
26,796✔
650
    }
651
}
652

653
#[derive(Debug, Clone)]
654
pub enum EditableLoadedNode<Key: std::cmp::Ord + Clone, Value: Clone> {
655
    Leaf(EditableLeafNode<Key, Value>),
656
    Internal(EditableInternalNode<Key, Value>),
657
}
658

659
impl<Key: Serialize + DeserializeOwned + Ord + Clone + Debug, Value: NodeValue + Clone>
660
    EditableLoadedNode<Key, Value>
661
{
662
    pub fn new(loaded: EitherNodeType<Key, Value>) -> Self {
75✔
663
        match loaded {
75✔
664
            EitherNodeType::Leaf(leaf_node) => {
73✔
665
                let mut entries = BTreeMap::new();
146✔
666
                for (key, value) in leaf_node.entries {
2,329✔
667
                    entries.insert(key, value);
1,692✔
668
                }
669
                EditableLoadedNode::Leaf(EditableLeafNode { entries })
73✔
670
            }
671
            EitherNodeType::Internal(internal_node) => {
2✔
672
                let mut entries = BTreeMap::new();
4✔
673
                for (key, child_node) in internal_node.entries {
26✔
674
                    entries.insert(key, EditableNode::Reference(child_node));
18✔
675
                }
676
                EditableLoadedNode::Internal(EditableInternalNode { entries })
2✔
677
            }
678
        }
679
    }
680

681
    pub async fn insert(
10,151✔
682
        &mut self,
683
        key: Key,
684
        value: Value,
685
        load_tree: &dyn LoadTree,
686
    ) -> Result<Vec<EditableLoadedNode<Key, Value>>, Box<dyn std::error::Error>> {
687
        match self {
10,151✔
688
            EditableLoadedNode::Leaf(leaf_node) => {
4,379✔
689
                let split_nodes = leaf_node.insert(key, value).await;
21,895✔
690
                Ok(split_nodes
4,379✔
691
                    .into_iter()
4,379✔
692
                    .map(|node| EditableLoadedNode::Leaf(node))
4,536✔
693
                    .collect())
4,379✔
694
            }
695
            EditableLoadedNode::Internal(internal_node) => {
5,772✔
696
                let split_nodes = internal_node.insert(key, value, load_tree).await?;
34,632✔
697
                Ok(split_nodes
5,772✔
698
                    .into_iter()
5,772✔
699
                    .map(|node| EditableLoadedNode::Internal(node))
5,816✔
700
                    .collect())
5,772✔
701
            }
702
        }
703
    }
704

705
    pub async fn remove(
2,633✔
706
        &mut self,
707
        key: &Key,
708
        load_tree: &dyn LoadTree,
709
    ) -> Result<(Option<Key>, Option<Value>), Box<dyn std::error::Error>> {
710
        match self {
2,633✔
711
            EditableLoadedNode::Leaf(leaf_node) => leaf_node.remove(key).await,
4,056✔
712
            EditableLoadedNode::Internal(internal_node) => {
1,619✔
713
                internal_node.remove(key, load_tree).await
6,476✔
714
            }
715
        }
716
    }
717

718
    pub fn simplify(&mut self) -> Option<EditableNode<Key, Value>> {
1,013✔
719
        match self {
1,013✔
720
            EditableLoadedNode::Internal(internal_node) => {
977✔
721
                if internal_node.entries.len() == 1 {
977✔
722
                    let (_, only_child) = internal_node
9✔
723
                        .entries
6✔
724
                        .iter_mut()
725
                        .next()
726
                        .expect("internal node has one entry");
727
                    Some(only_child.clone())
3✔
728
                } else {
729
                    None
974✔
730
                }
731
            }
732
            EditableLoadedNode::Leaf(_) => None,
36✔
733
        }
734
    }
735

736
    pub async fn find(
4,368,861✔
737
        &mut self,
738
        key: &Key,
739
        load_tree: &dyn LoadTree,
740
    ) -> Result<Option<Value>, Box<dyn std::error::Error>> {
741
        match self {
4,368,861✔
742
            EditableLoadedNode::Leaf(leaf_node) => Ok(leaf_node.find(key)),
4,649,022✔
743
            EditableLoadedNode::Internal(internal_node) => internal_node.find(key, load_tree).await,
14,095,935✔
744
        }
745
    }
746

747
    pub fn top_key(&self) -> Option<&Key> {
30,486✔
748
        match self {
30,486✔
749
            EditableLoadedNode::Leaf(leaf_node) => leaf_node.top_key(),
65,025✔
750
            EditableLoadedNode::Internal(internal_node) => internal_node.top_key(),
26,433✔
751
        }
752
    }
753

754
    pub async fn size(
75,850✔
755
        &mut self,
756
        load_tree: &dyn LoadTree,
757
    ) -> Result<u64, Box<dyn std::error::Error>> {
758
        match self {
75,850✔
759
            EditableLoadedNode::Leaf(leaf_node) => Ok(leaf_node.entries.len() as u64),
129,826✔
760
            EditableLoadedNode::Internal(internal_node) => {
10,937✔
761
                let mut total_size = 0;
21,874✔
762
                for child_node in internal_node.entries.values_mut() {
92,824✔
763
                    total_size += child_node.size(load_tree).await?;
212,850✔
764
                }
765
                Ok(total_size)
10,937✔
766
            }
767
        }
768
    }
769

770
    pub async fn save(
288✔
771
        &mut self,
772
        store_tree: &dyn StoreTree,
773
    ) -> Result<BlobDigest, Box<dyn std::error::Error>> {
774
        match self {
288✔
775
            EditableLoadedNode::Leaf(leaf_node) => {
255✔
776
                let mut new_node = crate::sorted_tree::Node {
777
                    entries: Vec::new(),
255✔
778
                };
779
                for (key, value) in &leaf_node.entries {
29,187✔
780
                    new_node.entries.push((key.clone(), value.clone()));
36,165✔
781
                }
782
                let digest = store_node(store_tree, &new_node, &Metadata { is_leaf: true }).await?;
1,275✔
783
                Ok(digest)
255✔
784
            }
785
            EditableLoadedNode::Internal(internal_node) => {
33✔
786
                let mut new_node = crate::sorted_tree::Node {
787
                    entries: Vec::new(),
33✔
788
                };
789
                for (key, child_node) in &mut internal_node.entries {
675✔
790
                    let child_digest = Box::pin(child_node.save(store_tree)).await?;
1,070✔
791
                    new_node
214✔
792
                        .entries
214✔
793
                        .push((key.clone(), TreeReference::new(child_digest)));
856✔
794
                }
795
                let digest =
33✔
796
                    store_node(store_tree, &new_node, &Metadata { is_leaf: false }).await?;
132✔
797
                Ok(digest)
33✔
798
            }
799
        }
800
    }
801

802
    pub async fn verify_integrity(
20,086✔
803
        &mut self,
804
        load_tree: &dyn LoadTree,
805
    ) -> Result<IntegrityCheckResult, Box<dyn std::error::Error>> {
806
        match self {
20,086✔
807
            EditableLoadedNode::Leaf(leaf_node) => leaf_node.verify_integrity(),
51,348✔
808
            EditableLoadedNode::Internal(internal_node) => {
2,970✔
809
                internal_node.verify_integrity(load_tree).await
8,910✔
810
            }
811
        }
812
    }
813

814
    pub fn is_naturally_split(&self) -> bool {
41,518✔
815
        match self {
41,518✔
816
            EditableLoadedNode::Leaf(leaf_node) => leaf_node.is_naturally_split(),
104,457✔
817
            EditableLoadedNode::Internal(internal_node) => internal_node.is_naturally_split(),
20,097✔
818
        }
819
    }
820
}
821

822
pub struct Iterator<
823
    't,
824
    Key: Serialize + DeserializeOwned + Ord + Clone + Debug,
825
    Value: NodeValue + Clone,
826
> {
827
    next: Vec<&'t mut EditableNode<Key, Value>>,
828
    leaf_iterator: Option<std::collections::btree_map::Iter<'t, Key, Value>>,
829
    load_tree: &'t dyn LoadTree,
830
}
831

832
impl<'t, Key, Value> Iterator<'t, Key, Value>
833
where
834
    Key: Serialize + DeserializeOwned + Ord + Clone + Debug,
835
    Value: NodeValue + Clone,
836
{
837
    pub fn new(node: &'t mut EditableNode<Key, Value>, load_tree: &'t dyn LoadTree) -> Self {
3✔
838
        Iterator {
839
            next: vec![node],
9✔
840
            leaf_iterator: None,
841
            load_tree,
842
        }
843
    }
844

845
    pub async fn next(&mut self) -> Result<Option<(Key, Value)>, Box<dyn std::error::Error>> {
1,012✔
NEW
846
        loop {
×
847
            if let Some(current_node) = self.leaf_iterator.as_mut() {
1,040✔
848
                match current_node.next() {
517✔
849
                    Some((key, value)) => return Ok(Some((key.clone(), value.clone()))),
2,515✔
850
                    None => {
14✔
851
                        self.leaf_iterator = None;
14✔
852
                    }
853
                }
854
            }
855
            match self.next.pop() {
20✔
856
                Some(next_node) => {
17✔
857
                    let loaded = next_node.require_loaded(self.load_tree).await?;
68✔
858
                    match loaded {
17✔
859
                        EditableLoadedNode::Leaf(leaf_node) => {
14✔
860
                            self.leaf_iterator = Some(leaf_node.entries().iter());
14✔
861
                            continue;
14✔
862
                        }
863
                        EditableLoadedNode::Internal(internal_node) => {
3✔
864
                            internal_node
3✔
865
                                .entries
3✔
866
                                .values_mut()
867
                                .rev()
868
                                .for_each(|child_node| {
17✔
869
                                    self.next.push(child_node);
42✔
870
                                });
871
                        }
872
                    };
873
                }
NEW
874
                None => {
×
875
                    return Ok(None);
3✔
876
                }
877
            }
878
        }
879
    }
880
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc