musli_zerocopy/buf/
validator.rs

1use core::marker::PhantomData;
2use core::mem::{align_of, size_of, transmute};
3use core::ops::Range;
4use core::ptr::{self, NonNull};
5
6use crate::error::Error;
7use crate::traits::ZeroCopy;
8
9/// Validator over a [`Buf`] constructed using [`Buf::validate_struct`].
10///
11/// [`Buf`]: crate::buf::Buf
12/// [`Buf::validate_struct`]: crate::buf::Buf::validate_struct
13#[must_use = "Must call `Validator::end` when validation is completed"]
14#[repr(transparent)]
15pub struct Validator<'a, T: ?Sized> {
16    data: NonNull<u8>,
17    _marker: PhantomData<&'a T>,
18}
19
20impl<'a, T: ?Sized> Validator<'a, T> {
21    /// Construct a validator around a slice.
22    ///
23    /// This does not require that the slice is a valid instance of `T`.
24    pub(crate) fn from_slice(slice: &[u8]) -> Self {
25        // SAFETY: a slice is guaranteed to be non-null.
26        unsafe { Self::new(NonNull::new_unchecked(slice.as_ptr() as *mut u8)) }
27    }
28
29    /// Construct a validator around a pointer.
30    ///
31    /// # Safety
32    ///
33    /// Caller must ensure that the pointer points to an initialized slice of
34    /// size `T`.
35    #[inline]
36    pub(crate) unsafe fn new(data: NonNull<u8>) -> Self {
37        Self {
38            data,
39            _marker: PhantomData,
40        }
41    }
42
43    /// Indicate that this validate is transparent over `U`.
44    //
45    /// # Safety
46    ///
47    /// This is only allowed if `T` is `#[repr(transparent)]` over `U`.
48    #[inline]
49    pub unsafe fn transparent<U>(&mut self) -> &mut Validator<'a, U> {
50        unsafe { transmute(self) }
51    }
52
53    /// Validate an additional field in the struct and return a reference to it.
54    ///
55    /// # Safety
56    ///
57    /// The current validator only guarantees that validation up to the size of
58    /// `T` can be performed. Advancing beyond that size causes the validator to
59    /// walk out of bounds.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
65    ///
66    /// #[derive(ZeroCopy)]
67    /// #[repr(C)]
68    /// struct Custom { field: u32, field2: u64 }
69    ///
70    /// let mut buf = OwnedBuf::new();
71    ///
72    /// let custom = buf.store(&Custom { field: 42, field2: 85 });
73    ///
74    /// let mut v = buf.validate_struct::<Custom>()?;
75    ///
76    /// // SAFETY: We're only validating fields we know are
77    /// // part of the struct, going beyond would constitute undefined behavior.
78    /// unsafe {
79    ///     assert_eq!(v.field::<u32>()?, &42);
80    ///     assert_eq!(v.field::<u64>()?, &85);
81    /// }
82    ///
83    /// # Ok::<_, musli_zerocopy::Error>(())
84    /// ```
85    ///
86    /// For packed structs we have to be even more careful. In fact, we're not
87    /// allowed to call `field` at all and must instead solely rely on
88    /// [`validate_with()`].
89    ///
90    /// [`validate_with()`]: Validator::validate_with
91    #[inline]
92    pub unsafe fn field<F>(&mut self) -> Result<&F, Error>
93    where
94        F: ZeroCopy,
95    {
96        // SAFETY: We've ensured that the provided buffer is aligned and sized
97        // appropriately above.
98        unsafe {
99            self.align_with(align_of::<F>());
100            F::validate(&mut Validator::new(self.data))?;
101            let output = self.data.cast::<F>().as_ref();
102            self.advance::<F>();
103            Ok(output)
104        }
105    }
106
107    /// Load a single byte from the validator.
108    ///
109    /// # Safety
110    ///
111    /// The current validator only guarantees that validation up to the size of
112    /// `T` can be performed. Advancing beyond that size causes the validator to
113    /// walk out of bounds.
114    #[inline]
115    pub unsafe fn byte(&mut self) -> u8 {
116        unsafe {
117            let b = ptr::read(self.data.as_ptr());
118            self.data = NonNull::new_unchecked(self.data.as_ptr().add(1));
119            b
120        }
121    }
122
123    /// Perform an unaligned load of the given field.
124    ///
125    /// # Safety
126    ///
127    /// The current validator only guarantees that validation up to the size of
128    /// `T` can be performed. Advancing beyond that size causes the validator to
129    /// walk out of bounds.
130    #[inline]
131    pub unsafe fn load_unaligned<F>(&mut self) -> Result<F, Error>
132    where
133        F: Copy,
134    {
135        // SAFETY: We've ensured that the provided buffer is aligned and
136        // sized
137        // appropriately above.
138        unsafe {
139            let output = ptr::read_unaligned(self.data.cast::<F>().as_ptr());
140            self.advance::<F>();
141            Ok(output)
142        }
143    }
144
145    /// Validate an additional field in the struct.
146    ///
147    /// # Safety
148    ///
149    /// The current validator only guarantees that validation up to the size of
150    /// `T` can be performed. Advancing beyond that size causes the validator to
151    /// walk out of bounds.
152    ///
153    /// # Examples
154    ///
155    /// Validator a packed struct:
156    ///
157    /// ```
158    /// use std::num::NonZeroU64;
159    ///
160    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
161    ///
162    /// #[derive(ZeroCopy)]
163    /// #[repr(C)]
164    /// struct Packed { field: u32, field2: NonZeroU64 }
165    ///
166    /// let mut buf = OwnedBuf::new();
167    ///
168    /// buf.store(&Packed { field: 42, field2: NonZeroU64::new(84).unwrap() });
169    ///
170    /// let mut v = buf.validate_struct::<Packed>()?;
171    ///
172    /// // SAFETY: We're only validating fields we know are
173    /// // part of the struct, and do not go beyond. We're
174    /// // also making sure not to construct reference to
175    /// // the fields which would be an error for a packed struct.
176    /// unsafe {
177    ///     v.validate::<u32>()?;
178    ///     v.validate::<NonZeroU64>()?;
179    /// }
180    ///
181    /// # Ok::<_, musli_zerocopy::Error>(())
182    /// ```
183    #[inline]
184    pub unsafe fn validate<F>(&mut self) -> Result<(), Error>
185    where
186        F: ZeroCopy,
187    {
188        unsafe { self.validate_with::<F>(align_of::<F>()) }
189    }
190
191    /// Validate an additional field in the struct with alignment `align`.
192    ///
193    /// # Safety
194    ///
195    /// The current validator only guarantees that validation up to the size of
196    /// `T` can be performed. Advancing beyond that size causes the validator to
197    /// walk out of bounds.
198    ///
199    /// The `align` argument must match the alignment `N` used in the
200    /// `#[repr(packed(N))]` argument, note that `#[repr(packed)]` has an
201    /// argument of 1.
202    ///
203    /// # Examples
204    ///
205    /// Validator a packed struct:
206    ///
207    /// ```
208    /// use std::num::NonZeroU64;
209    ///
210    /// use musli_zerocopy::{OwnedBuf, ZeroCopy};
211    ///
212    /// #[derive(ZeroCopy)]
213    /// #[repr(C, packed(2))]
214    /// struct Packed { field: u32, field2: NonZeroU64 }
215    ///
216    /// let mut buf = OwnedBuf::new();
217    ///
218    /// buf.store(&Packed { field: 42, field2: NonZeroU64::new(84).unwrap() });
219    ///
220    /// let mut v = buf.validate_struct::<Packed>()?;
221    ///
222    /// // SAFETY: We're only validating fields we know are
223    /// // part of the struct, and do not go beyond. We're
224    /// // also making sure not to construct reference to
225    /// // the fields which would be an error for a packed struct.
226    /// unsafe {
227    ///     v.validate_with::<u32>(2)?;
228    ///     v.validate_with::<NonZeroU64>(2)?;
229    /// }
230    ///
231    /// # Ok::<_, musli_zerocopy::Error>(())
232    /// ```
233    #[inline]
234    pub unsafe fn validate_with<F>(&mut self, align: usize) -> Result<(), Error>
235    where
236        F: ZeroCopy,
237    {
238        unsafe {
239            self.align_with(align);
240            F::validate(&mut Validator::new(self.data))?;
241            self.advance::<F>();
242            Ok(())
243        }
244    }
245
246    /// Only validate the given field without aligning it.
247    ///
248    /// # Safety
249    ///
250    /// The caller is responsible for ensuring that the field is properly
251    /// aligned already by for example calling [`align_with::<F>()`].
252    ///
253    /// [`align_with::<F>()`]: Self::align_with
254    #[inline]
255    pub(crate) unsafe fn validate_only<F>(&mut self) -> Result<(), Error>
256    where
257        F: ZeroCopy,
258    {
259        unsafe {
260            F::validate(&mut Validator::new(self.data))?;
261            self.advance::<F>();
262            Ok(())
263        }
264    }
265
266    /// Align the current pointer by `F`.
267    #[inline]
268    pub(crate) unsafe fn align_with(&mut self, align: usize) {
269        unsafe {
270            let offset = self.data.as_ptr().align_offset(align);
271            self.data = NonNull::new_unchecked(self.data.as_ptr().add(offset));
272        }
273    }
274
275    /// Advance the current pointer by `F`.
276    #[inline]
277    pub(crate) unsafe fn advance<F>(&mut self) {
278        unsafe {
279            self.data = NonNull::new_unchecked(self.data.as_ptr().add(size_of::<F>()));
280        }
281    }
282
283    /// Return the address range associated with a just read `F` for diagnostics.
284    #[inline]
285    pub(crate) fn range<F>(&self) -> Range<usize> {
286        let end = self.data.as_ptr() as usize;
287        let start = end.wrapping_sub(size_of::<F>());
288        start..end
289    }
290}