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}