Skip to main content

tiny_trie/
nib_trie.rs

1//! Nib Trie — a fixed-fanout radix trie indexed by 2-bit words (nibs).
2//!
3//! Each node has 4 child slots (one per 2-bit value 0–3), addressed by direct
4//! indexing. This trades space for simplicity and lookup speed compared to a
5//! binary trie, while using less space per node than a 16-way nibble trie.
6//!
7//! # No Stacking
8//!
9//! Unlike NibbleTrie, NibTrie does not support vnode stacking. Each physical
10//! node holds exactly one logical trie node. The `occupancy` and `leaf_mask`
11//! fields are `u8` (4 bits used) rather than `u16` (16 bits).
12//!
13//! # Terminal Nodes
14//!
15//! Keys that are prefixes of other keys are represented by a `terminal` flag on
16//! the node where the key ends, rather than a null-byte leaf child. This
17//! eliminates null terminators, allows `0x00` bytes in keys, and makes `get()`
18//! accept plain `&[u8]`.
19//!
20//! # Key Index Encoding
21//!
22//! Real keys start at index 1 (index 0 is the dummy entry). The sentinel
23//! `PTR::max_value()` marks empty slots in `children[]`.
24//!
25//! # Nib Addressing
26//!
27//! Each byte contains 4 nib positions:
28//! - nib 0: bits 7–6 of byte 0
29//! - nib 1: bits 5–4 of byte 0
30//! - nib 2: bits 3–2 of byte 0
31//! - nib 3: bits 1–0 of byte 0
32//! - nib 4: bits 7–6 of byte 1
33//! - etc.
34//!
35//! Total nib count for a key of length L is `L * 4`.
36
37use crate::nibble_trie::TrieIndex;
38use std::{fmt, simd::{Simd, cmp::SimdPartialEq}};
39
40// ---------------------------------------------------------------------------
41// Core types
42// ---------------------------------------------------------------------------
43
44/// A single node in the nib trie arena.
45///
46/// Each node has 4 child slots (one per 2-bit value), a leaf reference key,
47/// a prefix length in nib positions, and bitmasks for leaf/occupancy tracking.
48///
49/// Layout with PTR=u32, LEN=u16: 24 bytes (4×u32 + u32 + u16 + 3×u8 + padding).
50#[derive(Copy, Clone)]
51pub(crate) struct NibNode<PTR: TrieIndex = u32, LEN: TrieIndex = u16> {
52    pub(crate) children: [PTR; 4],     // one per 2-bit value; PTR::MAX = empty
53    pub(crate) leaf: PTR,              // key index for prefix comparison
54    pub(crate) prefix_len: LEN,        // prefix length in nibs (2-bit positions)
55    pub(crate) leaf_mask: u8,          // bit N = children[N] is leaf key index
56    pub(crate) occupancy: u8,          // bit N = slot N is occupied
57    pub(crate) terminal: u8,           // bit 0 = this node is terminal
58}
59
60impl<PTR: TrieIndex, LEN: TrieIndex> NibNode<PTR, LEN> {
61    pub(crate) fn new() -> Self {
62        NibNode {
63            children: [PTR::max_value_sentinel(); 4],
64            leaf: PTR::max_value_sentinel(),
65            prefix_len: LEN::zero(),
66            leaf_mask: 0,
67            occupancy: 0,
68            terminal: 0,
69        }
70    }
71
72    /// Check if this node is terminal (represents a key that ends here).
73    #[inline]
74    pub(crate) fn is_terminal(&self) -> bool {
75        self.terminal != 0
76    }
77
78    /// Set or clear the terminal flag.
79    #[inline]
80    fn set_terminal(&mut self, val: bool) {
81        if val {
82            self.terminal = 1;
83        } else {
84            self.terminal = 0;
85        }
86    }
87
88    /// Check if nib slot `nib` is a leaf (key index).
89    #[inline]
90    pub(crate) fn is_leaf(&self, nib: usize) -> bool {
91        debug_assert!(nib < 4);
92        (self.leaf_mask >> nib) & 1 == 1
93    }
94
95    /// Set the leaf flag for nib slot `nib`.
96    #[inline]
97    fn set_leaf(&mut self, nib: usize) {
98        debug_assert!(nib < 4);
99        self.leaf_mask |= 1 << nib;
100    }
101
102    /// Clear the leaf flag for nib slot `nib`.
103    #[inline]
104    fn clear_leaf(&mut self, nib: usize) {
105        debug_assert!(nib < 4);
106        self.leaf_mask &= !(1 << nib);
107    }
108
109    /// Check if nib slot `nib` is occupied.
110    #[inline]
111    pub(crate) fn is_occupied(&self, nib: usize) -> bool {
112        debug_assert!(nib < 4);
113        (self.occupancy >> nib) & 1 == 1
114    }
115
116    /// Set the occupancy bit for nib slot `nib`.
117    #[inline]
118    fn set_occupied(&mut self, nib: usize) {
119        debug_assert!(nib < 4);
120        self.occupancy |= 1 << nib;
121    }
122
123    /// Store a leaf key index at `nib`. Key index must not be the sentinel.
124    #[inline]
125    fn set_leaf_child(&mut self, nib: usize, key_index: PTR) {
126        debug_assert!(nib < 4);
127        debug_assert!(key_index != PTR::max_value_sentinel(), "sentinel key index");
128        self.set_leaf(nib);
129        self.set_occupied(nib);
130        self.children[nib] = key_index;
131    }
132
133    /// Store an arena index at `nib` (internal node reference).
134    #[inline]
135    fn set_internal_child(&mut self, nib: usize, addr: PTR) {
136        debug_assert!(nib < 4);
137        debug_assert!(addr != PTR::max_value_sentinel(), "sentinel address");
138        self.clear_leaf(nib);
139        self.set_occupied(nib);
140        self.children[nib] = addr;
141    }
142
143    /// Decode a leaf child at `nib` into a key index.
144    /// Returns `None` if the slot is empty or not a leaf.
145    #[inline]
146    fn leaf_key_index(&self, nib: usize) -> Option<PTR> {
147        debug_assert!(nib < 4);
148        if self.is_leaf(nib) && self.is_occupied(nib) {
149            Some(self.children[nib])
150        } else {
151            None
152        }
153    }
154
155    /// Compute a 4-bit mask where bit N is set if `children[N]` is not the sentinel.
156    #[allow(dead_code)]
157    #[inline]
158    pub(crate) fn children_mask(&self) -> u8 {
159        self.occupancy
160    }
161
162    /// Promote this node's PTR type to a wider one.
163    pub(crate) fn promote<NewPTR: TrieIndex>(self) -> NibNode<NewPTR, LEN> {
164        let mut children = [NewPTR::max_value_sentinel(); 4];
165        for i in 0..4 {
166            if self.occupancy & (1 << i) != 0 {
167                children[i] = NewPTR::from_usize(self.children[i].as_usize());
168            }
169        }
170        NibNode {
171            children,
172            leaf: if self.leaf == PTR::max_value_sentinel() {
173                NewPTR::max_value_sentinel()
174            } else {
175                NewPTR::from_usize(self.leaf.as_usize())
176            },
177            prefix_len: self.prefix_len,
178            leaf_mask: self.leaf_mask,
179            occupancy: self.occupancy,
180            terminal: self.terminal,
181        }
182    }
183
184    /// Demote this node's PTR type to a narrower one.
185    /// Returns `Err(self)` if any address doesn't fit.
186    pub(crate) fn demote<NewPTR: TrieIndex>(self) -> Result<NibNode<NewPTR, LEN>, Self> {
187        for i in 0..4 {
188            if self.occupancy & (1 << i) != 0 {
189                if self.children[i].as_usize() > NewPTR::max_value() {
190                    return Err(self);
191                }
192            }
193        }
194        if self.leaf != PTR::max_value_sentinel() && self.leaf.as_usize() > NewPTR::max_value() {
195            return Err(self);
196        }
197        let mut children = [NewPTR::max_value_sentinel(); 4];
198        for i in 0..4 {
199            if self.occupancy & (1 << i) != 0 {
200                children[i] = NewPTR::from_usize(self.children[i].as_usize());
201            }
202        }
203        Ok(NibNode {
204            children,
205            leaf: if self.leaf == PTR::max_value_sentinel() {
206                NewPTR::max_value_sentinel()
207            } else {
208                NewPTR::from_usize(self.leaf.as_usize())
209            },
210            prefix_len: self.prefix_len,
211            leaf_mask: self.leaf_mask,
212            occupancy: self.occupancy,
213            terminal: self.terminal,
214        })
215    }
216}
217
218impl<PTR: TrieIndex, LEN: TrieIndex> fmt::Debug for NibNode<PTR, LEN> {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        let active: Vec<(usize, &str, PTR)> = (0..4)
221            .filter(|&n| self.occupancy & (1 << n) != 0)
222            .map(|n| {
223                let tag = if (self.leaf_mask >> n) & 1 == 1 { "L" } else { "I" };
224                (n, tag, self.children[n])
225            })
226            .collect();
227        f.debug_struct("NibNode")
228            .field("prefix_len", &self.prefix_len)
229            .field("leaf_mask", &format_args!("{:04b}", self.leaf_mask))
230            .field("occupancy", &format_args!("{:04b}", self.occupancy))
231            .field("terminal", &self.terminal)
232            .field("leaf", &self.leaf)
233            .field("children", &active)
234            .finish()
235    }
236}
237
238// ---------------------------------------------------------------------------
239// NibTrie
240// ---------------------------------------------------------------------------
241
242#[derive(Clone)]
243pub struct NibTrie<T, PTR: TrieIndex = u32, LEN: TrieIndex = u16> {
244    pub(crate) arena: Vec<NibNode<PTR, LEN>>,
245    pub(crate) buf: Vec<u8>,                // all keys concatenated (no null terminators)
246    pub(crate) index: Vec<(usize, LEN)>,   // (offset into buf, len) per key — offset is usize, len is compact
247    pub(crate) values: Vec<T>,              // values[i] ↔ index[i]
248}
249
250// ---------------------------------------------------------------------------
251// Divergence result
252// ---------------------------------------------------------------------------
253
254enum DivergeResult {
255    /// The keys are identical (same nib count, same content).
256    Duplicate,
257    /// The keys diverge at this nib position, or one key is a prefix of the other.
258    At(usize),
259}
260
261enum PrefixCheck {
262    /// The keys match at every nib position in `from..to`.
263    Matches,
264    /// The keys diverge at this nib position (within `from..to`).
265    Diverges(usize),
266}
267
268// ---------------------------------------------------------------------------
269// SIMD helpers
270// ---------------------------------------------------------------------------
271
272#[inline]
273fn simd_eq(a: &[u8], b: &[u8]) -> bool {
274    if a.len() != b.len() {
275        return false;
276    }
277    let len = a.len();
278    let mut i = 0;
279    while i + 16 <= len {
280        let va = Simd::<u8, 16>::from_slice(unsafe { a.get_unchecked(i..i + 16) });
281        let vb = Simd::<u8, 16>::from_slice(unsafe { b.get_unchecked(i..i + 16) });
282        if va.simd_ne(vb).any() {
283            return false;
284        }
285        i += 16;
286    }
287    while i < len {
288        if unsafe { *a.get_unchecked(i) != *b.get_unchecked(i) } {
289            return false;
290        }
291        i += 1;
292    }
293    true
294}
295
296
297// ---------------------------------------------------------------------------
298// Nib helpers
299// ---------------------------------------------------------------------------
300
301/// Extract the 2-bit nib at position `idx` from `key`.
302///
303/// Each byte contains 4 nibs:
304/// - nib 0: bits 7–6 (most significant pair)
305/// - nib 1: bits 5–4
306/// - nib 2: bits 3–2
307/// - nib 3: bits 1–0 (least significant pair)
308///
309/// Past the end of the key, returns 0 (implicit zero padding for ordering).
310#[inline]
311fn key_nib_at(key: &[u8], idx: usize) -> u8 {
312    let byte_idx = idx / 4;
313    if byte_idx < key.len() {
314        let shift = 6 - 2 * (idx % 4); // 6, 4, 2, 0
315        (key[byte_idx] >> shift) & 0x03
316    } else {
317        0
318    }
319}
320
321/// Unchecked version of `key_nib_at`.
322///
323/// # Safety
324/// `idx / 4` must be < `key.len()`.
325#[inline]
326unsafe fn key_nib_at_unchecked(key: &[u8], idx: usize) -> u8 {
327    let byte_idx = idx / 4;
328    debug_assert!(byte_idx < key.len(), "nib {idx} out of bounds for key len {}", key.len());
329    let shift = 6 - 2 * (idx % 4);
330    (unsafe { *key.get_unchecked(byte_idx) } >> shift) & 0x03
331}
332
333#[inline]
334fn nib_count(key: &[u8]) -> usize {
335    key.len() * 4
336}
337
338/// Given a non-zero XOR of two differing bytes, return the nib position of the
339/// first divergence. Uses `leading_zeros` for branchless computation:
340/// `group = lz / 2`, so `nib = byte_idx * 4 + lz / 2`.
341#[inline]
342fn diverging_nib(xor: u8, byte_idx: usize) -> usize {
343    byte_idx * 4 + (xor.leading_zeros() as usize) / 2
344}
345
346/// Scan two keys from `from` onward to find the first diverging nib.
347#[inline]
348fn find_divergence(key_a: &[u8], key_b: &[u8], from: usize) -> DivergeResult {
349    let total_a = nib_count(key_a);
350    let total_b = nib_count(key_b);
351    let min = total_a.min(total_b);
352    let mut d = from;
353    while d < min {
354        if key_nib_at(key_a, d) != key_nib_at(key_b, d) {
355            return DivergeResult::At(d);
356        }
357        d += 1;
358    }
359    if total_a == total_b {
360        DivergeResult::Duplicate
361    } else {
362        DivergeResult::At(d)
363    }
364}
365
366fn simd_find_divergence<const N: usize>(key_a: &[u8], key_b: &[u8], from: usize) -> DivergeResult
367where
368{
369    let minlen = key_a.len().min(key_b.len());
370    let mut i = from / 4; // byte containing nib `from`
371
372    while i + N <= minlen {
373        let a = Simd::<u8, N>::from_slice(unsafe { key_a.get_unchecked(i..i + N) });
374        let b = Simd::<u8, N>::from_slice(unsafe { key_b.get_unchecked(i..i + N) });
375        let mask = a.simd_ne(b);
376        if mask.any() {
377            let diff_byte_idx = i + mask.first_set().unwrap();
378            let xor = unsafe { *key_a.get_unchecked(diff_byte_idx) ^ *key_b.get_unchecked(diff_byte_idx) };
379            return DivergeResult::At(diverging_nib(xor, diff_byte_idx));
380        }
381        i += N;
382    }
383
384    // Scalar tail
385    find_divergence(key_a, key_b, i * 4)
386}
387
388/// Scan nibs `from..to` of two keys. Returns `Diverges(pos)` if they differ
389/// at any nib in that range, or `Matches` if they agree throughout.
390#[inline]
391fn check_prefix(key_a: &[u8], key_b: &[u8], from: usize, to: usize) -> PrefixCheck {
392    for nib in from..to {
393        if key_nib_at(key_a, nib) != key_nib_at(key_b, nib) {
394            return PrefixCheck::Diverges(nib);
395        }
396    }
397    PrefixCheck::Matches
398}
399
400/// SIMD-accelerated bounded prefix check for 2-bit nibs.
401fn simd_check_prefix<const N: usize>(key_a: &[u8], key_b: &[u8], from: usize, to: usize) -> PrefixCheck
402where
403{
404    if from >= to {
405        return PrefixCheck::Matches;
406    }
407
408    let from_byte = from / 4;
409    let to_byte = (to + 3) / 4; // first byte fully outside the nib range
410    let minlen = key_a.len().min(key_b.len()).min(to_byte);
411    let mut i = from_byte;
412
413    while i + N <= minlen {
414        let a = Simd::<u8, N>::from_slice(unsafe { key_a.get_unchecked(i..i + N) });
415        let b = Simd::<u8, N>::from_slice(unsafe { key_b.get_unchecked(i..i + N) });
416        let mask = a.simd_ne(b);
417        if mask.any() {
418            let diff_byte_idx = i + mask.first_set().unwrap();
419            let xor = unsafe { *key_a.get_unchecked(diff_byte_idx) ^ *key_b.get_unchecked(diff_byte_idx) };
420            let nib = diverging_nib(xor, diff_byte_idx);
421            if nib < to {
422                return PrefixCheck::Diverges(nib);
423            }
424            // Divergence past the bound — keys match within range
425            return PrefixCheck::Matches;
426        }
427        i += N;
428    }
429
430    // Scalar tail
431    check_prefix(key_a, key_b, i * 4, to)
432}
433
434// ---------------------------------------------------------------------------
435// NibTrie methods
436// ---------------------------------------------------------------------------
437
438impl<T, PTR: TrieIndex, LEN: TrieIndex> NibTrie<T, PTR, LEN> {
439    /// Return the key slice for `key_index`.
440    #[inline]
441    fn key_slice(&self, key_index: PTR) -> &[u8] {
442        let (off, len) = self.index[key_index.as_usize()];
443        &self.buf[off..off + len.as_usize()]
444    }
445
446    pub fn new() -> Self {
447        NibTrie {
448            arena: Vec::new(),
449            buf: vec![0],           // buf[0] = dummy (unused byte)
450            index: vec![(0, LEN::zero())],   // index[0] = dummy entry
451            values: Vec::new(),
452        }
453    }
454
455    pub fn len(&self) -> usize {
456        self.index.len() - 1 // subtract dummy
457    }
458
459    pub fn is_empty(&self) -> bool {
460        self.index.len() == 1 // only the dummy
461    }
462
463    // -----------------------------------------------------------------------
464    // Lookup
465    // -----------------------------------------------------------------------
466
467    pub fn get_index(&self, key: &[u8]) -> Option<usize> {
468        if self.arena.is_empty() {
469            return None;
470        }
471        let mut node_idx: usize = 0;
472        let max_nib = key.len() * 4;
473        loop {
474            let node = &self.arena[node_idx];
475            let prefix_len = node.prefix_len.as_usize();
476
477            // Key nibs exhausted — check if this node is terminal.
478            if prefix_len >= max_nib {
479                if node.is_terminal() {
480                    let ki = node.leaf;
481                    let (off, len) = self.index[ki.as_usize()];
482                    let key_in_buf = &self.buf[off..off + len.as_usize()];
483                    if key.len() == len.as_usize() && simd_eq(&key_in_buf[..key.len()], key) {
484                        return Some(ki.as_usize());
485                    }
486                }
487                return None;
488            }
489
490            // Safe to use unchecked: prefix_len < max_nib guarantees byte_idx < key.len()
491            let nib = unsafe { key_nib_at_unchecked(key, prefix_len) } as usize;
492            let slot = node.children[nib];
493            if slot == PTR::max_value_sentinel() {
494                return None;
495            }
496            if (node.leaf_mask >> nib) & 1 == 1 {
497                // Leaf — verify full key match
498                let key_index = slot;
499                return if simd_eq(self.key_slice(key_index), key) {
500                    Some(key_index.as_usize())
501                } else {
502                    None
503                };
504            }
505            // Internal child — descend
506            node_idx = slot.as_usize();
507        }
508    }
509
510    /// Unchecked lookup — assumes the key is present in the trie.
511    ///
512    /// # Safety
513    /// The key **must** have been inserted into this trie.
514    #[cfg(feature = "unchecked")]
515    unsafe fn get_index_unchecked(&self, key: &[u8]) -> Option<usize> {
516        if self.arena.is_empty() {
517            return None;
518        }
519        let mut node_idx: usize = 0;
520        let max_nib = key.len() * 4;
521        loop {
522            let node = unsafe { self.arena.get_unchecked(node_idx) };
523            let prefix_len = node.prefix_len.as_usize();
524            if prefix_len >= max_nib {
525                debug_assert!(node.is_terminal(), "get_unchecked: key not in set");
526                return Some(node.leaf.as_usize());
527            }
528            let nib = unsafe { key_nib_at_unchecked(key, prefix_len) } as usize;
529            let slot = unsafe { *node.children.get_unchecked(nib) };
530            if slot == PTR::max_value_sentinel() {
531                return None;
532            }
533            if (node.leaf_mask >> nib) & 1 == 1 {
534                return Some(slot.as_usize());
535            }
536            node_idx = slot.as_usize();
537        }
538    }
539
540    pub fn get(&self, key: &[u8]) -> Option<&T> {
541        self.get_index(key).map(|idx| &self.values[idx - 1])
542    }
543
544    pub fn get_mut(&mut self, key: &[u8]) -> Option<&mut T> {
545        self.get_index(key).map(|idx| &mut self.values[idx - 1])
546    }
547
548    /// Unchecked value lookup — assumes the key is present in the trie.
549    ///
550    /// # Safety
551    /// The key **must** have been inserted into this trie.
552    #[cfg(feature = "unchecked")]
553    pub unsafe fn get_unchecked(&self, key: &[u8]) -> Option<&T> {
554        unsafe { self.get_index_unchecked(key).map(|idx| &self.values[idx - 1]) }
555    }
556
557    // -----------------------------------------------------------------------
558    // Iteration
559    // -----------------------------------------------------------------------
560
561    pub fn iter(&self) -> Cursor<'_, T, PTR, LEN> {
562        Cursor::new(self)
563    }
564
565    pub fn iter_last(&self) -> Cursor<'_, T, PTR, LEN> {
566        Cursor::new_last(self)
567    }
568
569    /// Public forward mutable cursor: a lending tree-walk that hands out `&mut T`
570    /// borrows tied to the cursor (see [`CursorMut`]). Parked *before* the first
571    /// key — call `next()`/`first()` to position.
572    pub fn iter_mut(&mut self) -> CursorMut<'_, T, PTR, LEN> {
573        CursorMut::new(self)
574    }
575
576    /// Public reverse mutable cursor: a lending tree-walk parked *on* the last
577    /// key (see [`CursorMut`]).
578    pub fn iter_mut_last(&mut self) -> CursorMut<'_, T, PTR, LEN> {
579        CursorMut::new_last(self)
580    }
581
582    pub fn into_keys_values(self) -> (Vec<Vec<u8>>, Vec<T>) {
583        let buf = self.buf;
584        let keys: Vec<Vec<u8>> = self.index.into_iter().skip(1).map(|(off, len)| {
585            buf[off..off + len.as_usize()].to_vec()
586        }).collect();
587        (keys, self.values)
588    }
589
590    // -----------------------------------------------------------------------
591    // Capacity
592    // -----------------------------------------------------------------------
593
594    pub fn near_capacity(&self) -> bool {
595        self.arena.len() >= PTR::max_value() || self.index.len() >= PTR::max_value()
596    }
597
598    // -----------------------------------------------------------------------
599    // Optimize (DFS key-sorted buf rewrite)
600    // -----------------------------------------------------------------------
601
602    /// Rewrite `buf` in DFS order for cache locality.
603    pub fn optimize(&mut self) {
604        if self.arena.is_empty() {
605            return;
606        }
607
608        let mut new_buf = vec![0u8; self.buf.len()];
609        let mut cursor: usize = 1; // position 0 is the dummy byte
610
611        // Remap table: maps old arena index → new arena index.
612        let mut remap: Vec<usize> = vec![0; self.arena.len()];
613
614        let mut new_arena: Vec<NibNode<PTR, LEN>> = Vec::new();
615
616        // Collect key indices in DFS visitation order for index/values sorting
617        let mut dfs_key_order: Vec<PTR> = Vec::new();
618
619        self.walk_optimize(
620            0,
621            &mut new_buf, &mut cursor,
622            &mut remap, &mut new_arena,
623            &mut dfs_key_order,
624        );
625
626        new_buf.truncate(cursor);
627        self.buf = new_buf;
628        self.arena = new_arena;
629
630        // Remap all internal child addresses in the new arena
631        for node in &mut self.arena {
632            for nib in 0..4 {
633                if node.occupancy & (1 << nib) != 0 && !node.is_leaf(nib) {
634                    let old_addr = node.children[nib].as_usize();
635                    debug_assert!(old_addr < remap.len(), "old_addr {} >= remap.len() {}", old_addr, remap.len());
636                    debug_assert!(!(remap[old_addr] == 0 && old_addr != 0), "remap[{}] == 0 but old_addr != 0", old_addr);
637                    node.children[nib] = PTR::from_usize(remap[old_addr]);
638                }
639            }
640        }
641
642        // --- Sort index and values into DFS order ---
643
644        let num_keys = dfs_key_order.len();
645        let mut key_remap: Vec<usize> = vec![0; self.index.len()];
646        key_remap[0] = 0; // dummy stays at 0
647        for (new_ki, &old_ki) in dfs_key_order.iter().enumerate() {
648            key_remap[old_ki.as_usize()] = new_ki + 1; // 1-based
649        }
650
651        // Remap all key index references in the arena
652        for node in &mut self.arena {
653            for nib in 0..4 {
654                if node.occupancy & (1 << nib) != 0 && node.is_leaf(nib) {
655                    let old_ki = node.children[nib].as_usize();
656                    let new_ki = key_remap[old_ki];
657                    node.children[nib] = PTR::from_usize(new_ki);
658                }
659            }
660            // Remap leaf pointer (skip sentinel)
661            let old_leaf = node.leaf;
662            if old_leaf != PTR::max_value_sentinel() {
663                let new_ki = key_remap[old_leaf.as_usize()];
664                node.leaf = PTR::from_usize(new_ki);
665            }
666        }
667
668        // Rebuild index in DFS order
669        let mut new_index: Vec<(usize, LEN)> = vec![(0, LEN::zero()); num_keys + 1];
670        new_index[0] = self.index[0]; // keep dummy entry
671        for (new_ki, &old_ki) in dfs_key_order.iter().enumerate() {
672            new_index[new_ki + 1] = self.index[old_ki.as_usize()];
673        }
674
675        // Reorder values to match new key ordering
676        let mut new_values = Vec::with_capacity(num_keys);
677        unsafe {
678            let old_values_ptr = self.values.as_ptr();
679            for &old_ki in &dfs_key_order {
680                let old_val = std::ptr::read(old_values_ptr.add(old_ki.as_usize() - 1));
681                new_values.push(old_val);
682            }
683        }
684        unsafe { self.values.set_len(0); }
685        std::mem::swap(&mut self.values, &mut new_values);
686        self.index = new_index;
687    }
688
689    fn walk_optimize(
690        &mut self,
691        old_idx: usize,
692        new_buf: &mut [u8],
693        cursor: &mut usize,
694        remap: &mut Vec<usize>,
695        new_arena: &mut Vec<NibNode<PTR, LEN>>,
696        dfs_key_order: &mut Vec<PTR>,
697    ) {
698        let node = self.arena[old_idx]; // copy to avoid borrow conflicts
699        let occ = node.occupancy;
700        let is_term = node.is_terminal();
701
702        let new_idx = new_arena.len();
703        new_arena.push(NibNode::new());
704        remap[old_idx] = new_idx;
705
706        // Populate new node fields
707        new_arena[new_idx].prefix_len = node.prefix_len;
708        new_arena[new_idx].occupancy = occ;
709        new_arena[new_idx].leaf_mask = node.leaf_mask;
710        if is_term {
711            new_arena[new_idx].set_terminal(true);
712        }
713
714        // Copy key data for terminal node
715        if is_term {
716            let ki = node.leaf;
717            let (old_off, len) = self.index[ki.as_usize()];
718            let start = *cursor;
719            new_buf[start..start + len.as_usize()].copy_from_slice(
720                &self.buf[old_off..old_off + len.as_usize()]
721            );
722            self.index[ki.as_usize()].0 = *cursor;
723            *cursor += len.as_usize();
724            new_arena[new_idx].leaf = ki;
725            dfs_key_order.push(ki);
726        }
727
728        // Recurse into children
729        for nib in 0..4 {
730            if (occ >> nib) & 1 == 0 {
731                continue;
732            }
733            if node.is_leaf(nib) {
734                // Leaf child — copy key data
735                let ki = node.children[nib];
736                let (old_off, len) = self.index[ki.as_usize()];
737                let start = *cursor;
738                new_buf[start..start + len.as_usize()].copy_from_slice(
739                    &self.buf[old_off..old_off + len.as_usize()]
740                );
741                self.index[ki.as_usize()].0 = *cursor;
742                *cursor += len.as_usize();
743                new_arena[new_idx].children[nib] = ki;
744                dfs_key_order.push(ki);
745            } else {
746                // Internal child — recurse, then store old address for remapping
747                let child_old_addr = node.children[nib].as_usize();
748                self.walk_optimize(
749                    child_old_addr,
750                    new_buf, cursor,
751                    remap, new_arena,
752                    dfs_key_order,
753                );
754                // Store old address so the remap loop can find it
755                new_arena[new_idx].children[nib] = node.children[nib];
756            }
757        }
758
759        // Propagate leaf for non-terminal nodes
760        if !is_term && new_arena[new_idx].leaf == PTR::max_value_sentinel() {
761            let first_nib = occ.trailing_zeros() as usize;
762            if new_arena[new_idx].is_leaf(first_nib) {
763                new_arena[new_idx].leaf = new_arena[new_idx].children[first_nib];
764            } else {
765                let child_old_addr = node.children[first_nib].as_usize();
766                if child_old_addr < remap.len() {
767                    let child_new_idx = remap[child_old_addr];
768                    new_arena[new_idx].leaf = new_arena[child_new_idx].leaf;
769                }
770            }
771        }
772    }
773}
774
775impl<T, PTR: TrieIndex, LEN: TrieIndex> Default for NibTrie<T, PTR, LEN> {
776    fn default() -> Self { Self::new() }
777}
778
779// ---------------------------------------------------------------------------
780// Insertion
781// ---------------------------------------------------------------------------
782
783impl<T, PTR: TrieIndex, LEN: TrieIndex> NibTrie<T, PTR, LEN> {
784    pub fn insert(&mut self, key: Vec<u8>, value: T) -> Result<usize, ()> {
785        // Overflow checks
786        if self.arena.len() >= PTR::max_value() || self.index.len() >= PTR::max_value() {
787            return Err(());
788        }
789        if key.len() * 4 > LEN::max_value() {
790            return Err(());
791        }
792
793        let new_index = PTR::from_usize(self.index.len());
794        let key_len = LEN::from_usize(key.len());
795        let offset = self.buf.len() as usize;
796        self.buf.extend_from_slice(&key);
797        self.index.push((offset, key_len));
798        self.values.push(value);
799
800        let max_nib = key.len() * 4;
801
802        if self.arena.is_empty() {
803            return Ok(self.insert_into_empty_trie(&key, new_index, max_nib));
804        }
805
806        let mut node_idx: usize = 0;
807        let mut confirmed: usize = 0;
808
809        loop {
810            let node = &self.arena[node_idx];
811            let ki = node.leaf;
812            let (off, ref_len) = self.index[ki.as_usize()];
813            let ref_key = &self.buf[off..off + ref_len.as_usize()];
814            let prefix_len = node.prefix_len.as_usize();
815
816            match simd_check_prefix::<8>(&key, ref_key, confirmed, prefix_len) {
817                PrefixCheck::Diverges(diverge) => {
818                    return Ok(self.split_node_before_prefix(
819                        node_idx, diverge, new_index, &key, max_nib,
820                    ));
821                }
822                PrefixCheck::Matches => {
823                    if max_nib == prefix_len {
824                        if key.len() == ref_key.len() {
825                            self.rollback_last_insert();
826                            return Err(());
827                        }
828                        self.arena[node_idx].set_terminal(true);
829                        self.arena[node_idx].leaf = new_index;
830                        return Ok(new_index.as_usize());
831                    }
832
833                    confirmed = prefix_len + 1;
834                    let nib = key_nib_at(&key, prefix_len) as usize;
835                    if !node.is_occupied(nib) {
836                        // Empty slot — new key diverges here
837                        self.arena[node_idx].set_leaf_child(nib, new_index);
838                        return Ok(new_index.as_usize());
839                    }
840                    let slot = node.children[nib];
841
842                    if node.is_leaf(nib) {
843                        return self.split_leaf_child(
844                            nib, node_idx, slot, new_index, &key, max_nib, confirmed,
845                        );
846                    }
847
848                    // Internal child — descend
849                    node_idx = slot.as_usize();
850                }
851            }
852        }
853    }
854
855    // -----------------------------------------------------------------------
856    // Insert helpers
857    // -----------------------------------------------------------------------
858
859    #[inline]
860    fn rollback_last_insert(&mut self) {
861        let (off, _len) = self.index.pop().unwrap();
862        self.buf.truncate(off);
863        let _ = self.values.pop();
864    }
865
866    #[inline]
867    fn insert_into_empty_trie(&mut self, key: &[u8], new_index: PTR, max_nib: usize) -> usize {
868        if max_nib == 0 {
869            let mut root = NibNode::new();
870            root.set_terminal(true);
871            root.leaf = new_index;
872            root.prefix_len = LEN::zero();
873            self.arena.push(root);
874            return new_index.as_usize();
875        }
876        let first_nib = key_nib_at(key, 0) as usize;
877        let mut root = NibNode::new();
878        root.set_leaf_child(first_nib, new_index);
879        root.leaf = new_index;
880        root.prefix_len = LEN::zero();
881        self.arena.push(root);
882        new_index.as_usize()
883    }
884
885    #[inline]
886    fn split_node_before_prefix(
887        &mut self,
888        node_idx: usize,
889        diverge: usize,
890        new_index: PTR,
891        key: &[u8],
892        max_nib: usize,
893    ) -> usize {
894        let node = &self.arena[node_idx];
895        let ki = node.leaf;
896        let (off, ref_len) = self.index[ki.as_usize()];
897        let ref_key = &self.buf[off..off + ref_len.as_usize()];
898
899        let new_nib = key_nib_at(key, diverge) as usize;
900        let ref_nib = key_nib_at(ref_key, diverge) as usize;
901
902        let mut new_parent = NibNode::new();
903        new_parent.prefix_len = LEN::from_usize(diverge);
904
905        if diverge >= max_nib {
906            new_parent.set_terminal(true);
907            new_parent.leaf = new_index;
908        } else {
909            new_parent.set_leaf_child(new_nib, new_index);
910            new_parent.leaf = new_index;
911        }
912
913        let old_node = std::mem::replace(&mut self.arena[node_idx], new_parent);
914        let old_addr = PTR::from_usize(self.arena.len()); // new node at next slot
915        self.arena.push(old_node);
916
917        self.arena[node_idx].set_internal_child(ref_nib, old_addr);
918
919        new_index.as_usize()
920    }
921
922    #[inline]
923    fn split_leaf_child(
924        &mut self,
925        nib: usize,
926        node_idx: usize,
927        existing_key_index: PTR,
928        new_index: PTR,
929        key: &[u8],
930        max_nib: usize,
931        confirmed: usize,
932    ) -> Result<usize, ()> {
933        let (existing_offset, existing_len) = self.index[existing_key_index.as_usize()];
934        let existing_key = &self.buf[existing_offset..existing_offset + existing_len.as_usize()];
935
936        match simd_find_divergence::<8>(key, existing_key, confirmed) {
937            DivergeResult::Duplicate => {
938                self.rollback_last_insert();
939                Err(())
940            }
941            DivergeResult::At(d) => {
942                let mut split_node = NibNode::new();
943                split_node.prefix_len = LEN::from_usize(d);
944
945                if d >= max_nib {
946                    // New key ends at the split point — terminal
947                    let exist_nib = key_nib_at(existing_key, d) as usize;
948                    split_node.set_terminal(true);
949                    split_node.leaf = new_index;
950                    split_node.set_leaf_child(exist_nib, existing_key_index);
951                } else if d >= existing_key.len() * 4 {
952                    // Existing key ends at the split point — terminal
953                    let new_nib = key_nib_at(key, d) as usize;
954                    split_node.set_terminal(true);
955                    split_node.leaf = existing_key_index;
956                    split_node.set_leaf_child(new_nib, new_index);
957                } else {
958                    // Neither key ends at the split point
959                    let new_nib = key_nib_at(key, d) as usize;
960                    let exist_nib = key_nib_at(existing_key, d) as usize;
961                    debug_assert_ne!(new_nib, exist_nib);
962                    split_node.set_leaf_child(new_nib, new_index);
963                    split_node.set_leaf_child(exist_nib, existing_key_index);
964                    split_node.leaf = existing_key_index;
965                }
966
967                let split_addr = PTR::from_usize(self.arena.len());
968                self.arena.push(split_node);
969                self.arena[node_idx].set_internal_child(nib, split_addr);
970
971                Ok(new_index.as_usize())
972            }
973        }
974    }
975}
976
977// ---------------------------------------------------------------------------
978// PTR width conversions (promote/demote)
979// ---------------------------------------------------------------------------
980
981impl<T, PTR: TrieIndex, LEN: TrieIndex> NibTrie<T, PTR, LEN> {
982    /// Promote the arena index type to a wider PTR.
983    pub fn promote<NewPTR: TrieIndex>(self) -> NibTrie<T, NewPTR, LEN> {
984        let arena = self.arena.into_iter().map(|node| node.promote()).collect();
985        NibTrie {
986            arena,
987            buf: self.buf,
988            index: self.index,
989            values: self.values,
990        }
991    }
992
993    /// Demote the arena index type to a narrower PTR.
994    /// Returns `Err(self)` if any address doesn't fit.
995    pub fn demote<NewPTR: TrieIndex>(self) -> Result<NibTrie<T, NewPTR, LEN>, Self> {
996        if self.arena.len() > NewPTR::max_value() || self.index.len() > NewPTR::max_value() {
997            return Err(self);
998        }
999        for node in &self.arena {
1000            if let Err(_) = node.demote::<NewPTR>() {
1001                return Err(self);
1002            }
1003        }
1004        let arena = self.arena.into_iter().map(|node| {
1005            node.demote().expect("demote capacity check should have caught this")
1006        }).collect();
1007        Ok(NibTrie {
1008            arena,
1009            buf: self.buf,
1010            index: self.index,
1011            values: self.values,
1012        })
1013    }
1014}
1015
1016// ---------------------------------------------------------------------------
1017// Iterator
1018// ---------------------------------------------------------------------------
1019
1020/// Sentinel nib value meaning "positioned at the terminal value of this node."
1021const TERMINAL_NIB: usize = 4;
1022
1023pub struct Cursor<'a, T, PTR: TrieIndex, LEN: TrieIndex> {
1024    trie: &'a NibTrie<T, PTR, LEN>,
1025    /// Stack of (node_index, occupancy_mask, nib_position) tuples.
1026    stack: Vec<(usize, u8, usize)>,
1027}
1028
1029impl<'a, T, PTR: TrieIndex, LEN: TrieIndex> Cursor<'a, T, PTR, LEN> {
1030    fn new(trie: &'a NibTrie<T, PTR, LEN>) -> Self {
1031        if trie.arena.is_empty() {
1032            return Cursor { trie, stack: Vec::new() };
1033        }
1034        let mask = trie.arena[0].occupancy;
1035        let nib = if trie.arena[0].is_terminal() { TERMINAL_NIB } else { usize::MAX };
1036        Cursor { trie, stack: vec![(0, mask, nib)] }
1037    }
1038
1039    fn new_last(trie: &'a NibTrie<T, PTR, LEN>) -> Self {
1040        if trie.arena.is_empty() {
1041            return Cursor { trie, stack: Vec::new() };
1042        }
1043        let mut stack = Vec::new();
1044        let mut node_idx: usize = 0;
1045        loop {
1046            let node = &trie.arena[node_idx];
1047            let mask = node.occupancy;
1048            if mask != 0 {
1049                let nib = (mask as u32).ilog2() as usize; // highest set bit (only bits 0-3 used)
1050                stack.push((node_idx, mask, nib));
1051                if node.is_leaf(nib) {
1052                    break;
1053                } else {
1054                    node_idx = node.children[nib].as_usize();
1055                }
1056            } else if node.is_terminal() {
1057                stack.push((node_idx, mask, TERMINAL_NIB));
1058                break;
1059            } else {
1060                break;
1061            }
1062        }
1063        Cursor { trie, stack }
1064    }
1065
1066    fn descend_first(&mut self, mut node_idx: usize) {
1067        loop {
1068            let node = &self.trie.arena[node_idx];
1069            if node.is_terminal() {
1070                let mask = node.occupancy;
1071                self.stack.push((node_idx, mask, TERMINAL_NIB));
1072                return;
1073            }
1074            let mask = node.occupancy;
1075            debug_assert!(mask != 0, "descend_first: non-terminal node with no children");
1076            let nib = mask.trailing_zeros() as usize;
1077            debug_assert!(nib < 4);
1078            self.stack.push((node_idx, mask, nib));
1079            if node.is_leaf(nib) {
1080                return;
1081            } else {
1082                node_idx = node.children[nib].as_usize();
1083            }
1084        }
1085    }
1086
1087    fn descend_last(&mut self, mut node_idx: usize) {
1088        loop {
1089            let node = &self.trie.arena[node_idx];
1090            if node.is_terminal() && node.occupancy == 0 {
1091                self.stack.push((node_idx, node.occupancy, TERMINAL_NIB));
1092                return;
1093            }
1094            let mask = node.occupancy;
1095            if mask == 0 {
1096                if node.is_terminal() {
1097                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1098                }
1099                return;
1100            }
1101            let nib = (mask as u32).ilog2() as usize;
1102            debug_assert!(nib < 4);
1103            self.stack.push((node_idx, mask, nib));
1104            if node.is_leaf(nib) {
1105                return;
1106            } else {
1107                node_idx = node.children[nib].as_usize();
1108            }
1109        }
1110    }
1111
1112    #[inline]
1113    fn push_next_child(&mut self, node_idx: usize, mask: u8, start_nib: usize) -> bool {
1114        let shifted = if start_nib >= 4 { 0u8 } else { mask >> start_nib };
1115        if shifted == 0 {
1116            return false;
1117        }
1118        let nib = start_nib + shifted.trailing_zeros() as usize;
1119        debug_assert!(nib < 4);
1120        debug_assert!(mask & (1 << nib) != 0);
1121        self.stack.push((node_idx, mask, nib));
1122        if !self.trie.arena[node_idx].is_leaf(nib) {
1123            let addr = self.trie.arena[node_idx].children[nib].as_usize();
1124            self.descend_first(addr);
1125        }
1126        true
1127    }
1128
1129    #[inline]
1130    fn backtrack_to_next(&mut self) -> Option<(&[u8], &T)> {
1131        loop {
1132            let (parent_idx, parent_mask, child_nib) = self.stack.pop()?;
1133            if self.push_next_child(parent_idx, parent_mask, child_nib + 1) {
1134                return self.current();
1135            }
1136        }
1137    }
1138
1139    pub fn current(&self) -> Option<(&[u8], &T)> {
1140        let (_, _, nib) = self.stack.last()?;
1141        if *nib == usize::MAX {
1142            return None;
1143        }
1144        let (node_idx, _, _) = self.stack.last()?;
1145        let node = &self.trie.arena[*node_idx];
1146        if *nib == TERMINAL_NIB {
1147            let ki = node.leaf;
1148            let (off, len) = self.trie.index[ki.as_usize()];
1149            let key = &self.trie.buf[off..off + len.as_usize()];
1150            let value = &self.trie.values[ki.as_usize() - 1];
1151            Some((key, value))
1152        } else if let Some(key_index) = node.leaf_key_index(*nib) {
1153            let key = self.trie.key_slice(key_index);
1154            let value = &self.trie.values[key_index.as_usize() - 1];
1155            Some((key, value))
1156        } else {
1157            None
1158        }
1159    }
1160
1161    pub fn current_index(&self) -> Option<usize> {
1162        let &(_, _, nib) = self.stack.last()?;
1163        if nib == usize::MAX {
1164            return None;
1165        }
1166        let (node_idx, _, _) = self.stack.last()?;
1167        let node = &self.trie.arena[*node_idx];
1168        if nib == TERMINAL_NIB {
1169            Some(node.leaf.as_usize())
1170        } else {
1171            node.leaf_key_index(nib).map(|ki| ki.as_usize())
1172        }
1173    }
1174
1175    #[inline]
1176    fn advance_next(&mut self) -> bool {
1177        loop {
1178            let (node_idx, mask, nib) = match self.stack.pop() {
1179                Some(v) => v,
1180                None => return false,
1181            };
1182
1183            if nib == TERMINAL_NIB {
1184                if self.push_next_child(node_idx, mask, 0) {
1185                    return true;
1186                }
1187                continue;
1188            }
1189
1190            let search_start = if nib == usize::MAX { 0 } else { nib + 1 };
1191            if self.push_next_child(node_idx, mask, search_start) {
1192                return true;
1193            }
1194        }
1195    }
1196
1197    #[inline]
1198    fn advance_prev(&mut self) -> bool {
1199        loop {
1200            let (node_idx, mask, nib) = match self.stack.pop() {
1201                Some(v) => v,
1202                None => return false,
1203            };
1204
1205            if nib == TERMINAL_NIB {
1206                continue;
1207            }
1208
1209            if nib == 0 || nib == usize::MAX {
1210                let node = &self.trie.arena[node_idx];
1211                if node.is_terminal() {
1212                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1213                    return true;
1214                }
1215                continue;
1216            }
1217
1218            let mask_below = mask & ((1 << nib) - 1);
1219            if mask_below != 0 {
1220                // Highest set bit in mask_below (only bits 0-3 are valid)
1221                let prev_nib = (mask_below as u32).ilog2() as usize;
1222                self.stack.push((node_idx, mask, prev_nib));
1223                if !self.trie.arena[node_idx].is_leaf(prev_nib) {
1224                    let addr = self.trie.arena[node_idx].children[prev_nib].as_usize();
1225                    self.descend_last(addr);
1226                }
1227                return true;
1228            }
1229
1230            let node = &self.trie.arena[node_idx];
1231            if node.is_terminal() {
1232                self.stack.push((node_idx, mask, TERMINAL_NIB));
1233                return true;
1234            }
1235        }
1236    }
1237
1238    #[inline]
1239    pub fn next_index(&mut self) -> Option<usize> {
1240        if self.advance_next() { self.current_index() } else { None }
1241    }
1242
1243    #[inline]
1244    pub fn prev_index(&mut self) -> Option<usize> {
1245        if self.advance_prev() { self.current_index() } else { None }
1246    }
1247
1248    #[inline]
1249    pub fn next(&mut self) -> Option<(&[u8], &T)> {
1250        if self.advance_next() { self.current() } else { None }
1251    }
1252
1253    #[inline]
1254    pub fn prev(&mut self) -> Option<(&[u8], &T)> {
1255        if self.advance_prev() { self.current() } else { None }
1256    }
1257
1258    pub fn seek(&mut self, key: &[u8]) -> Option<(&[u8], &T)> {
1259        if self.trie.arena.is_empty() {
1260            self.stack.clear();
1261            return None;
1262        }
1263
1264        self.stack.clear();
1265        let mut node_idx: usize = 0;
1266        let max_nib = key.len() * 4;
1267
1268        loop {
1269            let node = &self.trie.arena[node_idx];
1270            let mask = node.occupancy;
1271
1272            if node.is_terminal() && node.prefix_len.as_usize() >= max_nib {
1273                let ki = node.leaf;
1274                let (off, len) = self.trie.index[ki.as_usize()];
1275                let node_key = &self.trie.buf[off..off + len.as_usize()];
1276                if node_key >= key {
1277                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1278                    return self.current();
1279                }
1280            }
1281
1282            if node.prefix_len.as_usize() >= max_nib {
1283                if self.push_next_child(node_idx, mask, 0) {
1284                    return self.current();
1285                }
1286                return self.backtrack_to_next();
1287            }
1288
1289            let nib = key_nib_at(key, node.prefix_len.as_usize()) as usize;
1290            if !node.is_occupied(nib) {
1291                // No child at this nibble — find next higher child, or backtrack
1292                if self.push_next_child(node_idx, mask, nib + 1) {
1293                    return self.current();
1294                }
1295                return self.backtrack_to_next();
1296            }
1297
1298            self.stack.push((node_idx, mask, nib));
1299            let slot = node.children[nib];
1300            if node.is_leaf(nib) {
1301                let leaf_key = self.trie.key_slice(slot);
1302                if leaf_key >= key {
1303                    return self.current();
1304                }
1305                // Leaf key < seek key: advance past it
1306                return self.next();
1307            } else {
1308                node_idx = slot.as_usize();
1309            }
1310        }
1311    }
1312}
1313
1314// ---------------------------------------------------------------------------
1315// CursorMut — lending tree-walk iterator handing out &mut T
1316// ---------------------------------------------------------------------------
1317
1318/// Mutable counterpart to [`Cursor`]: a tree-walk iterator that lends out
1319/// `&mut T` borrows over the stored values, in sorted (DFS) key order.
1320///
1321/// Unlike [`Cursor`], the value reference is tied to `&mut self` (a *lending*
1322/// cursor), not to the trie lifetime `'a`. This is a soundness requirement, not
1323/// a stylistic choice: a cursor is re-positionable — `current()`, `seek()`,
1324/// `first()`, `last()` can all revisit a slot already visited. An `'a`-tied
1325/// `&mut T` (as the immutable cursor hands out `&'a T`) would let two such
1326/// calls return `&mut T` to the *same* element simultaneously — aliasing
1327/// undefined behavior. Tying the borrow to `&mut self` makes the borrow checker
1328/// enforce "one live `&mut T` at a time," which is the only sound rule for a
1329/// re-positionable mutable cursor. The practical consequence: you cannot
1330/// collect the `&mut T` into a `Vec` or hold two at once; each must be released
1331/// before the next `next()`/`prev()`/`current()`/`seek()` call. In-place
1332/// mutation loops (`while let Some((k, v)) = c.next() { *v += 1; }`) work as
1333/// expected.
1334///
1335/// The key is returned as `&[u8]` borrowing the trie's key buffer (zero
1336/// allocation, matching the immutable cursor's borrowed key). Both the key and
1337/// the `&mut T` are tied to `&mut self`. Only the stored *value* is mutated;
1338/// the cursor never alters key bytes, node structure, or slot occupancy, so
1339/// trie invariants are preserved.
1340pub struct CursorMut<'a, T, PTR: TrieIndex, LEN: TrieIndex> {
1341    trie: &'a mut NibTrie<T, PTR, LEN>,
1342    /// Stack of (node_index, occupancy_mask, nib_position) tuples — same shape
1343    /// as [`Cursor::stack`].
1344    stack: Vec<(usize, u8, usize)>,
1345}
1346
1347impl<'a, T, PTR: TrieIndex, LEN: TrieIndex> CursorMut<'a, T, PTR, LEN> {
1348    /// Forward mutable cursor parked *before* the first key.
1349    pub fn new(trie: &'a mut NibTrie<T, PTR, LEN>) -> Self {
1350        if trie.arena.is_empty() {
1351            return CursorMut { trie, stack: Vec::new() };
1352        }
1353        let mask = trie.arena[0].occupancy;
1354        let nib = if trie.arena[0].is_terminal() { TERMINAL_NIB } else { usize::MAX };
1355        CursorMut { trie, stack: vec![(0, mask, nib)] }
1356    }
1357
1358    /// Reverse mutable cursor parked *on* the last key (or empty if the trie is
1359    /// empty).
1360    pub fn new_last(trie: &'a mut NibTrie<T, PTR, LEN>) -> Self {
1361        let mut c = CursorMut { trie, stack: Vec::new() };
1362        c.last();
1363        c
1364    }
1365
1366    fn descend_first(&mut self, mut node_idx: usize) {
1367        loop {
1368            let node = &self.trie.arena[node_idx];
1369            if node.is_terminal() {
1370                let mask = node.occupancy;
1371                self.stack.push((node_idx, mask, TERMINAL_NIB));
1372                return;
1373            }
1374            let mask = node.occupancy;
1375            debug_assert!(mask != 0, "descend_first: non-terminal node with no children");
1376            let nib = mask.trailing_zeros() as usize;
1377            debug_assert!(nib < 4);
1378            self.stack.push((node_idx, mask, nib));
1379            if node.is_leaf(nib) {
1380                return;
1381            } else {
1382                node_idx = node.children[nib].as_usize();
1383            }
1384        }
1385    }
1386
1387    fn descend_last(&mut self, mut node_idx: usize) {
1388        loop {
1389            let node = &self.trie.arena[node_idx];
1390            if node.is_terminal() && node.occupancy == 0 {
1391                self.stack.push((node_idx, node.occupancy, TERMINAL_NIB));
1392                return;
1393            }
1394            let mask = node.occupancy;
1395            if mask == 0 {
1396                if node.is_terminal() {
1397                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1398                }
1399                return;
1400            }
1401            let nib = (mask as u32).ilog2() as usize;
1402            debug_assert!(nib < 4);
1403            self.stack.push((node_idx, mask, nib));
1404            if node.is_leaf(nib) {
1405                return;
1406            } else {
1407                node_idx = node.children[nib].as_usize();
1408            }
1409        }
1410    }
1411
1412    #[inline]
1413    fn push_next_child(&mut self, node_idx: usize, mask: u8, start_nib: usize) -> bool {
1414        let shifted = if start_nib >= 4 { 0u8 } else { mask >> start_nib };
1415        if shifted == 0 {
1416            return false;
1417        }
1418        let nib = start_nib + shifted.trailing_zeros() as usize;
1419        debug_assert!(nib < 4);
1420        debug_assert!(mask & (1 << nib) != 0);
1421        self.stack.push((node_idx, mask, nib));
1422        if !self.trie.arena[node_idx].is_leaf(nib) {
1423            let addr = self.trie.arena[node_idx].children[nib].as_usize();
1424            self.descend_first(addr);
1425        }
1426        true
1427    }
1428
1429    #[inline]
1430    fn backtrack_to_next(&mut self) -> Option<(&[u8], &mut T)> {
1431        loop {
1432            let (parent_idx, parent_mask, child_nib) = self.stack.pop()?;
1433            if self.push_next_child(parent_idx, parent_mask, child_nib + 1) {
1434                return self.current();
1435            }
1436        }
1437    }
1438
1439    /// The key/value the cursor is parked on, or `None` if not parked (before
1440    /// first, or exhausted). The key borrows the trie's key buffer and the
1441    /// `&mut T` reborrows the stored value — both tied to `&mut self`.
1442    ///
1443    /// Three sequential, non-overlapping borrows: (1) shared peek of `stack` /
1444    /// `arena` to recover the key index (copied out as `usize`), (2) shared
1445    /// read of `index`/`buf` for the key slice, (3) mutable borrow of `values`
1446    /// for `&mut T`. `buf` and `values` are disjoint fields, so the shared key
1447    /// borrow and the mutable value borrow coexist.
1448    #[inline]
1449    pub fn current(&mut self) -> Option<(&[u8], &mut T)> {
1450        let (node_idx, _, nib) = *self.stack.last()?;
1451        if nib == usize::MAX {
1452            return None;
1453        }
1454        let ki: PTR = if nib == TERMINAL_NIB {
1455            self.trie.arena[node_idx].leaf
1456        } else {
1457            self.trie.arena[node_idx].leaf_key_index(nib)?
1458        };
1459        let (off, len) = self.trie.index[ki.as_usize()];
1460        let key = &self.trie.buf[off..off + len.as_usize()];
1461        let value = &mut self.trie.values[ki.as_usize() - 1];
1462        Some((key, value))
1463    }
1464
1465    /// The key index the cursor is parked on, or `None` if not parked.
1466    #[inline]
1467    pub fn current_index(&self) -> Option<usize> {
1468        let &(_, _, nib) = self.stack.last()?;
1469        if nib == usize::MAX {
1470            return None;
1471        }
1472        let (node_idx, _, _) = *self.stack.last()?;
1473        let node = &self.trie.arena[node_idx];
1474        if nib == TERMINAL_NIB {
1475            Some(node.leaf.as_usize())
1476        } else {
1477            node.leaf_key_index(nib).map(|ki| ki.as_usize())
1478        }
1479    }
1480
1481    #[inline]
1482    fn advance_next(&mut self) -> bool {
1483        loop {
1484            let (node_idx, mask, nib) = match self.stack.pop() {
1485                Some(v) => v,
1486                None => return false,
1487            };
1488
1489            if nib == TERMINAL_NIB {
1490                if self.push_next_child(node_idx, mask, 0) {
1491                    return true;
1492                }
1493                continue;
1494            }
1495
1496            let search_start = if nib == usize::MAX { 0 } else { nib + 1 };
1497            if self.push_next_child(node_idx, mask, search_start) {
1498                return true;
1499            }
1500        }
1501    }
1502
1503    #[inline]
1504    fn advance_prev(&mut self) -> bool {
1505        loop {
1506            let (node_idx, mask, nib) = match self.stack.pop() {
1507                Some(v) => v,
1508                None => return false,
1509            };
1510
1511            if nib == TERMINAL_NIB {
1512                continue;
1513            }
1514
1515            if nib == 0 || nib == usize::MAX {
1516                let node = &self.trie.arena[node_idx];
1517                if node.is_terminal() {
1518                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1519                    return true;
1520                }
1521                continue;
1522            }
1523
1524            let mask_below = mask & ((1 << nib) - 1);
1525            if mask_below != 0 {
1526                let prev_nib = (mask_below as u32).ilog2() as usize;
1527                self.stack.push((node_idx, mask, prev_nib));
1528                if !self.trie.arena[node_idx].is_leaf(prev_nib) {
1529                    let addr = self.trie.arena[node_idx].children[prev_nib].as_usize();
1530                    self.descend_last(addr);
1531                }
1532                return true;
1533            }
1534
1535            let node = &self.trie.arena[node_idx];
1536            if node.is_terminal() {
1537                self.stack.push((node_idx, mask, TERMINAL_NIB));
1538                return true;
1539            }
1540        }
1541    }
1542
1543    /// Jump to the first key (smallest in sorted order). Returns its key/value,
1544    /// or `None` if the trie is empty.
1545    pub fn first(&mut self) -> Option<(&[u8], &mut T)> {
1546        if self.trie.arena.is_empty() {
1547            self.stack.clear();
1548            return None;
1549        }
1550        let mask = self.trie.arena[0].occupancy;
1551        let nib = if self.trie.arena[0].is_terminal() { TERMINAL_NIB } else { usize::MAX };
1552        self.stack.clear();
1553        self.stack.push((0, mask, nib));
1554        if nib == TERMINAL_NIB {
1555            // Parked on the root's terminal value — `current` returns it.
1556            return self.current();
1557        }
1558        // Before-first sentinel — advance to the first key.
1559        if self.advance_next() { self.current() } else { None }
1560    }
1561
1562    /// Jump to the last key (largest in sorted order). Returns its key/value,
1563    /// or `None` if the trie is empty.
1564    pub fn last(&mut self) -> Option<(&[u8], &mut T)> {
1565        if self.trie.arena.is_empty() {
1566            self.stack.clear();
1567            return None;
1568        }
1569        self.stack.clear();
1570        let mut node_idx: usize = 0;
1571        loop {
1572            let node = &self.trie.arena[node_idx];
1573            let mask = node.occupancy;
1574            if mask != 0 {
1575                let nib = (mask as u32).ilog2() as usize;
1576                self.stack.push((node_idx, mask, nib));
1577                if node.is_leaf(nib) {
1578                    break;
1579                } else {
1580                    node_idx = node.children[nib].as_usize();
1581                }
1582            } else if node.is_terminal() {
1583                self.stack.push((node_idx, mask, TERMINAL_NIB));
1584                break;
1585            } else {
1586                break;
1587            }
1588        }
1589        self.current()
1590    }
1591
1592    #[inline]
1593    pub fn next(&mut self) -> Option<(&[u8], &mut T)> {
1594        if self.advance_next() { self.current() } else { None }
1595    }
1596
1597    #[inline]
1598    pub fn prev(&mut self) -> Option<(&[u8], &mut T)> {
1599        if self.advance_prev() { self.current() } else { None }
1600    }
1601
1602    #[inline]
1603    pub fn next_index(&mut self) -> Option<usize> {
1604        if self.advance_next() { self.current_index() } else { None }
1605    }
1606
1607    #[inline]
1608    pub fn prev_index(&mut self) -> Option<usize> {
1609        if self.advance_prev() { self.current_index() } else { None }
1610    }
1611
1612    pub fn seek(&mut self, key: &[u8]) -> Option<(&[u8], &mut T)> {
1613        if self.trie.arena.is_empty() {
1614            self.stack.clear();
1615            return None;
1616        }
1617
1618        self.stack.clear();
1619        let mut node_idx: usize = 0;
1620        let max_nib = key.len() * 4;
1621
1622        loop {
1623            let node = &self.trie.arena[node_idx];
1624            let mask = node.occupancy;
1625
1626            if node.is_terminal() && node.prefix_len.as_usize() >= max_nib {
1627                let ki = node.leaf;
1628                let (off, len) = self.trie.index[ki.as_usize()];
1629                let node_key = &self.trie.buf[off..off + len.as_usize()];
1630                if node_key >= key {
1631                    self.stack.push((node_idx, mask, TERMINAL_NIB));
1632                    return self.current();
1633                }
1634            }
1635
1636            if node.prefix_len.as_usize() >= max_nib {
1637                if self.push_next_child(node_idx, mask, 0) {
1638                    return self.current();
1639                }
1640                return self.backtrack_to_next();
1641            }
1642
1643            let nib = key_nib_at(key, node.prefix_len.as_usize()) as usize;
1644            if !node.is_occupied(nib) {
1645                if self.push_next_child(node_idx, mask, nib + 1) {
1646                    return self.current();
1647                }
1648                return self.backtrack_to_next();
1649            }
1650
1651            self.stack.push((node_idx, mask, nib));
1652            let slot = node.children[nib];
1653            if node.is_leaf(nib) {
1654                let leaf_key = self.trie.key_slice(slot);
1655                if leaf_key >= key {
1656                    return self.current();
1657                }
1658                return self.next();
1659            } else {
1660                node_idx = slot.as_usize();
1661            }
1662        }
1663    }
1664}
1665
1666#[cfg(test)]
1667#[path = "tests/nib_trie.rs"]
1668mod tests;