1use super::{
2 branch_child_idx::{BranchChildIdx, BranchChildIter},
3 ArenaSparseNode, ArenaSparseNodeState, BranchChild, Index, NodeArena,
4};
5use alloc::vec::Vec;
6use reth_trie_common::Nibbles;
7use tracing::{instrument, trace};
8
9const TRACE_TARGET: &str = "trie::arena::cursor";
10
11#[derive(Debug, Clone)]
16pub(super) struct ArenaCursorStackEntry {
17 pub(super) index: Index,
19 path_len: u8,
22 next_dense_idx: u8,
26}
27
28#[derive(Debug)]
30pub(super) enum SeekResult {
31 EmptyRoot,
33 RevealedLeaf,
35 Blinded,
37 Diverged,
39 NoChild { child_nibble: u8 },
41 RevealedSubtrie,
43}
44
45#[derive(Debug)]
47pub(super) enum NextResult {
48 NonBranch,
51 Branch,
54 Done,
56}
57
58pub(super) fn branch_idx(arena: &NodeArena, idx: Index) -> Option<Index> {
60 match &arena[idx] {
61 ArenaSparseNode::Branch(_) => Some(idx),
62 ArenaSparseNode::Extension { child, .. } => Some(*child),
63 _ => None,
64 }
65}
66
67#[derive(Debug, Default, Clone)]
76pub(super) struct ArenaCursor {
77 stack: Vec<ArenaCursorStackEntry>,
78 path: Nibbles,
86 needs_pop: bool,
89}
90
91impl ArenaCursor {
92 pub(super) fn head(&self) -> Option<&ArenaCursorStackEntry> {
94 self.stack.last()
95 }
96
97 pub(super) fn parent(&self) -> Option<&ArenaCursorStackEntry> {
99 let len = self.stack.len();
100 (len >= 2).then(|| &self.stack[len - 2])
101 }
102
103 pub(super) const fn depth(&self) -> usize {
109 self.stack.len() - 1
110 }
111
112 pub(super) fn head_path(&self) -> Nibbles {
114 self.entry_path(self.head_entry())
115 }
116
117 pub(super) fn head_path_len(&self) -> usize {
120 self.head_entry().path_len as usize
121 }
122
123 pub(super) fn head_last_nibble(&self) -> Option<u8> {
126 self.head_path_len().checked_sub(1).map(|i| self.path.get_unchecked(i))
127 }
128
129 #[instrument(level = "trace", target = TRACE_TARGET, skip(self, arena))]
133 pub(super) fn reset(&mut self, arena: &NodeArena, idx: Index, path: Nibbles) {
134 debug_assert!(
135 self.stack.len() <= 1 && !self.needs_pop,
136 "cursor must be drained before reset; stack has {} entries, needs_pop={}",
137 self.stack.len(),
138 self.needs_pop,
139 );
140 self.stack.clear();
141 self.needs_pop = false;
142 self.path = path;
143 self.push(arena, idx, path.len());
144 }
145
146 fn push(&mut self, arena: &NodeArena, idx: Index, path_len: usize) {
149 debug_assert!(arena.contains_key(idx), "push called with invalid arena index");
150 debug_assert!(
151 path_len <= self.path.len(),
152 "pushed path length {path_len} exceeds cursor path {:?}",
153 self.path,
154 );
155 self.stack.push(ArenaCursorStackEntry {
156 index: idx,
157 path_len: path_len as u8,
158 next_dense_idx: 0,
159 });
160 trace!(
161 target: TRACE_TARGET,
162 ?idx,
163 path = ?self.path.slice_unchecked(0, path_len),
164 "Pushed stack entry",
165 );
166 }
167
168 #[instrument(level = "trace", target = TRACE_TARGET, skip(self, arena))]
174 pub(super) fn pop(&mut self, arena: &mut NodeArena) -> ArenaCursorStackEntry {
175 let entry = self.stack.pop().expect("pop can't be called on empty stack");
176 self.needs_pop = false;
177 trace!(
178 target: TRACE_TARGET,
179 idx = ?entry.index,
180 path = ?self.entry_path(&entry),
181 "Popped stack entry",
182 );
183 let node = &arena[entry.index];
184
185 #[cfg(debug_assertions)]
186 if let ArenaSparseNode::Subtrie(s) = node {
187 let entry_path = self.path.slice_unchecked(0, entry.path_len as usize);
188 debug_assert_eq!(
189 s.path, entry_path,
190 "subtrie cached path {:?} does not match stack entry path {:?}",
191 s.path, entry_path,
192 );
193 }
194
195 if let Some(parent) = self.stack.last() {
196 let child_is_dirty = match node {
197 ArenaSparseNode::Branch(b) => matches!(b.state, ArenaSparseNodeState::Dirty),
198 ArenaSparseNode::Extension { child, .. } => {
199 matches!(arena[*child].state_ref(), Some(ArenaSparseNodeState::Dirty))
200 }
201 ArenaSparseNode::Leaf { state, .. } => matches!(state, ArenaSparseNodeState::Dirty),
202 ArenaSparseNode::Subtrie(_) => {
203 matches!(node.state_ref(), Some(ArenaSparseNodeState::Dirty))
204 }
205 _ => false,
206 };
207 if child_is_dirty {
208 let parent_branch_idx =
209 branch_idx(arena, parent.index).expect("parent must be a branch or extension");
210 *arena[parent_branch_idx].state_mut() = ArenaSparseNodeState::Dirty;
211 }
212 }
213
214 entry
215 }
216
217 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
220 pub(super) fn drain(&mut self, arena: &mut NodeArena) {
221 trace!(target: TRACE_TARGET, "Draining stack");
222 self.needs_pop = false;
223 while self.stack.len() > 1 {
224 self.pop(arena);
225 }
226 }
227
228 pub(super) fn head_logical_branch_path(&self, arena: &NodeArena) -> Nibbles {
231 self.logical_branch_path(arena, self.head_entry())
232 }
233
234 pub(super) fn head_logical_branch_path_len(&self, arena: &NodeArena) -> usize {
237 let head = self.head_entry();
238 head.path_len as usize + arena[head.index].extension_key().map_or(0, Nibbles::len)
239 }
240
241 pub(super) fn parent_logical_branch_path(&self, arena: &NodeArena) -> Nibbles {
244 self.logical_branch_path(arena, self.parent().expect("cursor must have a parent"))
245 }
246
247 pub(super) fn replace_head_index(
251 &mut self,
252 arena: &mut NodeArena,
253 root: &mut Index,
254 new_idx: Index,
255 ) {
256 let child_nibble = self.head_last_nibble();
257 let head = self.stack.last_mut().expect("cursor must have head");
258 let old_idx = head.index;
259 head.index = new_idx;
260
261 let Some(parent) = self.parent() else {
262 *root = new_idx;
263 return;
264 };
265
266 let child_nibble =
267 child_nibble.expect("if cursor has a parent then the head path can't be empty");
268
269 let parent_branch_idx =
270 branch_idx(arena, parent.index).expect("parent must be a branch or extension");
271 let parent_branch = arena[parent_branch_idx].branch_mut();
272 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
273 .expect("child nibble not found in parent state_mask");
274
275 debug_assert_eq!(
276 parent_branch.children[child_idx].revealed_index(),
277 Some(old_idx),
278 "parent child at nibble {child_nibble} does not match old_idx",
279 );
280
281 parent_branch.children[child_idx] = BranchChild::revealed(new_idx);
282 }
283
284 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
298 pub(super) fn next(
299 &mut self,
300 arena: &mut NodeArena,
301 should_descend: impl Fn(usize, &ArenaSparseNode) -> bool,
302 ) -> NextResult {
303 if self.needs_pop {
304 self.pop(arena);
305 }
306
307 loop {
308 let Some(head) = self.stack.last_mut() else {
309 return NextResult::Done;
310 };
311 let head_idx = head.index;
312 let head_path_len = head.path_len as usize;
313
314 let Some(branch_idx) = branch_idx(arena, head_idx) else {
315 self.needs_pop = true;
316 return NextResult::NonBranch;
317 };
318 let branch = arena[branch_idx].branch_ref();
319
320 let state_mask = branch.state_mask;
321 let short_key = arena[head_idx].extension_key().copied().unwrap_or_default();
322 let start = head.next_dense_idx as usize;
323 let child_depth = self.stack.len();
324
325 let mut descended = false;
326 for (branch_child_idx, nibble) in BranchChildIter::new(state_mask) {
327 if branch_child_idx.get() < start {
328 continue;
329 }
330
331 let Some(child_idx) =
332 arena[branch_idx].branch_ref().children[branch_child_idx].revealed_index()
333 else {
334 continue;
335 };
336
337 if should_descend(child_depth, &arena[child_idx]) {
338 self.stack.last_mut().expect("head exists").next_dense_idx =
340 branch_child_idx.get() as u8 + 1;
341 self.path.truncate(head_path_len);
342 self.path.extend(&short_key);
343 self.path.push_unchecked(nibble);
344 self.push(arena, child_idx, self.path.len());
345 descended = true;
346 break;
347 }
348 }
349
350 if !descended {
351 self.needs_pop = true;
352 return NextResult::Branch;
353 }
354 }
355 }
356
357 #[instrument(level = "trace", target = TRACE_TARGET, skip(self, arena), ret)]
363 pub(super) fn seek(&mut self, arena: &mut NodeArena, full_path: &Nibbles) -> SeekResult {
364 let common = self.path.common_prefix_length(full_path);
368 while self.stack.len() > 1 && self.head_entry().path_len as usize > common {
369 self.pop(arena);
370 }
371
372 if self.head_entry().path_len as usize > common {
373 debug_assert!(
377 false,
378 "seek target {full_path:?} is not below the walk's root {:?}",
379 self.head_path(),
380 );
381 return SeekResult::Diverged;
382 }
383 self.path = *full_path;
384
385 loop {
386 let head = self.head_entry();
387 let head_idx = head.index;
388 let head_path_len = head.path_len as usize;
389
390 let head_branch = match &arena[head_idx] {
391 ArenaSparseNode::EmptyRoot { .. } => {
392 return SeekResult::EmptyRoot;
393 }
394 ArenaSparseNode::Leaf { key, .. } => {
395 return if full_path.slice_unchecked(head_path_len, full_path.len()) == *key {
396 SeekResult::RevealedLeaf
397 } else {
398 SeekResult::Diverged
399 };
400 }
401 ArenaSparseNode::Branch(b) => b,
402 ArenaSparseNode::Extension { child, .. } => arena[*child].branch_ref(),
403 ArenaSparseNode::Subtrie(_) => {
404 return SeekResult::RevealedSubtrie;
405 }
406 _ => unreachable!("unexpected node type on stack: {:?}", arena[head_idx]),
407 };
408
409 let short_key = arena[head_idx].extension_key().copied().unwrap_or_default();
410 let logical_len = head_path_len + short_key.len();
411
412 if full_path.len() <= logical_len ||
415 (!short_key.is_empty() &&
416 full_path.slice_unchecked(head_path_len, logical_len) != short_key)
417 {
418 return SeekResult::Diverged;
419 }
420
421 let child_nibble = full_path.get_unchecked(logical_len);
422 let Some(branch_child_idx) = BranchChildIdx::new(head_branch.state_mask, child_nibble)
423 else {
424 return SeekResult::NoChild { child_nibble };
425 };
426
427 let Some(child_idx) = head_branch.children[branch_child_idx].revealed_index() else {
428 return SeekResult::Blinded;
429 };
430 self.push(arena, child_idx, logical_len + 1);
431 }
432 }
433
434 fn head_entry(&self) -> &ArenaCursorStackEntry {
436 self.stack.last().expect("cursor is non-empty")
437 }
438
439 fn entry_path(&self, entry: &ArenaCursorStackEntry) -> Nibbles {
441 self.path.slice_unchecked(0, entry.path_len as usize)
442 }
443
444 fn logical_branch_path(&self, arena: &NodeArena, entry: &ArenaCursorStackEntry) -> Nibbles {
446 let mut path = self.entry_path(entry);
447 if let Some(key) = arena[entry.index].extension_key() {
448 path.extend(key);
449 }
450 path
451 }
452}