|
1 | 1 | // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. |
2 | 2 | // See the file LICENSE.md for licensing terms. |
3 | 3 |
|
4 | | -use crate::{HashType, PathBuf, PathComponent, TrieNode, TriePath, tries::TrieEdgeState}; |
| 4 | +use crate::{ |
| 5 | + HashType, IntoSplitPath, PathBuf, PathComponent, SplitPath, TrieEdgeState, TrieNode, TriePath, |
| 6 | +}; |
5 | 7 |
|
6 | 8 | /// A marker type for [`TrieEdgeIter`] that indicates that the iterator traverses |
7 | 9 | /// the trie in ascending order. |
@@ -33,6 +35,18 @@ pub struct TrieValueIter<'a, N: ?Sized, V: ?Sized, D> { |
33 | 35 | edges: TrieEdgeIter<'a, N, V, D>, |
34 | 36 | } |
35 | 37 |
|
| 38 | +/// An iterator over the edges along a specified path in a key-value trie |
| 39 | +/// terminating at the node corresponding to the path, if it exists; otherwise, |
| 40 | +/// terminating at the deepest existing edge along the path. |
| 41 | +#[derive(Debug)] |
| 42 | +#[must_use = "iterators are lazy and do nothing unless consumed"] |
| 43 | +pub struct TriePathIter<'a, P, N: ?Sized, V: ?Sized> { |
| 44 | + needle: P, |
| 45 | + current_path: PathBuf, |
| 46 | + current_edge: Option<TrieEdgeState<'a, N>>, |
| 47 | + marker: std::marker::PhantomData<V>, |
| 48 | +} |
| 49 | + |
36 | 50 | #[derive(Debug)] |
37 | 51 | struct Frame<'a, N: ?Sized, V: ?Sized> { |
38 | 52 | node: &'a N, |
|
83 | 97 | } |
84 | 98 | } |
85 | 99 |
|
| 100 | +impl<'a, P, N, V> TriePathIter<'a, P, N, V> |
| 101 | +where |
| 102 | + P: SplitPath, |
| 103 | + N: TrieNode<V> + ?Sized, |
| 104 | + V: AsRef<[u8]> + ?Sized, |
| 105 | +{ |
| 106 | + /// Creates a new iterator over the edges along the given path in the |
| 107 | + /// specified key-value trie. |
| 108 | + pub const fn new(root: &'a N, root_hash: Option<&'a HashType>, path: P) -> Self { |
| 109 | + let mut this = Self { |
| 110 | + needle: path, |
| 111 | + current_path: PathBuf::new_const(), |
| 112 | + current_edge: None, |
| 113 | + marker: std::marker::PhantomData, |
| 114 | + }; |
| 115 | + this.current_edge = Some(TrieEdgeState::from_node(root, root_hash)); |
| 116 | + this |
| 117 | + } |
| 118 | +} |
| 119 | + |
86 | 120 | /// Both iterators share this logic to descend into a node's children. |
87 | 121 | /// |
88 | 122 | /// The passed in `children_iter` should be an iterator over the indices into |
@@ -218,6 +252,45 @@ where |
218 | 252 | } |
219 | 253 | } |
220 | 254 |
|
| 255 | +impl<'a, P, N, V> Iterator for TriePathIter<'a, P, N, V> |
| 256 | +where |
| 257 | + P: SplitPath, |
| 258 | + N: TrieNode<V> + ?Sized, |
| 259 | + V: AsRef<[u8]> + ?Sized, |
| 260 | +{ |
| 261 | + type Item = (PathBuf, TrieEdgeState<'a, N>); |
| 262 | + |
| 263 | + fn next(&mut self) -> Option<Self::Item> { |
| 264 | + // qualified path to `Option::take` because rust-analyzer thinks |
| 265 | + // `self.current_edge` is an `Iterator` and displays an error for |
| 266 | + // `self.current_edge.take()` where `rustc` does not |
| 267 | + let edge = Option::take(&mut self.current_edge)?; |
| 268 | + let path = self.current_path.clone(); |
| 269 | + |
| 270 | + let Some(node) = edge.node() else { |
| 271 | + // the current edge is remote, so we cannot descend further |
| 272 | + return Some((path, edge)); |
| 273 | + }; |
| 274 | + |
| 275 | + self.current_path.extend(node.partial_path().components()); |
| 276 | + |
| 277 | + if let (None, Some((pc, needle_suffix)), _) = node |
| 278 | + .partial_path() |
| 279 | + .into_split_path() |
| 280 | + .longest_common_prefix(self.needle) |
| 281 | + .split_first_parts() |
| 282 | + { |
| 283 | + // the target path continues beyond the current node, descend |
| 284 | + // into it if there's a child along the path |
| 285 | + self.current_path.push(pc); |
| 286 | + self.current_edge = node.child_state(pc); |
| 287 | + self.needle = needle_suffix; |
| 288 | + } |
| 289 | + |
| 290 | + Some((path, edge)) |
| 291 | + } |
| 292 | +} |
| 293 | + |
221 | 294 | // auto-derived implementations would require N: Clone, V: Clone which is too much |
222 | 295 |
|
223 | 296 | impl<N: ?Sized, V: ?Sized, D> Clone for TrieEdgeIter<'_, N, V, D> { |
@@ -247,6 +320,23 @@ impl<N: ?Sized, V: ?Sized, D> Clone for TrieValueIter<'_, N, V, D> { |
247 | 320 | } |
248 | 321 | } |
249 | 322 |
|
| 323 | +impl<P: SplitPath, N: ?Sized, V: ?Sized> Clone for TriePathIter<'_, P, N, V> { |
| 324 | + fn clone(&self) -> Self { |
| 325 | + Self { |
| 326 | + needle: self.needle, |
| 327 | + current_path: self.current_path.clone(), |
| 328 | + current_edge: self.current_edge, |
| 329 | + marker: std::marker::PhantomData, |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + fn clone_from(&mut self, source: &Self) { |
| 334 | + self.needle = source.needle; |
| 335 | + self.current_path.clone_from(&source.current_path); |
| 336 | + self.current_edge = source.current_edge; |
| 337 | + } |
| 338 | +} |
| 339 | + |
250 | 340 | impl<N: ?Sized, V: ?Sized> Clone for Frame<'_, N, V> { |
251 | 341 | fn clone(&self) -> Self { |
252 | 342 | Self { |
|
0 commit comments