1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Arena-aware prefix iteration for `PersistentARTrie<V, S>`.
//!
//! Split out of byte `dict_impl.rs` (Phase-5 byte sub-module). The public entry
//! points:
//!
//! - `iter_prefix_with_arena` / `iter_prefix_with_values_and_arena` —
//! public prefix-enumeration entry points
//!
//! **L3.3c:** the owned tree is gone, so these now enumerate the lock-free overlay
//! directly (`arena_id` is `None` for every resident overlay term); the owned DFS
//! collectors + `navigate_to_prefix_with_arena` were deleted.
use crate::value::DictionaryValue;
use super::block_storage::BlockStorage;
use super::dict_impl::{PersistentARTrie, PrefixTermWithArena, PrefixTermWithValueAndArena};
use super::error::Result;
impl<V: DictionaryValue, S: BlockStorage> PersistentARTrie<V, S> {
// =========================================================================
// Arena-aware iteration and merge operations
// =========================================================================
// L3.3c: removed — `navigate_to_prefix_with_arena` + `collect_terms_with_arena`
// + `collect_terms_with_values_and_arena` walked the deleted owned `self.root` /
// `TrieRoot` / `ChildNode` representation. The public `iter_prefix_with_arena` /
// `iter_prefix_with_values_and_arena` chokepoints now enumerate the overlay
// directly.
/// Iterate over all terms with the given prefix, including arena locations.
///
/// Returns all terms matching the prefix along with their disk arena IDs.
/// This enables page-aware batch operations by grouping terms by arena.
///
/// # Arguments
///
/// * `prefix` - The byte prefix to search for
///
/// # Returns
///
/// - `Ok(Some(vec))` - Vector of terms with arena info
/// - `Ok(None)` - The prefix path doesn't exist
/// - `Err` - An I/O error occurred
pub fn iter_prefix_with_arena(
&self,
prefix: &[u8],
) -> Result<Option<Vec<PrefixTermWithArena>>> {
// **M3 read-flip (C6) — L3.3c collapse.** This is the byte read CHOKEPOINT:
// `iter` / `iter_prefix` / `compaction_snapshot` / the merge readers all funnel
// through it. The owned tree is gone (L3.3c), so this is now unconditionally the
// overlay enumeration. Overlay nodes are all resident (in-memory), so `arena_id`
// is `None` for every term; arena grouping is a disk-page-locality no-op for the
// in-memory overlay, but the TERMS are faithful (resident-finals, non-faulting).
Ok(self.overlay_iter_prefix(prefix).map(|terms| {
terms
.into_iter()
.map(|term| PrefixTermWithArena {
term,
arena_id: None,
})
.collect()
}))
}
/// Iterate over all terms with values and arena locations for the given prefix.
///
/// Returns all (term, value, arena_id) tuples matching the prefix.
/// This enables page-locality optimized merge operations.
///
/// **C6 + the audit's §C.2 VALUE-CARRYING rule.** The overlay is the sole
/// representation, so this routes to the VALUE-CARRYING overlay enumerator
/// [`overlay_iter_prefix_with_values`](Self::overlay_iter_prefix_with_values) —
/// NOT enumerate-overlay-then-value-owned. `arena_id` is `None` for every resident
/// overlay term. This is the value-carrying read chokepoint: `iter_prefix_with_values`
/// funnels through it, so routing it here routes that surface too.
pub fn iter_prefix_with_values_and_arena(
&self,
prefix: &[u8],
) -> Result<Option<Vec<PrefixTermWithValueAndArena<V>>>>
where
V: Clone,
{
// **M3 read-flip (C6 + §C.2 VALUE-CARRYING rule) — L3.3c collapse.** The owned
// tree is gone (L3.3c), so this routes unconditionally to the VALUE-CARRYING
// overlay enumerator
// [`overlay_iter_prefix_with_values`](Self::overlay_iter_prefix_with_values).
// `arena_id` is `None` for every resident overlay term. This is the
// value-carrying read chokepoint: `iter_prefix_with_values` funnels through it.
Ok(self.overlay_iter_prefix_with_values(prefix).map(|entries| {
entries
.into_iter()
.map(|(term, value)| PrefixTermWithValueAndArena {
term,
value,
arena_id: None,
})
.collect()
}))
}
}