firewood_storage/path/mod.rs
1// Copyright (C) 2025, Ava Labs, Inc. All rights reserved.
2// See the file LICENSE.md for licensing terms.
3
4mod buf;
5mod component;
6mod joined;
7mod packed;
8mod split;
9
10pub use self::buf::{PartialPath, PathBuf, PathGuard};
11pub use self::component::{ComponentIter, PathComponent, PathComponentSliceExt};
12pub use self::joined::JoinedPath;
13pub use self::packed::{PackedBytes, PackedPathComponents, PackedPathRef};
14pub use self::split::{IntoSplitPath, PathCommonPrefix, SplitPath};
15
16/// A trie path of components with different underlying representations.
17///
18/// The underlying representation does not need to be a contiguous array of
19/// [`PathComponent`], but it must be possible to iterate over them in order
20/// as well as have a known length.
21pub trait TriePath {
22 /// The iterator returned by [`TriePath::components`].
23 type Components<'a>: Iterator<Item = PathComponent> + Clone + 'a
24 where
25 Self: 'a;
26
27 /// The length, in path components, of this path.
28 fn len(&self) -> usize;
29
30 /// Returns true if this path is empty (i.e. has length 0).
31 fn is_empty(&self) -> bool {
32 self.len() == 0
33 }
34
35 /// Returns an iterator over the components of this path.
36 fn components(&self) -> Self::Components<'_>;
37
38 /// Returns a contiguous view of this path's components.
39 ///
40 /// If the underlying representation is already contiguous, this should be a
41 /// cheap operation (i.e. no allocations or copies). If not, this may allocate
42 /// and copy the components into a contiguous buffer.
43 fn as_component_slice(&self) -> PartialPath<'_>;
44
45 /// Appends the provided path segment to this path, returning a new joined
46 /// path that represents the concatenation of the two paths.
47 ///
48 /// The returned path is a view over the two input paths and does not
49 /// allocate. The input paths are consumed and ownership is taken.
50 fn append<S>(self, suffix: S) -> JoinedPath<Self, S>
51 where
52 Self: Sized,
53 S: TriePath,
54 {
55 JoinedPath::new(self, suffix)
56 }
57
58 /// Prepends the provided path segment to this path, returning a new joined
59 /// path that represents the concatenation of the two paths.
60 ///
61 /// The inverse of [`TriePath::append`].
62 ///
63 /// The returned path is a view over the two input paths and does not
64 /// allocate. The input paths are consumed and ownership is taken.
65 fn prepend<P>(self, prefix: P) -> JoinedPath<P, Self>
66 where
67 Self: Sized,
68 P: TriePath,
69 {
70 prefix.append(self)
71 }
72
73 /// Compares this path against another path for equality using path component
74 /// equality.
75 ///
76 /// This is analogous to [`Iterator::eq`] and is different than [`PartialEq`]
77 /// which may have different semantics depending on the underlying type and
78 /// representation as well as may not be implemented for the cross-type
79 /// comparisons.
80 fn path_eq<T: TriePath + ?Sized>(&self, other: &T) -> bool {
81 self.len() == other.len() && self.components().eq(other.components())
82 }
83
84 /// Compares this path against another path using path-component lexicographic
85 /// ordering. Strict prefixes are less than their longer counterparts.
86 ///
87 /// This is analogous to [`Iterator::cmp`] and is different than [`Ord`]
88 /// which may have different semantics depending on the underlying type and
89 /// representation as well as may not be implemented for the cross-type
90 /// comparisons.
91 fn path_cmp<T: TriePath + ?Sized>(&self, other: &T) -> std::cmp::Ordering {
92 self.components().cmp(other.components())
93 }
94
95 /// Returns a wrapper type that implements [`std::fmt::Display`] and
96 /// [`std::fmt::Debug`] for this path.
97 fn display(&self) -> DisplayPath<'_, Self> {
98 DisplayPath { path: self }
99 }
100}
101
102/// Constructor for a trie path from a set of unpacked bytes; where each byte
103/// is a whole path component regardless of the normal width of a path component.
104///
105/// For 256-ary tries, this is the bytes as-is.
106///
107/// For hexary tries, each byte must occupy only the lower 4 bits. Any byte with
108/// a bit set in the upper 4 bits will result in an error.
109pub trait TriePathFromUnpackedBytes<'input>: TriePath + Sized {
110 /// The error type returned if the bytes are invalid.
111 type Error;
112
113 /// Constructs a path from the given unpacked bytes.
114 ///
115 /// For hexary tries, each byte must be in the range 0x00 to 0x0F inclusive.
116 /// Any byte outside this range will result in an error.
117 ///
118 /// # Errors
119 ///
120 /// - The input is invalid.
121 fn path_from_unpacked_bytes(bytes: &'input [u8]) -> Result<Self, Self::Error>;
122}
123
124/// Constructor for a trie path from a set of packed bytes; where each byte contains
125/// as many path components as possible.
126///
127/// For 256-ary tries, this is just the bytes as-is.
128///
129/// For hexary tries, each byte contains two path components; one in the upper 4
130/// bits and one in the lower 4 bits, in big-endian order. The resulting path
131/// will always have an even length (`bytes.len() * 2`).
132///
133/// For future compatibility, this trait only supports paths where the width of
134/// a path component is a factor of 8 (i.e. 1, 2, 4, or 8 bits).
135pub trait TriePathFromPackedBytes<'input>: Sized {
136 /// Constructs a path from the given packed bytes.
137 fn path_from_packed_bytes(bytes: &'input [u8]) -> Self;
138}
139
140/// Converts this path to an iterator over its packed bytes.
141pub trait TriePathAsPackedBytes {
142 /// The iterator type returned by [`TriePathAsPackedBytes::as_packed_bytes`].
143 type PackedBytesIter<'a>: Iterator<Item = u8>
144 where
145 Self: 'a;
146
147 /// Returns an iterator over the packed bytes of this path.
148 ///
149 /// If the final path component does not fill a whole byte, it is padded with zero.
150 fn as_packed_bytes(&self) -> Self::PackedBytesIter<'_>;
151}
152
153#[inline]
154fn display_path(
155 f: &mut std::fmt::Formatter<'_>,
156 mut comp: impl Iterator<Item = PathComponent>,
157) -> std::fmt::Result {
158 comp.try_for_each(|c| write!(f, "{c}"))
159}
160
161/// A wrapper type that implements [`Display`](std::fmt::Display) and
162/// [`Debug`](std::fmt::Debug) for any type that implements [`TriePath`].
163pub struct DisplayPath<'a, P: TriePath + ?Sized> {
164 path: &'a P,
165}
166
167impl<P: TriePath + ?Sized> std::fmt::Debug for DisplayPath<'_, P> {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 display_path(f, self.path.components())
170 }
171}
172
173impl<P: TriePath + ?Sized> std::fmt::Display for DisplayPath<'_, P> {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 display_path(f, self.path.components())
176 }
177}
178
179impl<T: TriePath + ?Sized> TriePath for &T {
180 type Components<'a>
181 = T::Components<'a>
182 where
183 Self: 'a;
184
185 fn len(&self) -> usize {
186 (**self).len()
187 }
188
189 fn components(&self) -> Self::Components<'_> {
190 (**self).components()
191 }
192
193 fn as_component_slice(&self) -> PartialPath<'_> {
194 (**self).as_component_slice()
195 }
196}
197
198impl<T: TriePath + ?Sized> TriePath for &mut T {
199 type Components<'a>
200 = T::Components<'a>
201 where
202 Self: 'a;
203
204 fn len(&self) -> usize {
205 (**self).len()
206 }
207
208 fn components(&self) -> Self::Components<'_> {
209 (**self).components()
210 }
211
212 fn as_component_slice(&self) -> PartialPath<'_> {
213 (**self).as_component_slice()
214 }
215}
216
217impl<T: TriePath + ?Sized> TriePath for Box<T> {
218 type Components<'a>
219 = T::Components<'a>
220 where
221 Self: 'a;
222
223 fn len(&self) -> usize {
224 (**self).len()
225 }
226
227 fn components(&self) -> Self::Components<'_> {
228 (**self).components()
229 }
230
231 fn as_component_slice(&self) -> PartialPath<'_> {
232 (**self).as_component_slice()
233 }
234}
235
236impl<T: TriePath + ?Sized> TriePath for std::rc::Rc<T> {
237 type Components<'a>
238 = T::Components<'a>
239 where
240 Self: 'a;
241
242 fn len(&self) -> usize {
243 (**self).len()
244 }
245
246 fn components(&self) -> Self::Components<'_> {
247 (**self).components()
248 }
249
250 fn as_component_slice(&self) -> PartialPath<'_> {
251 (**self).as_component_slice()
252 }
253}
254
255impl<T: TriePath + ?Sized> TriePath for std::sync::Arc<T> {
256 type Components<'a>
257 = T::Components<'a>
258 where
259 Self: 'a;
260
261 fn len(&self) -> usize {
262 (**self).len()
263 }
264
265 fn components(&self) -> Self::Components<'_> {
266 (**self).components()
267 }
268
269 fn as_component_slice(&self) -> PartialPath<'_> {
270 (**self).as_component_slice()
271 }
272}