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}