use core::marker::PhantomData;
use core::mem::{align_of, size_of};
use core::ops::Range;
use core::ptr::{self, NonNull};
use crate::buf::padding_to;
use crate::error::{Error, ErrorKind};
use crate::traits::{ZeroCopy, ZeroSized};
#[must_use = "Must call `Validator::end` when validation is completed"]
#[repr(C)]
pub struct Validator<'a, T: ?Sized> {
data: NonNull<u8>,
offset: usize,
_marker: PhantomData<&'a T>,
}
impl<'a, T: ?Sized> Validator<'a, T> {
pub(crate) fn from_slice(slice: &[u8]) -> Self {
unsafe { Self::new(NonNull::new_unchecked(slice.as_ptr() as *mut u8)) }
}
#[inline]
pub(crate) unsafe fn new(data: NonNull<u8>) -> Self {
Self {
data,
offset: 0,
_marker: PhantomData,
}
}
#[inline]
fn current(&self) -> NonNull<u8> {
unsafe { self.data.add(self.offset) }
}
#[inline]
pub unsafe fn transparent<U>(&mut self) -> &mut Validator<'a, U> {
unsafe { &mut *(self as *mut Self).cast::<Validator<'a, U>>() }
}
#[inline]
pub unsafe fn field<F>(&mut self) -> Result<&F, Error>
where
F: ZeroCopy,
{
unsafe {
self.align_with(align_of::<F>());
let ptr = self.current();
if !ptr.cast::<F>().is_aligned() {
let addr = ptr.as_ptr() as usize;
return Err(Error::new(ErrorKind::AlignmentRangeMismatch {
addr,
range: addr..addr.wrapping_add(size_of::<F>()),
align: align_of::<F>(),
}));
}
F::validate(&mut Validator::new(ptr))?;
let output = ptr.cast::<F>().as_ref();
self.advance::<F>();
Ok(output)
}
}
#[inline]
pub(crate) unsafe fn read_field<F>(&mut self) -> Result<F, Error>
where
F: ZeroCopy + Copy,
{
unsafe {
self.align_with(align_of::<F>());
let ptr = self.current();
F::validate(&mut Validator::new(ptr))?;
let output = ptr::read_unaligned(ptr.cast::<F>().as_ptr());
self.advance::<F>();
Ok(output)
}
}
#[inline]
pub unsafe fn byte(&mut self) -> u8 {
unsafe {
let b = ptr::read(self.current().as_ptr());
self.offset += 1;
b
}
}
#[inline]
pub unsafe fn load_unaligned<F>(&mut self) -> Result<F, Error>
where
F: Copy,
{
unsafe {
let output = ptr::read_unaligned(self.current().cast::<F>().as_ptr());
self.advance::<F>();
Ok(output)
}
}
#[inline]
pub unsafe fn validate<F>(&mut self) -> Result<(), Error>
where
F: ZeroCopy,
{
unsafe { self.validate_with::<F>(align_of::<F>()) }
}
#[inline]
pub unsafe fn validate_with<F>(&mut self, align: usize) -> Result<(), Error>
where
F: ZeroCopy,
{
unsafe {
self.align_with(align.min(align_of::<F>()));
F::validate(&mut Validator::new(self.current()))?;
self.advance::<F>();
Ok(())
}
}
#[inline]
pub unsafe fn validate_zero_sized<F>(&mut self)
where
F: ZeroSized,
{
unsafe {
self.align_with(align_of::<F>());
}
}
#[inline]
pub unsafe fn validate_zero_sized_with<F>(&mut self, align: usize)
where
F: ZeroSized,
{
unsafe {
self.align_with(align.min(align_of::<F>()));
}
}
#[inline]
pub(crate) unsafe fn validate_only<F>(&mut self) -> Result<(), Error>
where
F: ZeroCopy,
{
unsafe {
F::validate(&mut Validator::new(self.current()))?;
self.advance::<F>();
Ok(())
}
}
#[inline]
pub(crate) unsafe fn align_with(&mut self, align: usize) {
self.offset += padding_to(self.offset, align);
}
#[inline]
pub(crate) unsafe fn advance<F>(&mut self) {
self.offset += size_of::<F>();
}
#[inline]
pub(crate) fn range<F>(&self) -> Range<usize> {
let end = (self.data.as_ptr() as usize).wrapping_add(self.offset);
let start = end.wrapping_sub(size_of::<F>());
start..end
}
}