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