Skip to main content

llkv_btree/
iter.rs

1//! Unified, zero-alloc iterator for BPlusTree.
2//!
3//! Single iterator type that supports forward, reverse, range, and  prefix scanning.
4//!
5//! It yields (KeyRef, ValueRef) with no allocations.
6//!
7// TODO: Clarify this. Does the pager really control this, and doesn't `NodeCache` contribute
8// to the residency size, and does `SharedBPlusTree` bounded channel size contribute as well?
9// Only one leaf page is resident at a time (pager controls that).
10use crate::bplus_tree::{BPlusTree, Node};
11use crate::codecs::{IdCodec, KeyCodec};
12use crate::errors::Error;
13use crate::pager::Pager;
14use crate::{
15    types::{CursorStepRes, EntryRefRes, FramePred, NodeWithNextRes},
16    views::key_view::KeyRef,
17    views::node_view::{NodeTag, NodeView},
18    views::value_view::ValueRef,
19};
20use core::cmp::Ordering;
21use core::marker::PhantomData;
22use core::ops::Bound;
23use core::ops::Bound::{Excluded, Included, Unbounded};
24use std::sync::Arc;
25
26// ------------------------------------------------------------------
27// Direction and scan options
28// ------------------------------------------------------------------
29
30#[derive(Copy, Clone, Debug, Eq, PartialEq)]
31pub enum Direction {
32    Forward,
33    Reverse,
34}
35
36/// Configuration for a key-ordered scan over a B+Tree.
37///
38/// A scan walks keys in logical order and yields zero-copy `(KeyRef,
39/// ValueRef)` pairs.
40/// The same `ScanOpts` works for both forward and
41/// reverse scans, whole-tree scans, bounded range scans, and prefix
42/// scans.
43/// Options are combined as an intersection (all active filters
44/// must match).
45///
46/// Fields
47/// ------
48/// - `dir`: Scan direction.
49///   `Forward` yields ascending keys; `Reverse` yields descending keys.
50/// - `lower`: Lower bound (`Unbounded`, `Included(&K)`, or `Excluded(&K)`).
51///   When `Unbounded`, the scan starts from the smallest key (or, for
52///   `Reverse`, the chosen start is governed by `upper`).
53/// - `upper`: Upper bound (`Unbounded`, `Included(&K)`, or `Excluded(&K)`).
54///   When `Unbounded`, the scan runs to the end in the selected direction.
55/// - `prefix`: Optional byte prefix filter over the *encoded* key
56///   bytes. Only entries whose encoded key starts with this prefix are
57///   yielded. If set together with bounds, the result is the
58///   intersection of the prefix filter and the range.
59/// - `frame_predicate`: Optional, stateful termination predicate. When
60///   provided, the iterator captures the **first yielded key's encoded
61///   bytes** as the frame head and will continue yielding **while**
62///   `pred(head_enc, cur_enc)` returns true. As soon as it returns
63///   false, the iterator ends. This lets you express window/partition
64///   boundaries without extra branching at the call site.
65///
66/// Semantics
67/// ---------
68/// - Ordering is defined by the tree's `KeyCodec::compare_encoded`.
69///   Ranges (`lower`/`upper`) are applied using the same comparison,
70///   so they are correct for non-lexicographic encodings as well.
71/// - Bounds are expressed symmetrically using `Bound`, matching Rust’s
72///   `RangeBounds` idioms. (`Included`/`Excluded`/`Unbounded`)
73/// - When `lower` > `upper` (according to `KeyCodec`), the result is
74///   empty.
75/// - If both `prefix` and bounds are set, both must match.
76///   If the `KeyCodec`'s byte encoding is lex-ordered (e.g., UTF-8 strings),
77///   a prefix typically corresponds to a contiguous key range; for
78///   other encodings, prefix is still applied as a byte-filter but may
79///   not map to a single contiguous range.
80/// - Results reflect the tree state at iterator creation time (mutations
81///   after creation are not included by that iterator).
82///
83/// Performance notes
84/// -----------------
85/// - Scans advance leaf-by-leaf. With the columnar leaf layout, key
86///   comparisons are branch-light and SIMD fast paths may engage for
87///   fixed-width u64 keys when available. Value payloads are returned
88///   as zero-copy `ValueRef` slices.
89/// - Range bounds are resolved with binary search inside the starting
90///   leaf; subsequent leaves run sequentially until the bound or prefix
91///   fails.
92/// - Prefix scans avoid decoding full keys where possible by comparing
93///   the encoded key bytes against `prefix`.
94///
95/// Examples
96/// --------
97/// Full forward scan:
98/// ```ignore
99/// use llkv_btree::{BPlusTreeIter, ScanOpts, Direction};
100/// let it = BPlusTreeIter::with_opts(&tree, ScanOpts {
101///     dir: Direction::Forward,
102///     lower: core::ops::Bound::Unbounded,
103///     upper: core::ops::Bound::Unbounded,
104///     prefix: None,
105///     frame_predicate: None,
106/// });
107/// for (kref, vref) in it { /* use kref.as_ref(), vref.as_ref() */ }
108/// ```
109///
110/// Forward range `[10, 20)` (exclusive upper):
111/// ```ignore
112/// use llkv_btree::{BPlusTreeIter, ScanOpts, Direction};
113/// use core::ops::Bound::{Included, Excluded, Unbounded};
114/// let lo = 10u64;
115/// let up = 20u64;
116/// let it = BPlusTreeIter::with_opts(&tree, ScanOpts {
117///     dir: Direction::Forward,
118///     lower: Included(&lo),
119///     upper: Excluded(&up),
120///     prefix: None,
121///     frame_predicate: None,
122/// });
123/// ```
124///
125/// Reverse range `(..=100]` (inclusive upper, unbounded lower):
126/// ```ignore
127/// use llkv_btree::{BPlusTreeIter, ScanOpts, Direction};
128/// use core::ops::Bound::{Included, Unbounded};
129/// let up = 100u64;
130/// let it = BPlusTreeIter::with_opts(&tree, ScanOpts {
131///     dir: Direction::Reverse,
132///     lower: Unbounded,
133///     upper: Included(&up),
134///     prefix: None,
135///     frame_predicate: None,
136/// });
137/// ```
138///
139/// Prefix-only scan (encoded-byte prefix):
140/// ```ignore
141/// use llkv_btree::{BPlusTreeIter, ScanOpts, Direction};
142/// let it = BPlusTreeIter::with_opts(&tree, ScanOpts {
143///     dir: Direction::Forward,
144///     lower: core::ops::Bound::Unbounded,
145///     upper: core::ops::Bound::Unbounded,
146///     prefix: Some(b"ap"),
147///     frame_predicate: None,
148/// });
149/// ```
150///
151/// Frame/partition scan (stop when the first 8 key bytes change):
152/// ```ignore
153/// use llkv_btree::{BPlusTreeIter, ScanOpts, Direction};
154/// let it = BPlusTreeIter::with_opts(
155///     &tree,
156///     ScanOpts::forward().with_frame_predicate(|head, cur| cur.get(..8) == head.get(..8)),
157/// ).unwrap();
158/// ```
159///
160/// Errors
161/// ------
162/// - Returns an error if the pager fails to read a page or the page
163///   bytes are corrupted.
164/// - Creating an iterator on an empty tree is ok; iteration yields no
165///   items.
166#[derive(Clone)]
167pub struct ScanOpts<'a, KC>
168where
169    KC: KeyCodec,
170{
171    pub dir: Direction,
172    pub lower: Bound<&'a KC::Key>,
173    pub upper: Bound<&'a KC::Key>,
174    pub prefix: Option<&'a [u8]>,
175
176    /// Optional end-of-frame predicate on **encoded** keys.
177    /// The iterator saves the encoded bytes of the **first yielded** key as the
178    /// head; for each subsequent candidate key `cur`, it continues while
179    /// `pred(head, cur)` is true. When it returns false, the iterator ends.
180    /// This is `Arc<dyn ... + Send + Sync>` so you can pass it across threads
181    /// from `SharedBPlusTree::start_stream_with_opts`.
182    pub frame_predicate: Option<FramePred>,
183}
184
185impl<'a, KC> Default for ScanOpts<'a, KC>
186where
187    KC: KeyCodec,
188{
189    fn default() -> Self {
190        Self {
191            dir: Direction::Forward,
192            lower: Unbounded,
193            upper: Unbounded,
194            prefix: None,
195            frame_predicate: None,
196        }
197    }
198}
199
200impl<'a, KC> ScanOpts<'a, KC>
201where
202    KC: KeyCodec,
203{
204    #[inline]
205    pub fn forward() -> Self {
206        Self {
207            dir: Direction::Forward,
208            ..Default::default()
209        }
210    }
211
212    #[inline]
213    pub fn reverse() -> Self {
214        Self {
215            dir: Direction::Reverse,
216            ..Default::default()
217        }
218    }
219
220    // -------- zero-alloc builder helpers (borrow K / prefix) --------
221    #[inline]
222    pub fn start_at(mut self, k: &'a KC::Key) -> Self {
223        self.lower = Included(k);
224        self
225    }
226    #[inline]
227    pub fn start_after(mut self, k: &'a KC::Key) -> Self {
228        self.lower = Excluded(k);
229        self
230    }
231    #[inline]
232    pub fn end_at(mut self, k: &'a KC::Key) -> Self {
233        self.upper = Included(k);
234        self
235    }
236    #[inline]
237    pub fn end_before(mut self, k: &'a KC::Key) -> Self {
238        self.upper = Excluded(k);
239        self
240    }
241    #[inline]
242    pub fn with_prefix(mut self, p: &'a [u8]) -> Self {
243        self.prefix = Some(p);
244        self
245    }
246    #[inline]
247    pub fn with_bounds(mut self, lo: Bound<&'a KC::Key>, up: Bound<&'a KC::Key>) -> Self {
248        self.lower = lo;
249        self.upper = up;
250        self
251    }
252
253    /// Continue the scan while `pred(head, cur)` is true. Ends the scan as
254    /// soon as it returns false. Predicate receives **encoded** key bytes.
255    #[inline]
256    pub fn with_frame_predicate<F>(mut self, pred: F) -> Self
257    where
258        F: Fn(&[u8], &[u8]) -> bool + Send + Sync + 'static,
259    {
260        self.frame_predicate = Some(Arc::new(pred));
261        self
262    }
263}
264
265// ------------------------------------------------------------------
266// ValueResolver: read nodes/pages and build zero-copy entry views
267// ------------------------------------------------------------------
268
269pub trait ValueResolver<'a, P, KC, IC>
270where
271    P: Pager,
272    KC: KeyCodec,
273    IC: IdCodec<Id = P::Id>,
274{
275    fn read_node(&self, id: &P::Id) -> Result<Node<KC::Key, P::Id>, Error>;
276    fn read_one(&self, id: &P::Id) -> Result<P::Page, Error>;
277    fn root_id(&self) -> P::Id;
278
279    /// Produce (KeyRef, ValueRef) for entry `i` without allocation.
280    fn entry_at(&self, page: P::Page, view: &NodeView<P>, i: usize) -> EntryRefRes<P>;
281}
282
283// Resolver for a plain BPlusTree: value bytes live in the leaf entry.
284pub struct PlainResolver<'a, P, KC, IC>
285where
286    P: Pager,
287    KC: KeyCodec,
288    IC: IdCodec<Id = P::Id>,
289{
290    tree: &'a BPlusTree<P, KC, IC>,
291}
292
293impl<'a, P, KC, IC> PlainResolver<'a, P, KC, IC>
294where
295    P: Pager,
296    KC: KeyCodec,
297    IC: IdCodec<Id = P::Id>,
298{
299    #[inline]
300    pub fn new(tree: &'a BPlusTree<P, KC, IC>) -> Self {
301        Self { tree }
302    }
303}
304
305impl<'a, P, KC, IC> ValueResolver<'a, P, KC, IC> for PlainResolver<'a, P, KC, IC>
306where
307    P: Pager,
308    KC: KeyCodec,
309    IC: IdCodec<Id = P::Id>,
310{
311    #[inline]
312    fn read_node(&self, id: &P::Id) -> Result<Node<KC::Key, P::Id>, Error> {
313        self.tree.read_node(id)
314    }
315
316    #[inline]
317    fn read_one(&self, id: &P::Id) -> Result<P::Page, Error> {
318        self.tree.read_one(id)
319    }
320
321    #[inline]
322    fn root_id(&self) -> P::Id {
323        self.tree.root_id()
324    }
325
326    #[inline]
327    fn entry_at(
328        &self,
329        page: P::Page,
330        view: &NodeView<P>,
331        i: usize,
332    ) -> Result<(KeyRef<P::Page>, ValueRef<P::Page>), Error> {
333        let (k_enc, _v_enc) = view.leaf_entry_slices(i);
334        let kref = KeyRef::from_subslice(page.clone(), k_enc);
335        let vr = view.leaf_entry_value_range(i);
336        let vref = ValueRef::new(page, vr);
337        Ok((kref, vref))
338    }
339}
340
341// ------------------------------------------------------------------
342// Unified iterator
343// ------------------------------------------------------------------
344
345/// One iterator that supports forward, reverse, range, and prefix.
346/// It yields zero-copy (KeyRef, ValueRef).
347pub struct ScanIter<'a, R, P, KC, IC>
348where
349    R: ValueResolver<'a, P, KC, IC>,
350    P: Pager,
351    KC: KeyCodec,
352    IC: IdCodec<Id = P::Id>,
353{
354    resolver: R,
355
356    // Current leaf state
357    cur_page: Option<P::Page>,
358    cur_view: Option<NodeView<P>>,
359    cur_count: usize,
360
361    // Forward index (0..cur_count)
362    pos: usize,
363
364    // Reverse cursor: first index after the current element
365    pos_after: usize,
366
367    // Cached next leaf id
368    leaf_id: Option<P::Id>,
369
370    // Bounds and filters
371    lower: Bound<&'a KC::Key>,
372    upper: Bound<&'a KC::Key>,
373    prefix: Option<&'a [u8]>,
374
375    // Frame termination predicate and captured head key bytes.
376    frame_predicate: Option<FramePred>,
377    first_key_enc: Option<Vec<u8>>,
378
379    dir: Direction,
380    _p: PhantomData<(KC, IC)>,
381}
382
383impl<'a, R, P, KC, IC> ScanIter<'a, R, P, KC, IC>
384where
385    R: ValueResolver<'a, P, KC, IC>,
386    P: Pager,
387    KC: KeyCodec,
388    IC: IdCodec<Id = P::Id>,
389{
390    pub fn from_parts(resolver: R, opts: ScanOpts<'a, KC>) -> Result<Self, Error> {
391        {
392            let root = resolver.root_id();
393            if resolver.read_node(&root)?.entry_count() == 0 {
394                return Ok(Self {
395                    resolver,
396                    cur_page: None,
397                    cur_view: None,
398                    cur_count: 0,
399                    pos: 0,
400                    pos_after: 0,
401                    leaf_id: None,
402                    lower: opts.lower,
403                    upper: opts.upper,
404                    prefix: opts.prefix,
405                    frame_predicate: opts.frame_predicate,
406                    first_key_enc: None,
407                    dir: opts.dir,
408                    _p: PhantomData,
409                });
410            }
411        }
412
413        match opts.dir {
414            Direction::Forward => {
415                let (page, view, pos, leaf_id) =
416                    descend_lower_pos::<R, P, KC, IC>(&resolver, opts.lower)?;
417                let count = view.count();
418                Ok(Self {
419                    resolver,
420                    cur_page: Some(page),
421                    cur_view: Some(view),
422                    cur_count: count,
423                    pos,
424                    pos_after: 0,
425                    leaf_id,
426                    lower: opts.lower,
427                    upper: opts.upper,
428                    prefix: opts.prefix,
429                    frame_predicate: opts.frame_predicate,
430                    first_key_enc: None,
431                    dir: opts.dir,
432                    _p: PhantomData,
433                })
434            }
435            Direction::Reverse => {
436                let (page, view, pos_after, leaf_id) =
437                    descend_from_upper::<R, P, KC, IC>(&resolver, opts.upper)?;
438                let count = view.count();
439                Ok(Self {
440                    resolver,
441                    cur_page: Some(page),
442                    cur_view: Some(view),
443                    cur_count: count,
444                    pos: 0,
445                    pos_after,
446                    leaf_id,
447                    lower: opts.lower,
448                    upper: opts.upper,
449                    prefix: opts.prefix,
450                    frame_predicate: opts.frame_predicate,
451                    first_key_enc: None,
452                    dir: opts.dir,
453                    _p: PhantomData,
454                })
455            }
456        }
457    }
458
459    #[inline]
460    pub fn with_resolver(resolver: R, opts: ScanOpts<'a, KC>) -> Result<Self, Error> {
461        Self::from_parts(resolver, opts)
462    }
463
464    #[inline]
465    fn within_upper(&self, view: &NodeView<P>, i: usize) -> bool {
466        match self.upper {
467            Unbounded => true,
468            Included(up) => {
469                let (k_enc, _) = view.leaf_entry_slices(i);
470                KC::compare_encoded(k_enc, up) != Ordering::Greater
471            }
472            Excluded(up) => {
473                let (k_enc, _) = view.leaf_entry_slices(i);
474                KC::compare_encoded(k_enc, up) == Ordering::Less
475            }
476        }
477    }
478
479    #[inline]
480    fn within_lower(&self, view: &NodeView<P>, i: usize) -> bool {
481        match self.lower {
482            Unbounded => true,
483            Included(lo) => {
484                let (k_enc, _) = view.leaf_entry_slices(i);
485                KC::compare_encoded(k_enc, lo) != Ordering::Less
486            }
487            Excluded(lo) => {
488                let (k_enc, _) = view.leaf_entry_slices(i);
489                KC::compare_encoded(k_enc, lo) == Ordering::Greater
490            }
491        }
492    }
493
494    #[inline]
495    fn within_prefix(&self, view: &NodeView<P>, i: usize) -> bool {
496        if let Some(pref) = self.prefix {
497            let (k_enc, _) = view.leaf_entry_slices(i);
498            k_enc.starts_with(pref)
499        } else {
500            true
501        }
502    }
503
504    fn advance_leaf_forward(&mut self) -> Result<bool, Error> {
505        let mut nid = match self.leaf_id.take() {
506            Some(id) => id,
507            None => {
508                self.cur_page = None;
509                self.cur_view = None;
510                self.cur_count = 0;
511                self.pos = 0;
512                return Ok(false);
513            }
514        };
515
516        loop {
517            let page = self.resolver.read_one(&nid)?;
518            let view = NodeView::<P>::new(page.clone())?;
519            let count = view.count();
520            let next_id = decode_next_id::<P, IC>(&view);
521
522            self.cur_page = Some(page);
523            self.cur_view = Some(view);
524            self.cur_count = count;
525            self.pos = 0;
526            self.leaf_id = next_id;
527
528            if self.cur_count > 0 {
529                return Ok(true);
530            }
531
532            nid = match self.leaf_id.take() {
533                Some(id) => id,
534                None => {
535                    self.cur_page = None;
536                    self.cur_view = None;
537                    self.cur_count = 0;
538                    self.pos = 0;
539                    return Ok(false);
540                }
541            };
542        }
543    }
544
545    fn step_reverse(&mut self) -> Result<bool, Error> {
546        if self.pos_after > 0 {
547            self.pos_after -= 1;
548            return Ok(true);
549        }
550
551        let view = match self.cur_view.as_ref() {
552            Some(v) => v,
553            None => return Ok(false),
554        };
555        if view.count() == 0 {
556            return Ok(false);
557        }
558
559        let (k_enc, _) = view.leaf_entry_slices(0);
560        let (page, v, pos_after, next_id) =
561            descend_lt_encoded::<R, P, KC, IC>(&self.resolver, k_enc)?;
562        self.cur_page = Some(page);
563        self.cur_view = Some(v);
564        self.cur_count = self.cur_view.as_ref().unwrap().count();
565        self.pos_after = pos_after;
566        self.leaf_id = next_id;
567        Ok(self.cur_count > 0 && self.pos_after > 0)
568    }
569}
570
571impl<'a, R, P, KC, IC> Iterator for ScanIter<'a, R, P, KC, IC>
572where
573    R: ValueResolver<'a, P, KC, IC>,
574    P: Pager,
575    KC: KeyCodec,
576    IC: IdCodec<Id = P::Id>,
577{
578    type Item = (KeyRef<P::Page>, ValueRef<P::Page>);
579    fn next(&mut self) -> Option<Self::Item> {
580        self.cur_page.as_ref()?;
581
582        match self.dir {
583            Direction::Forward => {
584                loop {
585                    // Hop to the next non-empty leaf when current is exhausted.
586                    if self.pos >= self.cur_count && !(self.advance_leaf_forward().ok()?) {
587                        return None;
588                    }
589
590                    let page = self.cur_page.as_ref()?.clone();
591                    let view = self.cur_view.as_ref()?;
592
593                    // Bounds/prefix/frame checks for current candidate.
594                    if !self.within_lower(view, self.pos) {
595                        return None;
596                    }
597                    if !self.within_upper(view, self.pos) {
598                        return None;
599                    }
600                    if !self.within_prefix(view, self.pos) {
601                        self.pos += 1;
602                        continue; // ITERATIVE skip (no recursion)
603                    }
604
605                    if let Some(pred) = self.frame_predicate.as_ref() {
606                        let (k_enc, _) = view.leaf_entry_slices(self.pos);
607                        if let Some(head) = self.first_key_enc.as_ref() {
608                            if !pred(head, k_enc) {
609                                return None;
610                            }
611                        } else {
612                            // Capture first yielded key
613                            self.first_key_enc = Some(k_enc.to_vec());
614                        }
615                    }
616
617                    let out = self.resolver.entry_at(page, view, self.pos).ok()?;
618                    self.pos += 1;
619                    return Some(out);
620                }
621            }
622            Direction::Reverse => {
623                loop {
624                    // Move cursor left; when current leaf exhausted, step to previous.
625                    if self.pos_after == 0 && !(self.step_reverse().ok()?) {
626                        return None;
627                    }
628
629                    let page = self.cur_page.as_ref()?.clone();
630                    let view = self.cur_view.as_ref()?;
631                    let idx = self.pos_after - 1;
632
633                    if !self.within_lower(view, idx) {
634                        return None;
635                    }
636                    // Upper is enforced by starting position for reverse.
637
638                    if !self.within_prefix(view, idx) {
639                        // Skip this entry, continue moving left.
640                        if self.pos_after > 0 {
641                            self.pos_after -= 1;
642                        }
643                        continue; // ITERATIVE skip (no recursion)
644                    }
645
646                    if let Some(pred) = self.frame_predicate.as_ref() {
647                        let (k_enc, _) = view.leaf_entry_slices(idx);
648                        if let Some(head) = self.first_key_enc.as_ref() {
649                            if !pred(head, k_enc) {
650                                return None;
651                            }
652                        } else {
653                            self.first_key_enc = Some(k_enc.to_vec());
654                        }
655                    }
656
657                    let out = self.resolver.entry_at(page, view, idx).ok()?;
658                    if self.pos_after > 0 {
659                        self.pos_after -= 1;
660                    }
661                    return Some(out);
662                }
663            }
664        }
665    }
666}
667
668// ------------------------------------------------------------------
669// Public type aliases and compat constructors
670// ------------------------------------------------------------------
671
672pub type BPlusTreeIter<'a, P, KC, IC> = ScanIter<'a, PlainResolver<'a, P, KC, IC>, P, KC, IC>;
673
674impl<'a, P, KC, IC> BPlusTreeIter<'a, P, KC, IC>
675where
676    P: Pager,
677    KC: KeyCodec,
678    IC: IdCodec<Id = P::Id>,
679{
680    #[inline]
681    pub fn new(tree: &'a BPlusTree<P, KC, IC>) -> Result<Self, Error> {
682        Self::with_opts(tree, ScanOpts::forward())
683    }
684
685    #[inline]
686    pub fn with_opts(
687        tree: &'a BPlusTree<P, KC, IC>,
688        opts: ScanOpts<'a, KC>,
689    ) -> Result<Self, Error> {
690        let r = PlainResolver::new(tree);
691        ScanIter::with_resolver(r, opts)
692    }
693}
694
695// ------------------------------------------------------------------
696// Internal helpers
697// ------------------------------------------------------------------
698
699#[inline]
700fn decode_next_id<P, IC>(view: &NodeView<P>) -> Option<P::Id>
701where
702    P: Pager,
703    IC: IdCodec<Id = P::Id>,
704{
705    let aux = view.leaf_next_aux();
706    if aux.is_empty() {
707        None
708    } else {
709        Some(IC::decode_from(aux).ok()?.0)
710    }
711}
712
713fn descend_leftmost<'a, R, P, KC, IC>(r: &R) -> CursorStepRes<P>
714where
715    R: ValueResolver<'a, P, KC, IC>,
716    P: Pager,
717    KC: KeyCodec,
718    IC: IdCodec<Id = P::Id>,
719{
720    let mut id = r.root_id();
721    loop {
722        match r.read_node(&id)? {
723            Node::Internal { entries } => {
724                let (_, child) = entries.first().expect("internal without child").clone();
725                id = child;
726            }
727            Node::Leaf { .. } => {
728                let page = r.read_one(&id)?;
729                let view = NodeView::<P>::new(page.clone())?;
730                let next_id = decode_next_id::<P, IC>(&view);
731                return Ok((page, view, 0, next_id));
732            }
733        }
734    }
735}
736
737fn descend_rightmost<'a, R, P, KC, IC>(r: &R) -> NodeWithNextRes<P>
738where
739    R: ValueResolver<'a, P, KC, IC>,
740    P: Pager,
741    KC: KeyCodec,
742    IC: IdCodec<Id = P::Id>,
743{
744    let mut id = r.root_id();
745    loop {
746        match r.read_node(&id)? {
747            Node::Internal { entries } => {
748                let (_, child) = entries.last().expect("internal without child").clone();
749                id = child;
750            }
751            Node::Leaf { .. } => {
752                let page = r.read_one(&id)?;
753                let view = NodeView::<P>::new(page.clone())?;
754                let next_id = decode_next_id::<P, IC>(&view);
755                return Ok((page, view, next_id));
756            }
757        }
758    }
759}
760
761fn leaf_lower_bound<P, KC>(view: &NodeView<P>, key: &KC::Key) -> usize
762where
763    P: Pager,
764    KC: KeyCodec,
765{
766    let mut lo = 0isize;
767    let mut hi = view.count() as isize;
768    while lo < hi {
769        let mid = ((lo + hi) >> 1) as usize;
770        let (k_enc, _) = view.leaf_entry_slices(mid);
771        match KC::compare_encoded(k_enc, key) {
772            Ordering::Less => lo = mid as isize + 1,
773            _ => hi = mid as isize,
774        }
775    }
776    lo as usize
777}
778
779fn leaf_upper_pos<P, KC>(view: &NodeView<P>, key: &KC::Key, inclusive: bool) -> usize
780where
781    P: Pager,
782    KC: KeyCodec,
783{
784    let mut lo = 0isize;
785    let mut hi = view.count() as isize;
786    while lo < hi {
787        let mid = ((lo + hi) >> 1) as usize;
788        let (k_enc, _) = view.leaf_entry_slices(mid);
789        let ord = KC::compare_encoded(k_enc, key);
790        let is_le = if inclusive {
791            ord != Ordering::Greater
792        } else {
793            ord == Ordering::Less
794        };
795        if is_le {
796            lo = mid as isize + 1;
797        } else {
798            hi = mid as isize;
799        }
800    }
801    lo as usize
802}
803
804fn descend_ge<'a, R, P, KC, IC>(r: &R, key: &KC::Key) -> CursorStepRes<P>
805where
806    R: ValueResolver<'a, P, KC, IC>,
807    P: Pager,
808    KC: KeyCodec,
809    IC: IdCodec<Id = P::Id>,
810{
811    let mut id = r.root_id();
812    loop {
813        let node_page = r.read_one(&id)?;
814        let view = NodeView::<P>::new(node_page)?;
815        if let Ok(NodeTag::Leaf) = view.tag() {
816            let page = r.read_one(&id)?;
817            let view = NodeView::<P>::new(page.clone())?;
818            let pos = leaf_lower_bound::<P, KC>(&view, key);
819            let next_id = decode_next_id::<P, IC>(&view);
820            return Ok((page, view, pos, next_id));
821        }
822
823        let n = view.count();
824        let mut lo = 0usize;
825        let mut hi = n;
826        while lo < hi {
827            let mid = (lo + hi) / 2;
828            let (k_enc, _) = view.internal_entry_slices(mid);
829            if KC::compare_encoded(k_enc, key) == Ordering::Less {
830                lo = mid + 1;
831            } else {
832                hi = mid;
833            }
834        }
835        let idx = lo.min(n - 1);
836        let (_, child_raw) = view.internal_entry_slices(idx);
837        let (child_id, _) = IC::decode_from(child_raw)?;
838        id = child_id;
839    }
840}
841
842fn descend_upper_pos<'a, R, P, KC, IC>(r: &R, key: &KC::Key, inclusive: bool) -> CursorStepRes<P>
843where
844    R: ValueResolver<'a, P, KC, IC>,
845    P: Pager,
846    KC: KeyCodec,
847    IC: IdCodec<Id = P::Id>,
848{
849    let mut id = r.root_id();
850    loop {
851        let node_page = r.read_one(&id)?;
852        let view = NodeView::<P>::new(node_page)?;
853        if let Ok(NodeTag::Leaf) = view.tag() {
854            let page = r.read_one(&id)?;
855            let view = NodeView::<P>::new(page.clone())?;
856            let pos_after = leaf_upper_pos::<P, KC>(&view, key, inclusive);
857            let next_id = decode_next_id::<P, IC>(&view);
858            return Ok((page, view, pos_after, next_id));
859        }
860
861        let n = view.count();
862        let mut lo = 0usize;
863        let mut hi = n;
864        while lo < hi {
865            let mid = (lo + hi) / 2;
866            let (k_enc, _) = view.internal_entry_slices(mid);
867            let ord = KC::compare_encoded(k_enc, key);
868            let ok = if inclusive {
869                ord != Ordering::Greater
870            } else {
871                ord == Ordering::Less
872            };
873            if ok {
874                lo = mid + 1;
875            } else {
876                hi = mid;
877            }
878        }
879        let chosen_idx = lo.min(n - 1);
880        let (_, child_raw) = view.internal_entry_slices(chosen_idx);
881        let (child_id, _) = IC::decode_from(child_raw)?;
882        id = child_id;
883    }
884}
885
886/// Strict less-than for an encoded key slice (reverse hop).
887fn descend_lt_encoded<'a, R, P, KC, IC>(r: &R, enc: &[u8]) -> CursorStepRes<P>
888where
889    R: ValueResolver<'a, P, KC, IC>,
890    P: Pager,
891    KC: KeyCodec,
892    IC: IdCodec<Id = P::Id>,
893{
894    // Local hex printer for quick tracing.
895    let _hex8 = |b: &[u8]| -> String {
896        let mut s = String::new();
897        for (i, x) in b.iter().take(8).enumerate() {
898            if i > 0 {
899                s.push(' ');
900            }
901            use core::fmt::Write as _;
902            let _ = write!(&mut s, "{:02x}", x);
903        }
904        s
905    };
906    // Decode once; all comparisons are numeric (LE-safe).
907    let key = KC::decode_from(enc)?;
908    // Keep a path of (internal_node_id, chosen_idx) to support fallback.
909    let mut path: Vec<(P::Id, usize)> = Vec::new();
910    let mut id = r.root_id();
911    loop {
912        let node_page = r.read_one(&id)?;
913        let view = NodeView::<P>::new(node_page)?;
914
915        if let Ok(NodeTag::Leaf) = view.tag() {
916            // We are at a candidate leaf.
917            // Count keys strictly < key.
918            let page = r.read_one(&id)?;
919            let view = NodeView::<P>::new(page.clone())?;
920            let pos_after = leaf_upper_pos::<P, KC>(&view, &key, false);
921            let next_id = decode_next_id::<P, IC>(&view);
922
923            if pos_after > 0 {
924                // This leaf has keys < key. Done.
925                return Ok((page, view, pos_after, next_id));
926            }
927
928            // Fallback: climb up until we can step left to a sibling,
929            // then descend to that subtree's rightmost leaf.
930            while let Some((anc_id, idx)) = path.pop() {
931                if idx == 0 {
932                    // No left sibling at this ancestor, keep climbing.
933                    continue;
934                }
935
936                // Step to the left sibling child: idx - 1.
937                let anc_page = r.read_one(&anc_id)?;
938                let anc_view = NodeView::<P>::new(anc_page)?;
939                let (_, child_raw) = anc_view.internal_entry_slices(idx - 1);
940                let (mut child_id, _) = IC::decode_from(child_raw)?;
941                // Descend to rightmost leaf of that subtree.
942                loop {
943                    let c_page = r.read_one(&child_id)?;
944                    let c_view = NodeView::<P>::new(c_page.clone())?;
945                    if let Ok(NodeTag::Leaf) = c_view.tag() {
946                        let next = decode_next_id::<P, IC>(&c_view);
947                        let cnt = c_view.count();
948                        return Ok((c_page, c_view, cnt, next));
949                    }
950                    let last = c_view.count().saturating_sub(1);
951                    let (_, cr) = c_view.internal_entry_slices(last);
952                    let (next_child, _) = IC::decode_from(cr)?;
953                    child_id = next_child;
954                }
955            }
956
957            // No ancestor to step left at: nothing < key in whole tree.
958            return Ok((page, view, 0, next_id));
959        }
960
961        // INTERNAL: lower_bound for first sep >= key, then tentatively
962        // choose that child.
963        // If it has no <key, we'll use the fallback.
964        let n = view.count();
965        let mut lo = 0usize;
966        let mut hi = n;
967        while lo < hi {
968            let mid = (lo + hi) / 2;
969            let (k_enc, _) = view.internal_entry_slices(mid);
970            let ord = KC::compare_encoded(k_enc, &key);
971            if ord == core::cmp::Ordering::Less {
972                lo = mid + 1;
973            } else {
974                hi = mid;
975            }
976        }
977
978        let chosen_idx = lo.min(n.saturating_sub(1));
979        let (_, child_raw) = view.internal_entry_slices(chosen_idx);
980        let (child_id, _) = IC::decode_from(child_raw)?;
981        path.push((id, chosen_idx));
982        id = child_id;
983    }
984}
985
986// ------------------------------------------------------------------
987// New small helpers to start from generic `Bound`s
988// ------------------------------------------------------------------
989
990fn descend_lower_pos<'a, R, P, KC, IC>(r: &R, bound: Bound<&KC::Key>) -> CursorStepRes<P>
991where
992    R: ValueResolver<'a, P, KC, IC>,
993    P: Pager,
994    KC: KeyCodec,
995    IC: IdCodec<Id = P::Id>,
996{
997    match bound {
998        Unbounded => descend_leftmost::<R, P, KC, IC>(r),
999        Included(k) => descend_ge::<R, P, KC, IC>(r, k),
1000        Excluded(k) => {
1001            let (page, view, mut pos, next) =
1002                descend_upper_pos::<R, P, KC, IC>(r, k, /*inclusive=*/ false)?;
1003
1004            // If the search landed exactly on the key we need to exclude, advance one position.
1005            if pos < view.count()
1006                && KC::compare_encoded(view.leaf_entry_slices(pos).0, k) == Ordering::Equal
1007            {
1008                pos += 1;
1009            }
1010
1011            Ok((page, view, pos, next))
1012        }
1013    }
1014}
1015
1016fn descend_from_upper<'a, R, P, KC, IC>(r: &R, bound: Bound<&KC::Key>) -> CursorStepRes<P>
1017where
1018    R: ValueResolver<'a, P, KC, IC>,
1019    P: Pager,
1020    KC: KeyCodec,
1021    IC: IdCodec<Id = P::Id>,
1022{
1023    match bound {
1024        Unbounded => {
1025            let (p, v, n) = descend_rightmost::<R, P, KC, IC>(r)?;
1026            let count = v.count();
1027            Ok((p, v, count, n))
1028        }
1029        Included(k) => descend_upper_pos::<R, P, KC, IC>(r, k, /*inclusive=*/ true),
1030        Excluded(k) => descend_upper_pos::<R, P, KC, IC>(r, k, /*inclusive=*/ false),
1031    }
1032}