Skip to main content

musli_zerocopy/buf/
validator.rs

1use core::marker::PhantomData;
2use core::mem::{align_of, size_of};
3use core::ops::Range;
4use core::ptr::{self, NonNull};
5
6use crate::buf::padding_to;
7use crate::error::{Error, ErrorKind};
8use crate::traits::{ZeroCopy, ZeroSized};
9
10/// Validator over a [`Buf`] constructed using [`Buf::validate_struct`].
11///
12/// Fields are aligned relative to the start of the value being validated,
13/// which matches how the compiler lays out fields. The value itself is not
14/// required to be aligned, since it might for example be stored inside of a
15/// `#[repr(packed)]` struct or be loaded through an unaligned load.
16///
17/// [`Buf`]: crate::buf::Buf
18/// [`Buf::validate_struct`]: crate::buf::Buf::validate_struct
19#[must_use = "Must call `Validator::end` when validation is completed"]
20// NB: `repr(C)` ensures that `Validator<T>` and `Validator<U>` have the same
21// layout, which `transparent` relies on.
22#[repr(C)]
23pub struct Validator<'a, T: ?Sized> {
24    /// The start of the value being validated.
25    data: NonNull<u8>,
26    /// The offset of the next field relative to `data`.
27    offset: usize,
28    _marker: PhantomData<&'a T>,
29}
30
31impl<'a, T: ?Sized> Validator<'a, T> {
32    /// Construct a validator around a slice.
33    ///
34    /// This does not require that the slice is a valid instance of `T`.
35    pub(crate) fn from_slice(slice: &[u8]) -> Self {
36        // SAFETY: a slice is guaranteed to be non-null.
37        unsafe { Self::new(NonNull::new_unchecked(slice.as_ptr() as *mut u8)) }
38    }
39
40    /// Construct a validator around a pointer.
41    ///
42    /// # Safety
43    ///
44    /// Caller must ensure that the pointer points to an initialized slice of
45    /// size `T`.
46    #[inline]
47    pub(crate) unsafe fn new(data: NonNull<u8>) -> Self {
48        Self {
49            data,
50            offset: 0,
51            _marker: PhantomData,
52        }
53    }
54
55    /// Pointer to the current field.
56    #[inline]
57    fn current(&self) -> NonNull<u8> {
58        // SAFETY: The caller of the methods which advance the offset ensure
59        // that it stays within the value being validated.
60        unsafe { self.data.add(self.offset) }
61    }
62
63    /// Indicate that this validate is transparent over `U`.
64    //
65    /// # Safety
66    ///
67    /// This is only allowed if `T` is `#[repr(transparent)]` over `U`.
68    #[inline]
69    pub unsafe fn transparent<U>(&mut self) -> &mut Validator<'a, U> {
70        // SAFETY: `Validator` is `repr(C)` and its layout does not depend on
71        // `T`.
72        unsafe { &mut *(self as *mut Self).cast::<Validator<'a, U>>() }
73    }
74
75    /// Validate an additional field in the struct and return a reference to it.
76    ///
77    /// # Safety
78    ///
79    /// The current validator only guarantees that validation up to the size of
80    /// `T` can be performed. Advancing beyond that size causes the validator to
81    /// walk out of bounds.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
87    ///
88    /// #[derive(ZeroCopy)]
89    /// #[repr(C)]
90    /// struct Custom { field: u32, field2: u64 }
91    ///
92    /// let mut buf = OwnedBuf::new();
93    ///
94    /// let custom = buf.store(&Custom { field: 42, field2: 85 })?;
95    ///
96    /// let mut v = buf.validate_struct::<Custom>()?;
97    ///
98    /// // SAFETY: We're only validating fields we know are
99    /// // part of the struct, going beyond would constitute undefined behavior.
100    /// unsafe {
101    ///     assert_eq!(v.field::<u32>()?, &42);
102    ///     assert_eq!(v.field::<u64>()?, &85);
103    /// }
104    ///
105    /// # Ok::<_, musli_zerocopy::Error>(())
106    /// ```
107    ///
108    /// For packed structs we have to be even more careful. In fact, we're not
109    /// allowed to call `field` at all and must instead solely rely on
110    /// [`validate_with()`].
111    ///
112    /// # Errors
113    ///
114    /// Since fields are aligned relative to the start of the value being
115    /// validated, this errors if the field is not aligned in memory. This can
116    /// happen if the value is not itself aligned, such as when it is stored
117    /// inside of a packed struct.
118    ///
119    /// [`validate_with()`]: Validator::validate_with
120    #[inline]
121    pub unsafe fn field<F>(&mut self) -> Result<&F, Error>
122    where
123        F: ZeroCopy,
124    {
125        unsafe {
126            self.align_with(align_of::<F>());
127            let ptr = self.current();
128
129            if !ptr.cast::<F>().is_aligned() {
130                let addr = ptr.as_ptr() as usize;
131
132                return Err(Error::new(ErrorKind::AlignmentRangeMismatch {
133                    addr,
134                    range: addr..addr.wrapping_add(size_of::<F>()),
135                    align: align_of::<F>(),
136                }));
137            }
138
139            F::validate(&mut Validator::new(ptr))?;
140            // SAFETY: We've checked that the pointer is aligned above, and
141            // the caller ensures that it is in bounds.
142            let output = ptr.cast::<F>().as_ref();
143            self.advance::<F>();
144            Ok(output)
145        }
146    }
147
148    /// Align, validate and perform an unaligned load of an additional field.
149    ///
150    /// # Safety
151    ///
152    /// The current validator only guarantees that validation up to the size of
153    /// `T` can be performed. Advancing beyond that size causes the validator to
154    /// walk out of bounds.
155    #[inline]
156    pub(crate) unsafe fn read_field<F>(&mut self) -> Result<F, Error>
157    where
158        F: ZeroCopy + Copy,
159    {
160        unsafe {
161            self.align_with(align_of::<F>());
162            let ptr = self.current();
163            F::validate(&mut Validator::new(ptr))?;
164            let output = ptr::read_unaligned(ptr.cast::<F>().as_ptr());
165            self.advance::<F>();
166            Ok(output)
167        }
168    }
169
170    /// Load a single byte from the validator.
171    ///
172    /// # Safety
173    ///
174    /// The current validator only guarantees that validation up to the size of
175    /// `T` can be performed. Advancing beyond that size causes the validator to
176    /// walk out of bounds.
177    #[inline]
178    pub unsafe fn byte(&mut self) -> u8 {
179        unsafe {
180            let b = ptr::read(self.current().as_ptr());
181            self.offset += 1;
182            b
183        }
184    }
185
186    /// Perform an unaligned load of the given field.
187    ///
188    /// # Safety
189    ///
190    /// The current validator only guarantees that validation up to the size of
191    /// `T` can be performed. Advancing beyond that size causes the validator to
192    /// walk out of bounds.
193    #[inline]
194    pub unsafe fn load_unaligned<F>(&mut self) -> Result<F, Error>
195    where
196        F: Copy,
197    {
198        // SAFETY: The caller ensures that the field is in bounds.
199        unsafe {
200            let output = ptr::read_unaligned(self.current().cast::<F>().as_ptr());
201            self.advance::<F>();
202            Ok(output)
203        }
204    }
205
206    /// Validate an additional field in the struct.
207    ///
208    /// # Safety
209    ///
210    /// The current validator only guarantees that validation up to the size of
211    /// `T` can be performed. Advancing beyond that size causes the validator to
212    /// walk out of bounds.
213    ///
214    /// # Examples
215    ///
216    /// Validator a packed struct:
217    ///
218    /// ```
219    /// use std::num::NonZeroU64;
220    ///
221    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
222    ///
223    /// #[derive(ZeroCopy)]
224    /// #[repr(C)]
225    /// struct Packed { field: u32, field2: NonZeroU64 }
226    ///
227    /// let mut buf = OwnedBuf::new();
228    ///
229    /// buf.store(&Packed { field: 42, field2: NonZeroU64::new(84).unwrap() })?;
230    ///
231    /// let mut v = buf.validate_struct::<Packed>()?;
232    ///
233    /// // SAFETY: We're only validating fields we know are
234    /// // part of the struct, and do not go beyond. We're
235    /// // also making sure not to construct reference to
236    /// // the fields which would be an error for a packed struct.
237    /// unsafe {
238    ///     v.validate::<u32>()?;
239    ///     v.validate::<NonZeroU64>()?;
240    /// }
241    ///
242    /// # Ok::<_, musli_zerocopy::Error>(())
243    /// ```
244    #[inline]
245    pub unsafe fn validate<F>(&mut self) -> Result<(), Error>
246    where
247        F: ZeroCopy,
248    {
249        unsafe { self.validate_with::<F>(align_of::<F>()) }
250    }
251
252    /// Validate an additional field in a struct marked
253    /// `#[repr(packed(align))]`.
254    ///
255    /// The field is aligned to `min(align, align_of::<F>())`, which is the
256    /// alignment the compiler uses for fields in a packed struct.
257    ///
258    /// # Safety
259    ///
260    /// The current validator only guarantees that validation up to the size of
261    /// `T` can be performed. Advancing beyond that size causes the validator to
262    /// walk out of bounds.
263    ///
264    /// The `align` argument must match the alignment `N` used in the
265    /// `#[repr(packed(N))]` argument, note that `#[repr(packed)]` has an
266    /// argument of 1. `align` must be a power of two.
267    ///
268    /// # Examples
269    ///
270    /// Validator a packed struct:
271    ///
272    /// ```
273    /// use std::num::NonZeroU64;
274    ///
275    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
276    ///
277    /// #[derive(ZeroCopy)]
278    /// #[repr(C, packed(2))]
279    /// struct Packed { field: u32, field2: NonZeroU64 }
280    ///
281    /// let mut buf = OwnedBuf::new();
282    ///
283    /// buf.store(&Packed { field: 42, field2: NonZeroU64::new(84).unwrap() })?;
284    ///
285    /// let mut v = buf.validate_struct::<Packed>()?;
286    ///
287    /// // SAFETY: We're only validating fields we know are
288    /// // part of the struct, and do not go beyond. We're
289    /// // also making sure not to construct reference to
290    /// // the fields which would be an error for a packed struct.
291    /// unsafe {
292    ///     v.validate_with::<u32>(2)?;
293    ///     v.validate_with::<NonZeroU64>(2)?;
294    /// }
295    ///
296    /// # Ok::<_, musli_zerocopy::Error>(())
297    /// ```
298    #[inline]
299    pub unsafe fn validate_with<F>(&mut self, align: usize) -> Result<(), Error>
300    where
301        F: ZeroCopy,
302    {
303        unsafe {
304            self.align_with(align.min(align_of::<F>()));
305            F::validate(&mut Validator::new(self.current()))?;
306            self.advance::<F>();
307            Ok(())
308        }
309    }
310
311    /// Skip over an ignored zero-sized field `F`.
312    ///
313    /// Zero-sized fields can have an alignment larger than 1, in which case
314    /// they affect the offset of the fields that follow them.
315    ///
316    /// # Safety
317    ///
318    /// The caller must ensure that the field type `F` is an actual field in
319    /// order in the struct being validated.
320    #[inline]
321    pub unsafe fn validate_zero_sized<F>(&mut self)
322    where
323        F: ZeroSized,
324    {
325        unsafe {
326            self.align_with(align_of::<F>());
327        }
328    }
329
330    /// Skip over an ignored zero-sized field `F` in a struct marked
331    /// `#[repr(packed(align))]`.
332    ///
333    /// The field is aligned to `min(align, align_of::<F>())`.
334    ///
335    /// # Safety
336    ///
337    /// The caller must ensure that the field type `F` is an actual field in
338    /// order in the struct being validated and that `align` matches the
339    /// argument provided to `#[repr(packed)]` (note that empty means 1).
340    /// `align` must be a power of two.
341    #[inline]
342    pub unsafe fn validate_zero_sized_with<F>(&mut self, align: usize)
343    where
344        F: ZeroSized,
345    {
346        unsafe {
347            self.align_with(align.min(align_of::<F>()));
348        }
349    }
350
351    /// Only validate the given field without aligning it.
352    ///
353    /// # Safety
354    ///
355    /// The caller is responsible for ensuring that the field is properly
356    /// aligned already by for example calling [`align_with::<F>()`].
357    ///
358    /// [`align_with::<F>()`]: Self::align_with
359    #[inline]
360    pub(crate) unsafe fn validate_only<F>(&mut self) -> Result<(), Error>
361    where
362        F: ZeroCopy,
363    {
364        unsafe {
365            F::validate(&mut Validator::new(self.current()))?;
366            self.advance::<F>();
367            Ok(())
368        }
369    }
370
371    /// Align the current offset to `align` relative to the start of the value
372    /// being validated.
373    ///
374    /// `align` must be a power of two.
375    #[inline]
376    pub(crate) unsafe fn align_with(&mut self, align: usize) {
377        self.offset += padding_to(self.offset, align);
378    }
379
380    /// Advance the current offset by the size of `F`.
381    #[inline]
382    pub(crate) unsafe fn advance<F>(&mut self) {
383        self.offset += size_of::<F>();
384    }
385
386    /// Return the address range associated with a just read `F` for diagnostics.
387    #[inline]
388    pub(crate) fn range<F>(&self) -> Range<usize> {
389        let end = (self.data.as_ptr() as usize).wrapping_add(self.offset);
390        let start = end.wrapping_sub(size_of::<F>());
391        start..end
392    }
393}