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}