1use super::ArenaSparseNode;
2use alloc::vec::Vec;
3use core::{
4 fmt,
5 ops::{Index as IndexOps, IndexMut},
6};
7use reth_trie_common::RlpNode;
8
9const WORD_BITS: usize = u64::BITS as usize;
11
12#[derive(Debug, Clone, Default)]
19pub(super) struct NodeArena {
20 nodes: Vec<ArenaSparseNode>,
21 free: Vec<Index>,
22 blinded: Vec<RlpNode>,
23 blinded_free: Vec<u32>,
24}
25
26impl NodeArena {
27 pub(super) const fn new() -> Self {
29 Self { nodes: Vec::new(), free: Vec::new(), blinded: Vec::new(), blinded_free: Vec::new() }
30 }
31
32 pub(super) fn with_capacity(capacity: usize) -> Self {
34 Self { nodes: Vec::with_capacity(capacity), ..Self::new() }
35 }
36
37 pub(super) fn insert(&mut self, node: ArenaSparseNode) -> Index {
39 if let Some(idx) = self.free.pop() {
40 self.nodes[idx.get()] = node;
41 return idx;
42 }
43 let idx = Index::new(self.nodes.len());
44 self.nodes.push(node);
45 idx
46 }
47
48 pub(super) fn drain_node(&mut self, idx: Index) -> ArenaSparseNode {
58 let node = core::mem::replace(&mut self.nodes[idx.get()], ArenaSparseNode::Free);
59 assert!(!matches!(node, ArenaSparseNode::Free), "drained a free arena slot");
60 node
61 }
62
63 pub(super) fn remove(&mut self, idx: Index) -> Option<ArenaSparseNode> {
65 let slot = self.nodes.get_mut(idx.get())?;
66 if matches!(slot, ArenaSparseNode::Free) {
67 return None;
68 }
69 let node = core::mem::replace(slot, ArenaSparseNode::Free);
70 self.free.push(idx);
71 Some(node)
72 }
73
74 pub(super) fn get(&self, idx: Index) -> Option<&ArenaSparseNode> {
76 match self.nodes.get(idx.get()) {
77 Some(ArenaSparseNode::Free) | None => None,
78 Some(node) => Some(node),
79 }
80 }
81
82 pub(super) fn contains_key(&self, idx: Index) -> bool {
85 self.get(idx).is_some()
86 }
87
88 pub(super) const fn len(&self) -> usize {
90 self.nodes.len() - self.free.len()
91 }
92
93 pub(super) fn iter(&self) -> impl Iterator<Item = (Index, &ArenaSparseNode)> + '_ {
95 self.nodes
96 .iter()
97 .enumerate()
98 .filter(|(_, node)| !matches!(node, ArenaSparseNode::Free))
99 .map(|(idx, node)| (Index(idx as u32), node))
100 }
101
102 pub(super) fn iter_mut(&mut self) -> impl Iterator<Item = (Index, &mut ArenaSparseNode)> + '_ {
104 self.nodes
105 .iter_mut()
106 .enumerate()
107 .filter(|(_, node)| !matches!(node, ArenaSparseNode::Free))
108 .map(|(idx, node)| (Index(idx as u32), node))
109 }
110
111 pub(super) fn insert_blinded(&mut self, rlp: RlpNode) -> BranchChild {
113 let slot = if let Some(slot) = self.blinded_free.pop() {
114 self.blinded[slot as usize] = rlp;
115 slot
116 } else {
117 let slot = self.blinded.len();
118 assert!(slot < BranchChild::BLINDED as usize, "blinded slot overflows the tag bit");
119 self.blinded.push(rlp);
120 slot as u32
121 };
122 BranchChild::blinded(slot)
123 }
124
125 pub(super) fn blinded(&self, child: BranchChild) -> &RlpNode {
131 &self.blinded[child.blinded_slot().expect("child is revealed") as usize]
132 }
133
134 pub(super) fn take_blinded(&mut self, child: BranchChild) -> RlpNode {
140 let slot = child.blinded_slot().expect("child is revealed");
141 self.blinded_free.push(slot);
142 core::mem::take(&mut self.blinded[slot as usize])
143 }
144
145 pub(super) fn shrink_nodes_to_fit(&mut self) {
147 self.nodes.shrink_to_fit();
148 }
149
150 pub(super) fn adopt_blinded(&mut self, src: &mut Self) -> BlindedMarks {
157 debug_assert!(self.blinded.is_empty(), "adopting into a non-empty side table");
158 self.blinded = core::mem::take(&mut src.blinded);
159 self.blinded_free = core::mem::take(&mut src.blinded_free);
160 BlindedMarks { words: alloc::vec![0; self.blinded.len().div_ceil(WORD_BITS)] }
161 }
162
163 pub(super) fn sweep_blinded(&mut self, marks: BlindedMarks) {
167 let words = marks.words;
168 let end = words
169 .iter()
170 .rposition(|word| *word != 0)
171 .map_or(0, |idx| idx * WORD_BITS + WORD_BITS - words[idx].leading_zeros() as usize);
172 debug_assert!(end <= self.blinded.len(), "marked slot outside the side table");
173 self.blinded.truncate(end);
174 self.blinded_free.clear();
175
176 let words = &words[..end.div_ceil(WORD_BITS)];
177 let live_count = words.iter().map(|word| word.count_ones() as usize).sum::<usize>();
178 if end > 0 && live_count <= end / 4 {
179 let mut offsets = Vec::with_capacity(words.len());
182 let mut next = 0;
183 for (idx, &word) in words.iter().enumerate() {
184 offsets.push(next as u32);
185 let mut marked = word;
186 while marked != 0 {
187 let slot = idx * WORD_BITS + marked.trailing_zeros() as usize;
188 self.blinded.swap(next, slot);
189 next += 1;
190 marked &= marked - 1;
191 }
192 }
193 self.blinded.truncate(next);
194 for (_, node) in self.iter_mut() {
195 if let ArenaSparseNode::Branch(branch) = node {
196 for child in &mut branch.children {
197 if let Some(slot) = child.blinded_slot() {
198 let word = slot as usize / WORD_BITS;
199 let bit = slot as usize % WORD_BITS;
200 debug_assert_ne!(words[word] & (1 << bit), 0, "unmarked child");
201 let preceding = words[word] & ((1 << bit) - 1);
202 *child = BranchChild::blinded(offsets[word] + preceding.count_ones());
203 }
204 }
205 }
206 }
207 shrink_excess_capacity(&mut self.blinded);
208 shrink_excess_capacity(&mut self.blinded_free);
209 return;
210 }
211
212 let word_count = end.div_ceil(WORD_BITS);
215 for (idx, word) in words[..word_count].iter().enumerate().rev() {
216 let covered = if idx + 1 == word_count && end % WORD_BITS != 0 {
218 (1 << (end % WORD_BITS)) - 1
219 } else {
220 u64::MAX
221 };
222 let mut unmarked = !word & covered;
223 while unmarked != 0 {
224 let bit = u64::BITS - 1 - unmarked.leading_zeros();
225 unmarked ^= 1 << bit;
226 self.blinded_free.push(idx as u32 * u64::BITS + bit);
227 }
228 }
229
230 shrink_excess_capacity(&mut self.blinded);
231 shrink_excess_capacity(&mut self.blinded_free);
232 }
233}
234
235impl IndexOps<Index> for NodeArena {
236 type Output = ArenaSparseNode;
237
238 #[inline]
239 fn index(&self, idx: Index) -> &Self::Output {
240 let node = &self.nodes[idx.get()];
241 assert!(!matches!(node, ArenaSparseNode::Free), "indexed a free arena slot");
242 node
243 }
244}
245
246impl IndexMut<Index> for NodeArena {
247 #[inline]
248 fn index_mut(&mut self, idx: Index) -> &mut Self::Output {
249 let node = &mut self.nodes[idx.get()];
250 assert!(!matches!(node, ArenaSparseNode::Free), "indexed a free arena slot");
251 node
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
257pub(super) struct Index(u32);
258
259impl Index {
260 pub(super) const fn new(index: usize) -> Self {
266 assert!(index < BranchChild::BLINDED as usize, "arena index overflows the blinded tag bit");
267 Self(index as u32)
268 }
269
270 pub(super) const fn get(self) -> usize {
272 self.0 as usize
273 }
274}
275
276#[must_use = "adopted blinded children must be marked and swept"]
280pub(super) struct BlindedMarks {
281 words: Vec<u64>,
282}
283
284impl BlindedMarks {
285 pub(super) fn mark(&mut self, child: BranchChild) {
291 let slot = child.blinded_slot().expect("child is revealed") as usize;
292 let idx = slot / WORD_BITS;
293 if idx >= self.words.len() {
294 self.words.resize(idx + 1, 0);
295 }
296 self.words[idx] |= 1 << (slot % WORD_BITS);
297 }
298}
299
300#[derive(Clone, Copy, PartialEq, Eq)]
306pub(super) struct BranchChild(u32);
307
308impl BranchChild {
309 const BLINDED: u32 = 1 << 31;
311
312 pub(super) const fn revealed(idx: Index) -> Self {
318 assert!(idx.0 & Self::BLINDED == 0, "arena index overflows the blinded tag bit");
319 Self(idx.0)
320 }
321
322 const fn blinded(slot: u32) -> Self {
328 assert!(slot & Self::BLINDED == 0, "blinded slot overflows the tag bit");
329 Self(slot | Self::BLINDED)
330 }
331
332 pub(super) const fn is_blinded(self) -> bool {
334 self.0 & Self::BLINDED != 0
335 }
336
337 pub(super) const fn revealed_index(self) -> Option<Index> {
339 if self.is_blinded() {
340 None
341 } else {
342 Some(Index(self.0))
343 }
344 }
345
346 const fn blinded_slot(self) -> Option<u32> {
348 if self.is_blinded() {
349 Some(self.0 & !Self::BLINDED)
350 } else {
351 None
352 }
353 }
354}
355
356impl fmt::Debug for BranchChild {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 match self.blinded_slot() {
359 Some(slot) => write!(f, "Blinded({slot})"),
360 None => write!(f, "Revealed({})", self.0),
361 }
362 }
363}
364
365fn shrink_excess_capacity<T>(vec: &mut Vec<T>) {
367 if vec.len() <= vec.capacity() / 4 {
370 vec.shrink_to(vec.len() * 2);
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use crate::arena::{ArenaSparseNodeBranch, ArenaSparseNodeState};
378 use alloy_primitives::B256;
379
380 #[test]
381 fn removed_slots_are_recycled() {
382 let mut arena = NodeArena::new();
383 let first = arena.insert(ArenaSparseNode::TakenSubtrie);
384 let second = arena.insert(ArenaSparseNode::TakenSubtrie);
385 assert_eq!(arena.len(), 2);
386
387 assert!(arena.remove(first).is_some());
388 assert!(arena.get(first).is_none());
389 assert!(!arena.contains_key(first));
390 assert!(arena.remove(first).is_none());
391 assert_eq!(arena.len(), 1);
392
393 let third = arena.insert(ArenaSparseNode::TakenSubtrie);
394 assert_eq!(third, first, "the freed slot is handed out again");
395 assert_ne!(third, second);
396 assert_eq!(arena.len(), 2);
397 assert_eq!(arena.iter().count(), 2);
398 }
399
400 #[test]
401 fn contains_key_excludes_free_slots() {
402 let mut arena = NodeArena::new();
403 let removed = arena.insert(ArenaSparseNode::TakenSubtrie);
404 let drained = arena.insert(ArenaSparseNode::TakenSubtrie);
405 assert!(arena.contains_key(removed));
406 assert!(arena.contains_key(drained));
407 assert!(!arena.contains_key(Index::new(2)));
408
409 arena.remove(removed);
410 arena.drain_node(drained);
411 assert!(!arena.contains_key(removed));
412 assert!(!arena.contains_key(drained));
413 }
414
415 #[test]
416 #[should_panic(expected = "indexed a free arena slot")]
417 fn indexing_a_free_slot_panics() {
418 let mut arena = NodeArena::new();
419 let idx = arena.insert(ArenaSparseNode::TakenSubtrie);
420 arena.remove(idx);
421 let _ = &arena[idx];
422 }
423
424 #[test]
425 #[should_panic(expected = "indexed a free arena slot")]
426 fn mutably_indexing_a_free_slot_panics() {
427 let mut arena = NodeArena::new();
428 let idx = arena.insert(ArenaSparseNode::TakenSubtrie);
429 arena.remove(idx);
430 arena[idx] = ArenaSparseNode::TakenSubtrie;
431 }
432
433 #[test]
434 #[should_panic(expected = "drained a free arena slot")]
435 fn draining_a_free_slot_panics() {
436 let mut arena = NodeArena::new();
437 let idx = arena.insert(ArenaSparseNode::TakenSubtrie);
438 arena.drain_node(idx);
439 arena.drain_node(idx);
440 }
441
442 #[test]
443 fn branch_child_preserves_largest_index() {
444 let index = Index::new((1 << 31) - 1);
445 assert_eq!(BranchChild::revealed(index).revealed_index(), Some(index));
446 assert_eq!(BranchChild::blinded(index.0).blinded_slot(), Some(index.0));
447 }
448
449 #[test]
450 #[should_panic(expected = "arena index overflows the blinded tag bit")]
451 fn arena_index_rejects_blinded_tag_bit() {
452 Index::new(1 << 31);
453 }
454
455 #[test]
456 fn blinded_slots_are_recycled() {
457 let mut arena = NodeArena::new();
458 let rlp = RlpNode::word_rlp(&B256::repeat_byte(1));
459 let child = arena.insert_blinded(rlp.clone());
460 assert!(child.is_blinded());
461 assert_eq!(child.revealed_index(), None);
462 assert_eq!(arena.blinded(child), &rlp);
463
464 assert_eq!(arena.take_blinded(child), rlp);
465 let other = RlpNode::word_rlp(&B256::repeat_byte(2));
466 assert_eq!(arena.insert_blinded(other.clone()), child, "the freed slot is reused");
467 assert_eq!(arena.blinded(child), &other);
468 }
469
470 #[test]
471 fn sweeping_an_adopted_table_keeps_marked_slots() {
472 let mut src = NodeArena::new();
473 let children: Vec<_> = (0..200u8)
475 .map(|byte| src.insert_blinded(RlpNode::word_rlp(&B256::repeat_byte(byte))))
476 .collect();
477
478 let mut dst = NodeArena::new();
479 let mut marks = dst.adopt_blinded(&mut src);
480 for child in children.iter().step_by(3) {
481 marks.mark(*child);
482 }
483 dst.sweep_blinded(marks);
484
485 let last_marked = (children.len() - 1) / 3 * 3;
486 assert_eq!(dst.blinded.len(), last_marked + 1, "the unmarked tail is dropped");
487
488 let expected: Vec<u32> = (0..=last_marked as u32).filter(|slot| slot % 3 != 0).collect();
490 let handed_out: Vec<u32> = expected
491 .iter()
492 .map(|_| dst.insert_blinded(RlpNode::default()).blinded_slot().unwrap())
493 .collect();
494 assert_eq!(handed_out, expected);
495
496 for (byte, child) in (0..200u8).step_by(3).zip(children.iter().step_by(3)) {
497 assert_eq!(dst.blinded(*child), &RlpNode::word_rlp(&B256::repeat_byte(byte)));
498 }
499 }
500
501 #[test]
502 fn sweeping_releases_excess_side_table_capacity() {
503 let mut src = NodeArena::new();
504 let rlp = RlpNode::word_rlp(&B256::repeat_byte(1));
505 let children: Vec<_> = (0..4096).map(|_| src.insert_blinded(rlp.clone())).collect();
506 for child in &children[2048..] {
507 src.take_blinded(*child);
508 }
509
510 let mut dst = NodeArena::new();
511 let mut marks = dst.adopt_blinded(&mut src);
512 for child in children[..64].iter().step_by(2) {
513 marks.mark(*child);
514 }
515 dst.sweep_blinded(marks);
516
517 assert!(dst.blinded.capacity() <= 128, "release the discarded RLP capacity");
518 assert!(dst.blinded_free.capacity() <= 64, "release the discarded free-list capacity");
519 for child in children[..64].iter().step_by(2) {
520 assert_eq!(dst.blinded(*child), &rlp);
521 }
522 for slot in (1..63).step_by(2) {
523 assert_eq!(dst.insert_blinded(rlp.clone()).blinded_slot(), Some(slot));
524 }
525
526 let mut empty = NodeArena::new();
527 let marks = empty.adopt_blinded(&mut dst);
528 empty.sweep_blinded(marks);
529 assert_eq!(empty.blinded.capacity(), 0);
530 assert_eq!(empty.blinded_free.capacity(), 0);
531 }
532
533 #[test]
534 fn sweeping_keeps_capacity_for_small_contractions() {
535 let mut src = NodeArena::new();
536 let children: Vec<_> = (0..128).map(|_| src.insert_blinded(RlpNode::default())).collect();
537 let capacity = src.blinded.capacity();
538 let mut dst = NodeArena::new();
539 let mut marks = dst.adopt_blinded(&mut src);
540 for child in &children[..96] {
541 marks.mark(*child);
542 }
543 dst.sweep_blinded(marks);
544 assert_eq!(dst.blinded.capacity(), capacity);
545 }
546
547 #[test]
548 fn sweeping_compacts_a_live_replacement_at_the_tail() {
549 let mut src = NodeArena::new();
550 for _ in 0..65_536 {
551 src.insert_blinded(RlpNode::default());
552 }
553 let mut dst = NodeArena::new();
554 let mut marks = dst.adopt_blinded(&mut src);
555 let rlp = RlpNode::word_rlp(&B256::repeat_byte(1));
556 let replacement = dst.insert_blinded(rlp.clone());
557 assert_eq!(replacement.blinded_slot(), Some(65_536));
558 let root = dst.insert(branch([replacement]));
559 marks.mark(replacement);
560 dst.sweep_blinded(marks);
561
562 let child = dst[root].branch_ref().children[0];
563 assert_eq!(dst.blinded(child), &rlp);
564 assert_eq!(child.blinded_slot(), Some(0));
565 assert_eq!(dst.blinded.len(), 1);
566 assert!(dst.blinded.capacity() <= 2);
567 assert_eq!(dst.blinded_free.capacity(), 0);
568
569 let mut next = NodeArena::new();
571 let mut marks = next.adopt_blinded(&mut dst);
572 assert_eq!(marks.words.len(), 1);
573 marks.mark(child);
574 next.insert(dst.drain_node(root));
575 next.sweep_blinded(marks);
576 assert_eq!(next.blinded(child), &rlp);
577 assert_eq!(next.blinded.len(), 1);
578 }
579
580 #[test]
581 fn sweeping_rewrites_sparse_slots_across_bitmap_words() {
582 let mut src = NodeArena::new();
583 let children: Vec<_> = (0..200u8)
584 .map(|byte| src.insert_blinded(RlpNode::word_rlp(&B256::repeat_byte(byte))))
585 .collect();
586 let mut dst = NodeArena::new();
587 let mut marks = dst.adopt_blinded(&mut src);
588 let kept = [199, 64, 0, 130, 63];
589 for slot in kept {
590 marks.mark(children[slot]);
591 }
592 let leaf = dst.insert(ArenaSparseNode::TakenSubtrie);
593 let root = dst.insert(branch(
594 kept.map(|slot| children[slot]).into_iter().chain([BranchChild::revealed(leaf)]),
595 ));
596 dst.sweep_blinded(marks);
597
598 assert_eq!(dst.blinded.len(), kept.len());
599 assert!(dst.blinded.capacity() <= kept.len() * 2);
600 assert_eq!(dst.blinded_free.capacity(), 0);
601 let branch = dst[root].branch_ref();
602 for (pos, slot) in kept.into_iter().enumerate() {
603 assert_eq!(
604 dst.blinded(branch.children[pos]),
605 &RlpNode::word_rlp(&B256::repeat_byte(slot as u8))
606 );
607 }
608 assert_eq!(branch.children[kept.len()].revealed_index(), Some(leaf));
609 assert!(dst.contains_key(leaf));
610 assert_eq!(dst.insert_blinded(RlpNode::default()).blinded_slot(), Some(5));
611 }
612
613 fn branch(children: impl IntoIterator<Item = BranchChild>) -> ArenaSparseNode {
614 let children: smallvec::SmallVec<_> = children.into_iter().collect();
615 ArenaSparseNode::Branch(ArenaSparseNodeBranch {
616 state_mask: alloy_trie::TrieMask::new((1 << children.len()) - 1),
617 children,
618 state: ArenaSparseNodeState::Revealed,
619 short_key: Default::default(),
620 branch_masks: Default::default(),
621 })
622 }
623}