reth_trie_sparse_parallel/lower.rs
1use crate::SparseSubtrie;
2use reth_trie_common::Nibbles;
3
4/// Tracks the state of the lower subtries.
5///
6/// When a [`crate::ParallelSparseTrie`] is initialized/cleared then its `LowerSparseSubtrie`s are
7/// all blinded, meaning they have no nodes. A blinded `LowerSparseSubtrie` may hold onto a cleared
8/// [`SparseSubtrie`] in order to reuse allocations.
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub(crate) enum LowerSparseSubtrie {
11 Blind(Option<Box<SparseSubtrie>>),
12 Revealed(Box<SparseSubtrie>),
13}
14
15impl Default for LowerSparseSubtrie {
16 /// Creates a new blinded subtrie with no allocated storage.
17 fn default() -> Self {
18 Self::Blind(None)
19 }
20}
21
22impl LowerSparseSubtrie {
23 /// Returns a reference to the underlying [`SparseSubtrie`] if this subtrie is revealed.
24 ///
25 /// Returns `None` if the subtrie is blinded (has no nodes).
26 pub(crate) fn as_revealed_ref(&self) -> Option<&SparseSubtrie> {
27 match self {
28 Self::Blind(_) => None,
29 Self::Revealed(subtrie) => Some(subtrie.as_ref()),
30 }
31 }
32
33 /// Returns a mutable reference to the underlying [`SparseSubtrie`] if this subtrie is revealed.
34 ///
35 /// Returns `None` if the subtrie is blinded (has no nodes).
36 pub(crate) fn as_revealed_mut(&mut self) -> Option<&mut SparseSubtrie> {
37 match self {
38 Self::Blind(_) => None,
39 Self::Revealed(subtrie) => Some(subtrie.as_mut()),
40 }
41 }
42
43 /// Reveals the lower [`SparseSubtrie`], transitioning it from the Blinded to the Revealed
44 /// variant, preserving allocations if possible.
45 ///
46 /// The given path is the path of a node which will be set into the [`SparseSubtrie`]'s `nodes`
47 /// map immediately upon being revealed. If the subtrie is blinded, or if its current root path
48 /// is longer than this one, than this one becomes the new root path of the subtrie.
49 pub(crate) fn reveal(&mut self, path: &Nibbles) {
50 match self {
51 Self::Blind(allocated) => {
52 debug_assert!(allocated.as_ref().is_none_or(|subtrie| subtrie.is_empty()));
53 *self = if let Some(mut subtrie) = allocated.take() {
54 subtrie.path = *path;
55 Self::Revealed(subtrie)
56 } else {
57 Self::Revealed(Box::new(SparseSubtrie::new(*path)))
58 }
59 }
60 Self::Revealed(subtrie) => {
61 if path.len() < subtrie.path.len() {
62 subtrie.path = *path;
63 }
64 }
65 };
66 }
67
68 /// Clears the subtrie and transitions it to the blinded state, preserving a cleared
69 /// [`SparseSubtrie`] if possible.
70 pub(crate) fn clear(&mut self) {
71 *self = match core::mem::take(self) {
72 Self::Blind(allocated) => {
73 debug_assert!(allocated.as_ref().is_none_or(|subtrie| subtrie.is_empty()));
74 Self::Blind(allocated)
75 }
76 Self::Revealed(mut subtrie) => {
77 subtrie.clear();
78 Self::Blind(Some(subtrie))
79 }
80 }
81 }
82
83 /// Takes ownership of the underlying [`SparseSubtrie`] if revealed, putting this
84 /// `LowerSparseSubtrie` will be put into the blinded state.
85 ///
86 /// Otherwise returns None.
87 pub(crate) fn take_revealed(&mut self) -> Option<Box<SparseSubtrie>> {
88 self.take_revealed_if(|_| true)
89 }
90
91 /// Takes ownership of the underlying [`SparseSubtrie`] if revealed and the predicate returns
92 /// true.
93 ///
94 /// If the subtrie is revealed, and the predicate function returns `true` when called with it,
95 /// then this method will take ownership of the subtrie and transition this `LowerSparseSubtrie`
96 /// to the blinded state. Otherwise, returns `None`.
97 pub(crate) fn take_revealed_if<P>(&mut self, predicate: P) -> Option<Box<SparseSubtrie>>
98 where
99 P: FnOnce(&SparseSubtrie) -> bool,
100 {
101 match self {
102 Self::Revealed(subtrie) if predicate(subtrie) => {
103 let Self::Revealed(subtrie) = core::mem::take(self) else { unreachable!() };
104 Some(subtrie)
105 }
106 Self::Revealed(_) | Self::Blind(_) => None,
107 }
108 }
109}