willow_data_model/paths/
component.rs

1use core::fmt::{Debug, Write};
2use core::{borrow::Borrow, fmt, ops::Deref};
3
4use bytes::Bytes;
5
6/// A slice of a [Path Component](https://willowprotocol.org/specs/data-model/index.html#Component).
7///
8/// This type statically enforces a [**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length) of `MCL`. Otherwise, it is basically a regular `[u8]`.
9///
10/// This is an *unsized* type, meaning that it must always be used behind a pointer like `&` or [`Box`].
11#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct Component<const MCL: usize>([u8]);
14
15impl<const MCL: usize> Component<MCL> {
16    /// Creates a reference to a [`Component`] from a reference to a byte slice. Returns [`None`] if the slice is longer than `MCL`.
17    ///
18    /// Aside from checking the length, this is a cost-free conversion.
19    ///
20    /// #### Examples
21    ///
22    /// ```
23    /// use willow_data_model::prelude::*;
24    /// assert!(Component::<3>::new(b"yay").is_ok());
25    /// assert!(Component::<3>::new(b"too_long").is_err());
26    /// ```
27    pub fn new(s: &[u8]) -> Result<&Self, InvalidComponentError> {
28        if s.len() <= MCL {
29            Ok(unsafe { Self::new_unchecked(s) })
30        } else {
31            Err(InvalidComponentError)
32        }
33    }
34
35    /// Creates a reference to a [`Component`] from a reference to a byte slice, without verifying its length.
36    ///
37    /// This is a cost-free conversion.
38    ///
39    /// #### Safety
40    ///
41    /// Supplying a slice of length strictly greater than `MCL` may trigger undefined behavior,
42    /// either immediately, or on any subsequent function invocation that operates on the resulting [`Component`].
43    ///
44    /// #### Examples
45    ///
46    /// ```
47    /// use willow_data_model::prelude::*;
48    /// let unchecked_component = unsafe { Component::<3>::new_unchecked(b"yay") };
49    /// assert_eq!(unchecked_component.as_ref(), b"yay");
50    /// ```
51    pub unsafe fn new_unchecked(s: &[u8]) -> &Self {
52        debug_assert!(s.len() <= MCL);
53        unsafe { &*(s as *const [u8] as *const Self) }
54    }
55
56    /// Creates a mutable reference to a [`Component`] from a mutable reference to a byte slice. Returns [`None`] if the slice is longer than `MCL`.
57    ///
58    /// Aside from checking the length, this is a cost-free conversion.
59    ///
60    /// #### Examples
61    ///
62    /// ```
63    /// use willow_data_model::prelude::*;
64    /// assert!(Component::<3>::new_mut(&mut [b'y', b'a', b'y']).is_ok());
65    /// assert!(Component::<3>::new_mut(&mut [b'n', b'o', b'p', b'e']).is_err());
66    /// ```
67    pub fn new_mut(s: &mut [u8]) -> Result<&mut Self, InvalidComponentError> {
68        if s.len() <= MCL {
69            Ok(unsafe { Self::new_mut_unchecked(s) })
70        } else {
71            Err(InvalidComponentError)
72        }
73    }
74
75    /// Creates a mutable reference to a [`Component`] from a mutable reference to a byte slice, without verifying its length.
76    ///
77    /// This is a cost-free conversion.
78    ///
79    /// #### Safety
80    ///
81    /// Supplying a slice of length strictly greater than `MCL` may trigger undefined behavior,
82    /// either immediately, or on any subsequent function invocation that operates on the resulting [`Component`].
83    ///
84    /// #### Examples
85    ///
86    /// ```
87    /// use willow_data_model::prelude::*;
88    /// let mut buf = [b'y', b'a', b'y'];
89    /// let unchecked_component = unsafe { Component::<3>::new_mut_unchecked(&mut buf[..]) };
90    /// assert_eq!(unchecked_component.as_ref(), &mut [b'y', b'a', b'y']);
91    /// ```
92    pub unsafe fn new_mut_unchecked(s: &mut [u8]) -> &mut Self {
93        debug_assert!(s.len() <= MCL);
94        unsafe { &mut *(s as *mut [u8] as *mut Self) }
95    }
96
97    /// Creates a `&'static` reference to the empty component.
98    pub fn new_empty() -> &'static Self {
99        unsafe { Self::new_unchecked(&[]) }
100    }
101
102    /// Returns the raw bytes of the component.
103    ///
104    /// ```
105    /// use willow_data_model::prelude::*;
106    /// assert_eq!(Component::<3>::new(b"yay").unwrap().as_bytes(), b"yay");
107    /// ```
108    pub fn as_bytes(&self) -> &[u8] {
109        &self.0
110    }
111
112    /// Returns a mutable reference to the raw bytes of the component.
113    ///
114    /// ```
115    /// use willow_data_model::prelude::*;
116    /// assert_eq!(
117    ///     Component::<3>::new_mut(&mut [b'y', b'a', b'y']).unwrap().as_bytes_mut(),
118    ///     &mut [b'y', b'a', b'y'],
119    /// );
120    /// ```
121    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
122        &mut self.0
123    }
124}
125
126impl<const MCL: usize> Deref for Component<MCL> {
127    type Target = [u8];
128
129    fn deref(&self) -> &Self::Target {
130        &self.0
131    }
132}
133
134impl<const MCL: usize> AsRef<[u8]> for Component<MCL> {
135    fn as_ref(&self) -> &[u8] {
136        &self.0
137    }
138}
139
140impl<const MCL: usize> Borrow<[u8]> for Component<MCL> {
141    fn borrow(&self) -> &[u8] {
142        &self.0
143    }
144}
145
146impl<const MCL: usize> fmt::Debug for Component<MCL> {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        f.debug_tuple("Component")
149            .field(&ComponentFmtHelper::new(&self, true))
150            .finish()
151    }
152}
153
154impl<const MCL: usize> fmt::Display for Component<MCL> {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        ComponentFmtHelper::new(&self, true).fmt(f)
157    }
158}
159
160/// An owned [component](https://willowprotocol.org/specs/data-model/index.html#Component) of a Willow [Path](https://willowprotocol.org/specs/data-model/index.html#Path), using reference counting for cheap cloning. Typically obtained from a [`Path`](super::Path) instead of being created independently.
161///
162/// This type enforces a const-generic [maximum component length](https://willowprotocol.org/specs/data-model/index.html#max_component_length). Use the [`AsRef`], [`Deref`], or [`Borrow`] implementation to access wrapped the immutable byte slice.
163#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
164pub struct OwnedComponent<const MCL: usize>(pub(crate) Bytes);
165
166impl<const MCL: usize> OwnedComponent<MCL> {
167    /// Creates an [`OwnedComponent`] by copying data from a byte slice. Returns [`None`] if the slice is longer than `MCL`.
168    ///
169    /// #### Complexity
170    ///
171    /// Runs in `O(n)`, where `n` is the length of the slice. Performs a single allocation of `O(n)` bytes.
172    ///
173    /// #### Examples
174    ///
175    /// ```
176    /// use willow_data_model::prelude::*;
177    /// assert!(OwnedComponent::<3>::new(b"yay").is_ok());
178    /// assert!(OwnedComponent::<3>::new(b"too_long").is_err());
179    /// ```
180    pub fn new(data: &[u8]) -> Result<Self, InvalidComponentError> {
181        if data.len() <= MCL {
182            Ok(unsafe { Self::new_unchecked(data) }) // Safe because we just checked the length.
183        } else {
184            Err(InvalidComponentError)
185        }
186    }
187
188    /// Creates an [`OwnedComponent`] by copying data from a byte slice, without verifying its length.
189    ///
190    /// #### Safety
191    ///
192    /// Supplying a slice of length strictly greater than `MCL` may trigger undefined behavior,
193    /// either immediately, or on any subsequent function invocation that operates on the resulting [`OwnedComponent`].
194    ///
195    /// #### Complexity
196    ///
197    /// Runs in `O(n)`, where `n` is the length of the slice. Performs a single allocation of `O(n)` bytes.
198    ///
199    /// #### Examples
200    ///
201    /// ```
202    /// use willow_data_model::prelude::*;
203    /// let unchecked_component = unsafe { OwnedComponent::<3>::new_unchecked(b"yay") };
204    /// assert_eq!(unchecked_component.as_ref(), b"yay");
205    /// ```
206    pub unsafe fn new_unchecked(data: &[u8]) -> Self {
207        debug_assert!(data.len() <= MCL);
208        Self(Bytes::copy_from_slice(data))
209    }
210
211    /// Returns an empty [`OwnedComponent`].
212    ///
213    /// #### Complexity
214    ///
215    /// Runs in `O(1)`, performs no allocations.
216    ///
217    /// #### Examples
218    ///
219    /// ```
220    /// use willow_data_model::prelude::*;
221    /// let empty_component = OwnedComponent::<3>::new_empty();
222    /// assert_eq!(empty_component.as_ref(), &[]);
223    /// assert_eq!(empty_component, OwnedComponent::<3>::default());
224    /// ```
225    pub fn new_empty() -> Self {
226        Self(Bytes::new())
227    }
228
229    /// Returns the raw bytes of the component.
230    ///
231    /// ```
232    /// use willow_data_model::prelude::*;
233    /// assert_eq!(OwnedComponent::<3>::new(b"yay").unwrap().as_bytes(), b"yay");
234    /// ```
235    pub fn as_bytes(&self) -> &[u8] {
236        self.0.as_ref()
237    }
238}
239
240impl<const MCL: usize> Deref for OwnedComponent<MCL> {
241    type Target = [u8];
242
243    fn deref(&self) -> &Self::Target {
244        self.0.deref()
245    }
246}
247
248impl<const MCL: usize> AsRef<[u8]> for OwnedComponent<MCL> {
249    fn as_ref(&self) -> &[u8] {
250        self.0.as_ref()
251    }
252}
253
254impl<const MCL: usize> Borrow<[u8]> for OwnedComponent<MCL> {
255    fn borrow(&self) -> &[u8] {
256        self.0.borrow()
257    }
258}
259
260impl<const MCL: usize> fmt::Debug for OwnedComponent<MCL> {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        f.debug_tuple("OwnedComponent")
263            .field(&ComponentFmtHelper::new(self, true))
264            .finish()
265    }
266}
267
268impl<const MCL: usize> fmt::Display for OwnedComponent<MCL> {
269    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270        ComponentFmtHelper::new(self, true).fmt(f)
271    }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
275/// An error arising from trying to construct a [`Component`] of length strictly greater than the `MCL` ([max\_component\_length](https://willowprotocol.org/specs/data-model/index.html#max_component_length)).
276///
277/// #### Example
278///
279/// ```
280/// use willow_data_model::prelude::*;
281/// assert_eq!(Component::<4>::new(b"too_long"), Err(InvalidComponentError));
282/// ```
283pub struct InvalidComponentError;
284
285impl core::fmt::Display for InvalidComponentError {
286    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
287        write!(
288            f,
289            "Length of a component exceeded the maximum component length"
290        )
291    }
292}
293
294impl core::error::Error for InvalidComponentError {}
295
296pub(crate) struct ComponentFmtHelper<'a, T> {
297    component: &'a T,
298    /// If `true`, renders empty components as `"<empty>"`, if `false` renders them as `""` (the empty string).
299    render_empty: bool,
300}
301
302impl<'a, T> ComponentFmtHelper<'a, T> {
303    pub(crate) fn new(component: &'a T, render_empty: bool) -> Self {
304        Self {
305            component,
306            render_empty,
307        }
308    }
309}
310
311impl<'a, T> fmt::Debug for ComponentFmtHelper<'a, T>
312where
313    T: AsRef<[u8]>,
314{
315    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316        if self.component.as_ref().is_empty() {
317            if self.render_empty {
318                write!(f, "<empty>")
319            } else {
320                Ok(())
321            }
322        } else {
323            for byte in self.component.as_ref().iter() {
324                percent_encode_fmt(f, *byte)?;
325            }
326            Ok(())
327        }
328    }
329}
330
331fn byte_is_unreserved(byte: u8) -> bool {
332    byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'.' || byte == b'_' || byte == b'~'
333}
334
335fn percent_encode_fmt(f: &mut fmt::Formatter<'_>, byte: u8) -> fmt::Result {
336    if byte_is_unreserved(byte) {
337        f.write_char(unsafe { char::from_u32_unchecked(byte as u32) })
338    } else {
339        f.write_char('%')?;
340        let low = byte & 0b0000_1111;
341        let high = byte >> 4;
342        f.write_char(char::from_digit(high as u32, 16).unwrap())?;
343        f.write_char(char::from_digit(low as u32, 16).unwrap())
344    }
345}
346
347#[test]
348#[cfg(feature = "std")]
349fn test_fmt() {
350    assert_eq!(
351        &format!("{}", Component::<17>::new(b"").unwrap()),
352        "<empty>"
353    );
354    assert_eq!(&format!("{}", Component::<17>::new(b" ").unwrap()), "%20");
355    assert_eq!(
356        &format!("{}", Component::<17>::new(b".- ~_ab190%/").unwrap()),
357        ".-%20~_ab190%25%2f"
358    );
359}