|  | 
| 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