1#![forbid(unsafe_code)]
48
49use core::ops::{Bound, RangeBounds};
50
51use crate::btree::node::{decode_node, DecodedNode, NodeKind};
52use crate::btree::{BTree, MAX_BTREE_DEPTH, MAX_RANGE_NODES};
53use crate::error::{Error, Result};
54use crate::pager::page::PageId;
55use crate::pager::Pager;
56use crate::platform::FileBackend;
57
58use heapless::Vec as HeaplessVec;
59
60pub struct RangeIter<'a, F: FileBackend> {
80 pager: &'a mut Pager<F>,
81 root: PageId,
85 current_leaf: Option<DecodedNode>,
86 slot_index: usize,
87 start_bound: Bound<Vec<u8>>,
88 end_bound: Bound<Vec<u8>>,
89 last_emitted_key: Option<Vec<u8>>,
92 nodes_visited: usize,
93 finished: bool,
94}
95
96impl<F: FileBackend> std::fmt::Debug for RangeIter<'_, F> {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.debug_struct("RangeIter")
99 .field("slot_index", &self.slot_index)
100 .field("start_bound", &self.start_bound)
101 .field("end_bound", &self.end_bound)
102 .field("nodes_visited", &self.nodes_visited)
103 .field("finished", &self.finished)
104 .finish()
105 }
106}
107
108impl<F: FileBackend> BTree<F> {
109 pub fn range<'a, R>(&self, pager: &'a mut Pager<F>, range: R) -> Result<RangeIter<'a, F>>
126 where
127 R: RangeBounds<Vec<u8>>,
128 {
129 let start_bound = clone_bound_vec(range.start_bound());
130 let end_bound = clone_bound_vec(range.end_bound());
131 Self::build_range_iter(pager, self.root, start_bound, end_bound)
132 }
133
134 #[allow(clippy::iter_not_returning_iterator)] pub fn iter<'a>(&self, pager: &'a mut Pager<F>) -> Result<RangeIter<'a, F>> {
149 self.range(pager, ..)
150 }
151
152 fn build_range_iter(
153 pager: &mut Pager<F>,
154 root: PageId,
155 start_bound: Bound<Vec<u8>>,
156 end_bound: Bound<Vec<u8>>,
157 ) -> Result<RangeIter<'_, F>> {
158 let (leaf, slot_index, nodes_visited) =
159 locate_first_in_range_leaf(pager, root, &start_bound)?;
160 let finished = slot_index >= leaf.leaves.len();
161 Ok(RangeIter {
162 pager,
163 root,
164 current_leaf: Some(leaf),
165 slot_index,
166 start_bound,
167 end_bound,
168 last_emitted_key: None,
169 nodes_visited,
170 finished,
171 })
172 }
173
174 pub fn range_via_snapshot<'a, R>(
197 pager: &'a Pager<F>,
198 snapshot: &'a crate::pager::ReaderSnapshot<F>,
199 root: PageId,
200 range: R,
201 ) -> Result<SnapshotRangeIter<'a, F>>
202 where
203 R: RangeBounds<Vec<u8>>,
204 {
205 let start_bound = clone_bound_vec(range.start_bound());
206 let end_bound = clone_bound_vec(range.end_bound());
207 let (leaf, slot_index, nodes_visited) =
208 snap_locate_first_in_range_leaf(pager, snapshot, root, &start_bound)?;
209 let finished = slot_index >= leaf.leaves.len();
210 Ok(SnapshotRangeIter {
211 pager,
212 snapshot,
213 root,
214 current_leaf: Some(leaf),
215 slot_index,
216 start_bound,
217 end_bound,
218 last_emitted_key: None,
219 nodes_visited,
220 finished,
221 })
222 }
223}
224
225fn locate_first_in_range_leaf<F: FileBackend>(
248 pager: &mut Pager<F>,
249 root: PageId,
250 start_bound: &Bound<Vec<u8>>,
251) -> Result<(DecodedNode, usize, usize)> {
252 let descend_key = match start_bound {
256 Bound::Included(k) | Bound::Excluded(k) => k.as_slice(),
257 Bound::Unbounded => &[][..],
258 };
259 let leaf_id = descend_to_start_leaf(pager, root, descend_key)?;
260 let mut leaf = read_leaf(pager, leaf_id)?;
261 let mut slot_index = position_in_leaf(&leaf, start_bound);
262 let mut nodes_visited: usize = 1;
263 while slot_index >= leaf.leaves.len() {
264 let Some(last_key) = leaf.leaves.last().map(|e| e.key.clone()) else {
265 break;
268 };
269 nodes_visited += 1;
270 if nodes_visited > MAX_RANGE_NODES {
271 return Err(Error::BTreeScanLimitExceeded {
272 limit: MAX_RANGE_NODES,
273 });
274 }
275 let Some(next_id) = descend_to_leaf_after(pager, root, last_key.as_slice())? else {
276 break;
279 };
280 leaf = read_leaf(pager, next_id)?;
281 slot_index = position_in_leaf(&leaf, start_bound);
282 }
283 Ok((leaf, slot_index, nodes_visited))
284}
285
286fn clone_bound_vec(b: Bound<&Vec<u8>>) -> Bound<Vec<u8>> {
287 match b {
288 Bound::Included(s) => Bound::Included(s.clone()),
289 Bound::Excluded(s) => Bound::Excluded(s.clone()),
290 Bound::Unbounded => Bound::Unbounded,
291 }
292}
293
294fn descend_to_start_leaf<F: FileBackend>(
295 pager: &mut Pager<F>,
296 root: PageId,
297 key: &[u8],
298) -> Result<PageId> {
299 let mut path: HeaplessVec<PageId, MAX_BTREE_DEPTH> = HeaplessVec::new();
300 let mut current = root;
301 loop {
302 path.push(current).map_err(|_| Error::BTreeDepthExceeded {
303 limit: MAX_BTREE_DEPTH,
304 })?;
305 let decoded = {
306 let pr = pager.read_page(current)?;
307 decode_node(pr.as_bytes())?
308 };
309 match decoded.kind {
310 NodeKind::Leaf => return Ok(current),
311 NodeKind::Internal => {
312 let idx = pivot_index_for_start(&decoded, key);
313 current =
314 PageId::new(decoded.children[idx]).ok_or(Error::BTreeInvariantViolated {
315 reason: "range descent: zero child page-id",
316 })?;
317 }
318 }
319 }
320}
321
322fn pivot_index_for_start(node: &DecodedNode, key: &[u8]) -> usize {
323 let mut idx = node.internals.len();
326 for (i, p) in node.internals.iter().enumerate() {
327 if p.key.as_slice() > key {
328 idx = i;
329 break;
330 }
331 }
332 idx
333}
334
335fn read_leaf<F: FileBackend>(pager: &mut Pager<F>, id: PageId) -> Result<DecodedNode> {
336 let pr = pager.read_page(id)?;
337 let decoded = decode_node(pr.as_bytes())?;
338 if !matches!(decoded.kind, NodeKind::Leaf) {
339 return Err(Error::BTreeInvariantViolated {
340 reason: "range: expected leaf",
341 });
342 }
343 Ok(decoded)
344}
345
346fn position_in_leaf(leaf: &DecodedNode, start: &Bound<Vec<u8>>) -> usize {
348 match start {
349 Bound::Unbounded => 0,
350 Bound::Included(k) => leaf
351 .leaves
352 .iter()
353 .position(|e| e.key.as_slice() >= k.as_slice())
354 .unwrap_or(leaf.leaves.len()),
355 Bound::Excluded(k) => leaf
356 .leaves
357 .iter()
358 .position(|e| e.key.as_slice() > k.as_slice())
359 .unwrap_or(leaf.leaves.len()),
360 }
361}
362
363fn within_end(key: &[u8], end: &Bound<Vec<u8>>) -> bool {
364 match end {
365 Bound::Unbounded => true,
366 Bound::Included(k) => key <= k.as_slice(),
367 Bound::Excluded(k) => key < k.as_slice(),
368 }
369}
370
371impl<F: FileBackend> Iterator for RangeIter<'_, F> {
372 type Item = Result<(Vec<u8>, Vec<u8>)>;
373
374 fn next(&mut self) -> Option<Self::Item> {
375 if self.finished {
376 return None;
377 }
378 loop {
379 let leaf = self.current_leaf.as_ref()?;
380 if self.slot_index < leaf.leaves.len() {
381 let entry = &leaf.leaves[self.slot_index];
382 if !within_end(entry.key.as_slice(), &self.end_bound) {
383 self.finished = true;
384 return None;
385 }
386 let item = (entry.key.clone(), entry.value.clone());
387 self.slot_index += 1;
388 self.last_emitted_key = Some(item.0.clone());
389 return Some(Ok(item));
390 }
391 match self.advance_to_next_leaf() {
395 Ok(true) => (),
396 Ok(false) => {
397 self.finished = true;
398 return None;
399 }
400 Err(e) => {
401 self.finished = true;
402 return Some(Err(e));
403 }
404 }
405 }
406 }
407}
408
409impl<F: FileBackend> RangeIter<'_, F> {
410 fn advance_to_next_leaf(&mut self) -> Result<bool> {
415 let Some(last) = self.last_emitted_key.clone() else {
416 return Ok(false);
419 };
420 self.nodes_visited += 1;
421 if self.nodes_visited > MAX_RANGE_NODES {
422 return Err(Error::BTreeScanLimitExceeded {
423 limit: MAX_RANGE_NODES,
424 });
425 }
426 let Some(leaf_id) = descend_to_leaf_after(self.pager, self.root, last.as_slice())? else {
427 return Ok(false);
428 };
429 let leaf = read_leaf(self.pager, leaf_id)?;
430 let slot_index = leaf
435 .leaves
436 .iter()
437 .position(|e| e.key.as_slice() > last.as_slice())
438 .unwrap_or(leaf.leaves.len());
439 if slot_index == leaf.leaves.len() {
440 return Ok(false);
441 }
442 self.current_leaf = Some(leaf);
443 self.slot_index = slot_index;
444 Ok(true)
445 }
446}
447
448fn descend_to_leaf_after<F: FileBackend>(
458 pager: &mut Pager<F>,
459 root: PageId,
460 key: &[u8],
461) -> Result<Option<PageId>> {
462 let mut frames: HeaplessVec<DescendFrame, MAX_BTREE_DEPTH> = HeaplessVec::new();
463 let mut current = root;
464 loop {
465 let decoded = {
466 let pr = pager.read_page(current)?;
467 decode_node(pr.as_bytes())?
468 };
469 match decoded.kind {
470 NodeKind::Leaf => {
471 if decoded.leaves.iter().any(|e| e.key.as_slice() > key) {
472 return Ok(Some(current));
473 }
474 break;
475 }
476 NodeKind::Internal => {
477 let child_index = pivot_index_for_start(&decoded, key);
478 let raw = decoded.children[child_index];
479 frames
480 .push(DescendFrame {
481 node: decoded,
482 child_index,
483 })
484 .map_err(|_| Error::BTreeDepthExceeded {
485 limit: MAX_BTREE_DEPTH,
486 })?;
487 current = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
488 reason: "descend_to_leaf_after: zero child id",
489 })?;
490 }
491 }
492 }
493 while let Some(frame) = frames.pop() {
496 let next_child = frame.child_index + 1;
497 if next_child < frame.node.children.len() {
498 let raw = frame.node.children[next_child];
499 let next_root = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
500 reason: "descend_to_leaf_after: zero next-child id",
501 })?;
502 return descend_leftmost_leaf(pager, next_root).map(Some);
503 }
504 }
505 Ok(None)
506}
507
508struct DescendFrame {
509 node: DecodedNode,
510 child_index: usize,
511}
512
513fn descend_leftmost_leaf<F: FileBackend>(pager: &mut Pager<F>, root: PageId) -> Result<PageId> {
515 let mut path: HeaplessVec<PageId, MAX_BTREE_DEPTH> = HeaplessVec::new();
516 let mut current = root;
517 loop {
518 path.push(current).map_err(|_| Error::BTreeDepthExceeded {
519 limit: MAX_BTREE_DEPTH,
520 })?;
521 let decoded = {
522 let pr = pager.read_page(current)?;
523 decode_node(pr.as_bytes())?
524 };
525 match decoded.kind {
526 NodeKind::Leaf => return Ok(current),
527 NodeKind::Internal => {
528 let raw = decoded.children[0];
529 current = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
530 reason: "descend_leftmost_leaf: zero child id",
531 })?;
532 }
533 }
534 }
535}
536
537pub struct SnapshotRangeIter<'a, F: FileBackend> {
556 pager: &'a Pager<F>,
557 snapshot: &'a crate::pager::ReaderSnapshot<F>,
558 root: PageId,
561 current_leaf: Option<DecodedNode>,
562 slot_index: usize,
563 start_bound: Bound<Vec<u8>>,
564 end_bound: Bound<Vec<u8>>,
565 last_emitted_key: Option<Vec<u8>>,
566 nodes_visited: usize,
567 finished: bool,
568}
569
570impl<F: FileBackend> std::fmt::Debug for SnapshotRangeIter<'_, F> {
571 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572 f.debug_struct("SnapshotRangeIter")
573 .field("slot_index", &self.slot_index)
574 .field("start_bound", &self.start_bound)
575 .field("end_bound", &self.end_bound)
576 .field("nodes_visited", &self.nodes_visited)
577 .field("finished", &self.finished)
578 .finish()
579 }
580}
581
582impl<F: FileBackend> Iterator for SnapshotRangeIter<'_, F> {
583 type Item = Result<(Vec<u8>, Vec<u8>)>;
584
585 fn next(&mut self) -> Option<Self::Item> {
586 if self.finished {
587 return None;
588 }
589 loop {
590 let leaf = self.current_leaf.as_ref()?;
591 if self.slot_index < leaf.leaves.len() {
592 let entry = &leaf.leaves[self.slot_index];
593 if !within_end(entry.key.as_slice(), &self.end_bound) {
594 self.finished = true;
595 return None;
596 }
597 let item = (entry.key.clone(), entry.value.clone());
598 self.slot_index += 1;
599 self.last_emitted_key = Some(item.0.clone());
600 return Some(Ok(item));
601 }
602 match self.advance_to_next_leaf() {
603 Ok(true) => (),
604 Ok(false) => {
605 self.finished = true;
606 return None;
607 }
608 Err(e) => {
609 self.finished = true;
610 return Some(Err(e));
611 }
612 }
613 }
614 }
615}
616
617impl<F: FileBackend> SnapshotRangeIter<'_, F> {
618 fn advance_to_next_leaf(&mut self) -> Result<bool> {
621 let Some(last) = self.last_emitted_key.clone() else {
622 return Ok(false);
623 };
624 self.nodes_visited += 1;
625 if self.nodes_visited > MAX_RANGE_NODES {
626 return Err(Error::BTreeScanLimitExceeded {
627 limit: MAX_RANGE_NODES,
628 });
629 }
630 let Some(leaf_id) =
631 snap_descend_to_leaf_after(self.pager, self.snapshot, self.root, last.as_slice())?
632 else {
633 return Ok(false);
634 };
635 let leaf = snap_read_leaf(self.pager, self.snapshot, leaf_id)?;
636 let slot_index = leaf
637 .leaves
638 .iter()
639 .position(|e| e.key.as_slice() > last.as_slice())
640 .unwrap_or(leaf.leaves.len());
641 if slot_index == leaf.leaves.len() {
642 return Ok(false);
643 }
644 self.current_leaf = Some(leaf);
645 self.slot_index = slot_index;
646 Ok(true)
647 }
648}
649
650fn snap_locate_first_in_range_leaf<F: FileBackend>(
652 pager: &Pager<F>,
653 snapshot: &crate::pager::ReaderSnapshot<F>,
654 root: PageId,
655 start_bound: &Bound<Vec<u8>>,
656) -> Result<(DecodedNode, usize, usize)> {
657 let descend_key = match start_bound {
658 Bound::Included(k) | Bound::Excluded(k) => k.as_slice(),
659 Bound::Unbounded => &[][..],
660 };
661 let leaf_id = snap_descend_to_start_leaf(pager, snapshot, root, descend_key)?;
662 let mut leaf = snap_read_leaf(pager, snapshot, leaf_id)?;
663 let mut slot_index = position_in_leaf(&leaf, start_bound);
664 let mut nodes_visited: usize = 1;
665 while slot_index >= leaf.leaves.len() {
666 let Some(last_key) = leaf.leaves.last().map(|e| e.key.clone()) else {
667 break;
668 };
669 nodes_visited += 1;
670 if nodes_visited > MAX_RANGE_NODES {
671 return Err(Error::BTreeScanLimitExceeded {
672 limit: MAX_RANGE_NODES,
673 });
674 }
675 let Some(next_id) = snap_descend_to_leaf_after(pager, snapshot, root, last_key.as_slice())?
676 else {
677 break;
678 };
679 leaf = snap_read_leaf(pager, snapshot, next_id)?;
680 slot_index = position_in_leaf(&leaf, start_bound);
681 }
682 Ok((leaf, slot_index, nodes_visited))
683}
684
685fn snap_descend_to_start_leaf<F: FileBackend>(
687 pager: &Pager<F>,
688 snapshot: &crate::pager::ReaderSnapshot<F>,
689 root: PageId,
690 key: &[u8],
691) -> Result<PageId> {
692 let mut path: HeaplessVec<PageId, MAX_BTREE_DEPTH> = HeaplessVec::new();
693 let mut current = root;
694 loop {
695 path.push(current).map_err(|_| Error::BTreeDepthExceeded {
696 limit: MAX_BTREE_DEPTH,
697 })?;
698 let page = snapshot.read_page(pager, current)?;
699 let decoded = decode_node(page.as_bytes())?;
700 match decoded.kind {
701 NodeKind::Leaf => return Ok(current),
702 NodeKind::Internal => {
703 let idx = pivot_index_for_start(&decoded, key);
704 current =
705 PageId::new(decoded.children[idx]).ok_or(Error::BTreeInvariantViolated {
706 reason: "range descent: zero child page-id",
707 })?;
708 }
709 }
710 }
711}
712
713fn snap_read_leaf<F: FileBackend>(
715 pager: &Pager<F>,
716 snapshot: &crate::pager::ReaderSnapshot<F>,
717 id: PageId,
718) -> Result<DecodedNode> {
719 let page = snapshot.read_page(pager, id)?;
720 let decoded = decode_node(page.as_bytes())?;
721 if !matches!(decoded.kind, NodeKind::Leaf) {
722 return Err(Error::BTreeInvariantViolated {
723 reason: "range: expected leaf",
724 });
725 }
726 Ok(decoded)
727}
728
729fn snap_descend_to_leaf_after<F: FileBackend>(
731 pager: &Pager<F>,
732 snapshot: &crate::pager::ReaderSnapshot<F>,
733 root: PageId,
734 key: &[u8],
735) -> Result<Option<PageId>> {
736 let mut frames: HeaplessVec<DescendFrame, MAX_BTREE_DEPTH> = HeaplessVec::new();
737 let mut current = root;
738 loop {
739 let page = snapshot.read_page(pager, current)?;
740 let decoded = decode_node(page.as_bytes())?;
741 match decoded.kind {
742 NodeKind::Leaf => {
743 if decoded.leaves.iter().any(|e| e.key.as_slice() > key) {
744 return Ok(Some(current));
745 }
746 break;
747 }
748 NodeKind::Internal => {
749 let child_index = pivot_index_for_start(&decoded, key);
750 let raw = decoded.children[child_index];
751 frames
752 .push(DescendFrame {
753 node: decoded,
754 child_index,
755 })
756 .map_err(|_| Error::BTreeDepthExceeded {
757 limit: MAX_BTREE_DEPTH,
758 })?;
759 current = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
760 reason: "descend_to_leaf_after: zero child id",
761 })?;
762 }
763 }
764 }
765 while let Some(frame) = frames.pop() {
766 let next_child = frame.child_index + 1;
767 if next_child < frame.node.children.len() {
768 let raw = frame.node.children[next_child];
769 let next_root = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
770 reason: "descend_to_leaf_after: zero next-child id",
771 })?;
772 return snap_descend_leftmost_leaf(pager, snapshot, next_root).map(Some);
773 }
774 }
775 Ok(None)
776}
777
778fn snap_descend_leftmost_leaf<F: FileBackend>(
780 pager: &Pager<F>,
781 snapshot: &crate::pager::ReaderSnapshot<F>,
782 root: PageId,
783) -> Result<PageId> {
784 let mut path: HeaplessVec<PageId, MAX_BTREE_DEPTH> = HeaplessVec::new();
785 let mut current = root;
786 loop {
787 path.push(current).map_err(|_| Error::BTreeDepthExceeded {
788 limit: MAX_BTREE_DEPTH,
789 })?;
790 let page = snapshot.read_page(pager, current)?;
791 let decoded = decode_node(page.as_bytes())?;
792 match decoded.kind {
793 NodeKind::Leaf => return Ok(current),
794 NodeKind::Internal => {
795 let raw = decoded.children[0];
796 current = PageId::new(raw).ok_or(Error::BTreeInvariantViolated {
797 reason: "descend_leftmost_leaf: zero child id",
798 })?;
799 }
800 }
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807 use crate::pager::{Config, Pager};
808 use crate::platform::FileHandle;
809
810 use rand::Rng;
811 use rand::SeedableRng;
812 use rand_chacha::ChaCha8Rng;
813 use std::collections::BTreeMap;
814 use std::ops::Bound;
815
816 fn config() -> Config {
817 Config::default()
818 }
819
820 fn collect_all(
821 pager: &mut Pager<FileHandle>,
822 tree: &BTree<FileHandle>,
823 ) -> Vec<(Vec<u8>, Vec<u8>)> {
824 let iter = tree.iter(pager).expect("iter");
825 iter.map(|r| r.expect("step")).collect()
826 }
827
828 #[test]
829 fn iter_empty_tree() {
830 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
831 let tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
832 let v = collect_all(&mut pager, &tree);
833 assert!(v.is_empty());
834 }
835
836 #[test]
837 fn iter_single_leaf() {
838 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
839 let mut tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
840 for (k, v) in [("alpha", "A"), ("bravo", "B"), ("charlie", "C")] {
841 tree.insert(&mut pager, k.as_bytes(), v.as_bytes())
842 .expect("ins");
843 }
844 let got = collect_all(&mut pager, &tree);
845 let want: Vec<(Vec<u8>, Vec<u8>)> = [("alpha", "A"), ("bravo", "B"), ("charlie", "C")]
846 .iter()
847 .map(|(k, v)| (k.as_bytes().to_vec(), v.as_bytes().to_vec()))
848 .collect();
849 assert_eq!(got, want);
850 }
851
852 #[test]
853 fn range_across_leaf_split_boundary() {
854 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
856 let mut tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
857 let value = vec![0xAAu8; 256];
858 for i in 0..50u32 {
859 let key = format!("key-{i:08}");
860 tree.insert(&mut pager, key.as_bytes(), &value)
861 .expect("ins");
862 }
863 let root = tree.root();
865 let root_decoded = {
866 let pr = pager.read_page(root).expect("read");
867 decode_node(pr.as_bytes()).expect("dec")
868 };
869 assert!(root_decoded.level >= 1, "expected root split");
870 let iter = tree.iter(&mut pager).expect("iter");
872 let mut prev: Option<Vec<u8>> = None;
873 let mut count = 0;
874 for item in iter {
875 let (k, _v) = item.expect("step");
876 if let Some(p) = &prev {
877 assert!(p.as_slice() < k.as_slice(), "non-monotonic at {k:?}");
878 }
879 prev = Some(k);
880 count += 1;
881 }
882 assert_eq!(count, 50);
883 }
884
885 #[test]
886 fn range_bounded_subset() {
887 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
888 let mut tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
889 for i in 0..20u32 {
890 let key = format!("k-{i:03}");
891 tree.insert(&mut pager, key.as_bytes(), b"v").expect("ins");
892 }
893 let iter = tree
895 .range(
896 &mut pager,
897 (
898 Bound::Included(b"k-005".to_vec()),
899 Bound::Excluded(b"k-010".to_vec()),
900 ),
901 )
902 .expect("range");
903 let got: Vec<String> = iter
904 .map(|r| String::from_utf8(r.expect("step").0).expect("utf8"))
905 .collect();
906 assert_eq!(got, vec!["k-005", "k-006", "k-007", "k-008", "k-009"]);
907 }
908
909 #[test]
910 fn range_oracle_1000_ops() {
911 for seed in 0..3u64 {
912 run_range_oracle(seed, 1_000);
913 }
914 }
915
916 #[test]
931 fn range_start_bound_past_landing_leaf_end() {
932 let (mut pager, tree, value) = build_split_fixture();
933 let (start_key, want) = construct_past_landing_leaf_query(&mut pager, &tree, &value);
934 let iter = tree
935 .range(
936 &mut pager,
937 (Bound::Included(start_key), Bound::<Vec<u8>>::Unbounded),
938 )
939 .expect("range");
940 let got: Vec<(Vec<u8>, Vec<u8>)> = iter.map(|r| r.expect("step")).collect();
941 assert_eq!(got, want, "iterator must advance past empty landing leaf");
942 }
943
944 fn build_split_fixture() -> (Pager<FileHandle>, BTree<FileHandle>, Vec<u8>) {
947 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
948 let mut tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
949 let value = vec![0xCDu8; 256];
950 for i in 0..50u32 {
951 let key = format!("key-{i:08}");
952 tree.insert(&mut pager, key.as_bytes(), &value)
953 .expect("ins");
954 }
955 (pager, tree, value)
956 }
957
958 type ExpectedRangeResult = Vec<(Vec<u8>, Vec<u8>)>;
959
960 fn construct_past_landing_leaf_query(
965 pager: &mut Pager<FileHandle>,
966 tree: &BTree<FileHandle>,
967 value: &[u8],
968 ) -> (Vec<u8>, ExpectedRangeResult) {
969 let root_decoded = {
970 let pr = pager.read_page(tree.root()).expect("read");
971 decode_node(pr.as_bytes()).expect("dec")
972 };
973 assert!(root_decoded.level >= 1, "fixture must split the tree");
974 let leftmost_id = PageId::new(root_decoded.children[0]).expect("nz");
975 let leftmost = read_leaf(pager, leftmost_id).expect("leaf");
976 let last_in_leftmost = leftmost.leaves.last().expect("non-empty").key.clone();
977 let pivot = root_decoded.internals[0].key.clone();
978 let mut start_key = last_in_leftmost.clone();
982 start_key.push(0);
983 assert!(start_key.as_slice() > last_in_leftmost.as_slice());
984 assert!(start_key.as_slice() < pivot.as_slice());
985 let oracle: BTreeMap<Vec<u8>, Vec<u8>> = (0..50u32)
986 .map(|i| (format!("key-{i:08}").into_bytes(), value.to_vec()))
987 .collect();
988 let want: Vec<(Vec<u8>, Vec<u8>)> = oracle
989 .range::<Vec<u8>, _>((Bound::Included(&start_key), Bound::<&Vec<u8>>::Unbounded))
990 .map(|(k, v)| (k.clone(), v.clone()))
991 .collect();
992 assert!(!want.is_empty(), "oracle must return sibling-leaf entries");
993 (start_key, want)
994 }
995
996 fn run_range_oracle(seed: u64, ops: usize) {
997 let mut rng = ChaCha8Rng::seed_from_u64(seed);
998 let mut pager = Pager::<FileHandle>::memory(config()).expect("pager");
999 let mut tree = BTree::<FileHandle>::empty(&mut pager).expect("empty");
1000 let mut oracle: BTreeMap<Vec<u8>, Vec<u8>> = BTreeMap::new();
1001 for op in 0..ops {
1002 match rng.random_range(0u32..6) {
1003 0..=2 => insert_step(&mut pager, &mut tree, &mut oracle, &mut rng, seed, op),
1004 3 => delete_step(&mut pager, &mut tree, &mut oracle, &mut rng, seed, op),
1005 _ => range_check(&mut pager, &tree, &oracle, &mut rng, seed, op),
1006 }
1007 }
1008 let got = collect_all(&mut pager, &tree);
1010 let want: Vec<(Vec<u8>, Vec<u8>)> =
1011 oracle.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
1012 assert_eq!(got, want, "seed {seed}: final iter mismatch");
1013 }
1014
1015 fn insert_step(
1016 pager: &mut Pager<FileHandle>,
1017 tree: &mut BTree<FileHandle>,
1018 oracle: &mut BTreeMap<Vec<u8>, Vec<u8>>,
1019 rng: &mut ChaCha8Rng,
1020 seed: u64,
1021 op: usize,
1022 ) {
1023 let key = random_key(rng);
1024 let value = random_value(rng);
1025 if let std::collections::btree_map::Entry::Vacant(slot) = oracle.entry(key.clone()) {
1026 tree.insert(pager, &key, &value)
1027 .unwrap_or_else(|e| panic!("seed {seed} op {op} ins: {e:?}"));
1028 slot.insert(value);
1029 }
1030 }
1031
1032 fn delete_step(
1033 pager: &mut Pager<FileHandle>,
1034 tree: &mut BTree<FileHandle>,
1035 oracle: &mut BTreeMap<Vec<u8>, Vec<u8>>,
1036 rng: &mut ChaCha8Rng,
1037 seed: u64,
1038 op: usize,
1039 ) {
1040 if oracle.is_empty() {
1041 return;
1042 }
1043 let pick = rng.random_range(0..oracle.len());
1044 let key = oracle.keys().nth(pick).cloned().unwrap_or_default();
1045 oracle.remove(&key);
1046 tree.delete(pager, &key)
1047 .unwrap_or_else(|e| panic!("seed {seed} op {op} del: {e:?}"));
1048 }
1049
1050 fn range_check(
1051 pager: &mut Pager<FileHandle>,
1052 tree: &BTree<FileHandle>,
1053 oracle: &BTreeMap<Vec<u8>, Vec<u8>>,
1054 rng: &mut ChaCha8Rng,
1055 seed: u64,
1056 op: usize,
1057 ) {
1058 let (start, end) = random_bounds(rng);
1059 if !bounds_well_ordered(&start, &end) {
1064 return;
1065 }
1066 let iter = tree
1067 .range(pager, (start.clone(), end.clone()))
1068 .expect("range");
1069 let got: Vec<(Vec<u8>, Vec<u8>)> = iter.map(|r| r.expect("step")).collect();
1070 let want: Vec<(Vec<u8>, Vec<u8>)> = oracle
1071 .range::<Vec<u8>, _>((start.as_ref(), end.as_ref()))
1072 .map(|(k, v)| (k.clone(), v.clone()))
1073 .collect();
1074 assert_eq!(got, want, "seed {seed} op {op}: range mismatch");
1075 }
1076
1077 fn bounds_well_ordered(start: &Bound<Vec<u8>>, end: &Bound<Vec<u8>>) -> bool {
1078 match (start, end) {
1079 (Bound::Unbounded, _) | (_, Bound::Unbounded) => true,
1080 (Bound::Excluded(s), Bound::Excluded(e)) => s.as_slice() < e.as_slice(),
1081 (Bound::Included(s) | Bound::Excluded(s), Bound::Included(e) | Bound::Excluded(e)) => {
1082 s.as_slice() <= e.as_slice()
1083 }
1084 }
1085 }
1086
1087 fn random_bounds(rng: &mut ChaCha8Rng) -> (Bound<Vec<u8>>, Bound<Vec<u8>>) {
1088 let s = match rng.random_range(0u32..3) {
1089 0 => Bound::Unbounded,
1090 1 => Bound::Included(random_key(rng)),
1091 _ => Bound::Excluded(random_key(rng)),
1092 };
1093 let e = match rng.random_range(0u32..3) {
1094 0 => Bound::Unbounded,
1095 1 => Bound::Included(random_key(rng)),
1096 _ => Bound::Excluded(random_key(rng)),
1097 };
1098 (s, e)
1099 }
1100
1101 fn random_key(rng: &mut ChaCha8Rng) -> Vec<u8> {
1102 let len = rng.random_range(1..6);
1103 (0..len).map(|_| rng.random_range(b'a'..=b'c')).collect()
1104 }
1105
1106 fn random_value(rng: &mut ChaCha8Rng) -> Vec<u8> {
1107 let len = rng.random_range(0..16);
1108 (0..len).map(|_| rng.random()).collect()
1109 }
1110}