#![allow(clippy::missing_safety_doc)]
use core::marker::PhantomData;
use core::mem::{align_of, size_of, size_of_val};
use core::num::Wrapping;
use core::slice;
use core::str;
use crate::buf::{Buf, BufMut, Padder, Validator, Visit};
use crate::error::{Error, ErrorKind};
use crate::pointer::{Pointee, Size};
use crate::Ref;
mod sealed {
use crate::ZeroCopy;
pub trait Sealed {}
impl Sealed for str {}
impl<T> Sealed for [T] where T: ZeroCopy {}
}
pub unsafe trait UnsizedZeroCopy<P: ?Sized, O>: self::sealed::Sealed
where
P: Pointee<O>,
{
const ALIGN: usize;
fn size(&self) -> usize;
fn metadata(&self) -> P::Metadata;
unsafe fn store(&self, buf: &mut BufMut<'_>);
unsafe fn validate(
buf: *const u8,
len: usize,
metadata: P::Packed,
) -> Result<P::Metadata, Error>;
unsafe fn coerce(buf: *const u8, metadata: P::Metadata) -> *const Self;
unsafe fn coerce_mut(buf: *mut u8, metadata: P::Metadata) -> *mut Self;
}
pub unsafe trait ZeroSized {}
unsafe impl<T> ZeroSized for Wrapping<T> where T: ZeroSized {}
unsafe impl<T> ZeroCopy for Wrapping<T>
where
T: Copy + ZeroCopy,
{
const ANY_BITS: bool = T::ANY_BITS;
const PADDED: bool = T::PADDED;
#[inline]
unsafe fn pad(padder: &mut Padder<'_, Self>) {
padder.pad::<T>();
}
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
validator.validate::<T>()
}
}
unsafe impl ZeroSized for () {}
unsafe impl<T> ZeroSized for [T; 0] {}
unsafe impl<T: ?Sized> ZeroSized for PhantomData<T> {}
pub unsafe trait ZeroCopy: Sized {
#[doc(hidden)]
const ANY_BITS: bool;
#[doc(hidden)]
const PADDED: bool;
#[doc(hidden)]
unsafe fn pad(padder: &mut Padder<'_, Self>);
#[doc(hidden)]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error>;
fn init_padding(&mut self) {
unsafe {
let ptr = (self as *mut Self).cast::<u8>();
if Self::PADDED {
let mut padder = Padder::new(ptr);
Self::pad(&mut padder);
padder.remaining();
}
}
}
#[inline]
fn to_bytes(&mut self) -> &[u8] {
self.init_padding();
unsafe {
let ptr = (self as *mut Self).cast::<u8>();
slice::from_raw_parts(ptr, size_of::<Self>())
}
}
#[inline]
unsafe fn to_bytes_unchecked(&self) -> &[u8] {
unsafe {
let ptr = (self as *const Self).cast::<u8>();
slice::from_raw_parts(ptr, size_of::<Self>())
}
}
#[inline]
fn from_bytes(bytes: &[u8]) -> Result<&Self, Error> {
Buf::new(bytes).load(Ref::<Self>::zero())
}
#[inline]
fn from_bytes_mut(bytes: &mut [u8]) -> Result<&mut Self, Error> {
Buf::new_mut(bytes).load_mut(Ref::<Self>::zero())
}
}
unsafe impl<P: ?Sized, O> UnsizedZeroCopy<P, O> for str
where
P: Pointee<O, Packed = O, Metadata = usize>,
O: Size,
{
const ALIGN: usize = align_of::<u8>();
#[inline]
fn size(&self) -> usize {
size_of_val(self)
}
#[inline]
fn metadata(&self) -> P::Metadata {
<str>::len(self)
}
#[inline]
unsafe fn store(&self, buf: &mut BufMut<'_>) {
buf.store_unsized_slice(self.as_bytes());
}
#[inline]
unsafe fn validate(
ptr: *const u8,
len: usize,
metadata: P::Packed,
) -> Result<P::Metadata, Error> {
let metadata = metadata.as_usize();
if metadata > len {
return Err(Error::new(ErrorKind::OutOfRangeBounds {
range: 0..metadata,
len,
}));
};
let buf = slice::from_raw_parts(ptr, metadata);
str::from_utf8(buf).map_err(|error| Error::new(ErrorKind::Utf8Error { error }))?;
Ok(metadata)
}
#[inline]
unsafe fn coerce(ptr: *const u8, metadata: P::Metadata) -> *const Self {
let slice = slice::from_raw_parts(ptr, metadata);
str::from_utf8_unchecked(slice)
}
#[inline]
unsafe fn coerce_mut(ptr: *mut u8, metadata: P::Metadata) -> *mut Self {
let slice = slice::from_raw_parts_mut(ptr, metadata);
str::from_utf8_unchecked_mut(slice)
}
}
unsafe impl<T, P: ?Sized, O> UnsizedZeroCopy<P, O> for [T]
where
T: ZeroCopy,
P: Pointee<O, Packed = O, Metadata = usize>,
O: Size,
{
const ALIGN: usize = align_of::<T>();
#[inline]
fn size(&self) -> usize {
size_of_val(self)
}
#[inline]
fn metadata(&self) -> usize {
self.len()
}
#[inline]
unsafe fn store(&self, buf: &mut BufMut<'_>) {
buf.store_unsized_slice(self);
}
#[inline]
unsafe fn validate(
buf: *const u8,
len: usize,
metadata: P::Packed,
) -> Result<P::Metadata, Error> {
let metadata = metadata.as_usize();
let Some(size) = metadata.checked_mul(size_of::<T>()) else {
return Err(Error::new(ErrorKind::LengthOverflow {
len: metadata,
size: size_of::<T>(),
}));
};
if size > len {
return Err(Error::new(ErrorKind::OutOfRangeBounds {
range: 0..metadata,
len,
}));
};
if !T::ANY_BITS {
crate::buf::validate_array::<[T], T>(&mut Validator::new(buf), metadata)?;
}
Ok(metadata)
}
#[inline]
unsafe fn coerce(buf: *const u8, metadata: P::Metadata) -> *const Self {
slice::from_raw_parts(buf.cast(), metadata)
}
#[inline]
unsafe fn coerce_mut(buf: *mut u8, metadata: P::Metadata) -> *mut Self {
slice::from_raw_parts_mut(buf.cast(), metadata)
}
}
macro_rules! impl_number {
($ty:ty) => {
#[doc = concat!(" [`ZeroCopy`] implementation for `", stringify!($ty), "`")]
#[doc = concat!(" field: ", stringify!($ty), ",")]
#[doc = concat!("let zero: ", stringify!($ty), " = 0;")]
#[doc = concat!("let one: ", stringify!($ty), " = 1;")]
#[doc = concat!("let zero = ", stringify!($ty), "::to_ne_bytes(0);")]
#[doc = concat!("let zero = buf::aligned_buf::<", stringify!($ty), ">(&zero);")]
#[doc = concat!("let one = ", stringify!($ty), "::to_ne_bytes(1);")]
#[doc = concat!("let one = buf::aligned_buf::<", stringify!($ty), ">(&one);")]
unsafe impl ZeroCopy for $ty {
const ANY_BITS: bool = true;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[inline]
unsafe fn validate(_: &mut Validator<'_, Self>) -> Result<(), Error> {
Ok(())
}
}
impl Visit for $ty {
type Target = $ty;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
};
}
impl_number!(usize);
impl_number!(isize);
impl_number!(u8);
impl_number!(u16);
impl_number!(u32);
impl_number!(u64);
impl_number!(u128);
impl_number!(i8);
impl_number!(i16);
impl_number!(i32);
impl_number!(i64);
impl_number!(i128);
macro_rules! impl_float {
($ty:ty) => {
unsafe impl ZeroCopy for $ty {
const ANY_BITS: bool = true;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[inline]
unsafe fn validate(_: &mut Validator<'_, Self>) -> Result<(), Error> {
Ok(())
}
}
impl Visit for $ty {
type Target = $ty;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
};
}
impl_float!(f32);
impl_float!(f64);
unsafe impl ZeroCopy for char {
const ANY_BITS: bool = false;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[allow(clippy::missing_safety_doc)]
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
let repr = validator.load_unaligned::<u32>()?;
if char::try_from(repr).is_err() {
return Err(Error::new(ErrorKind::IllegalChar { repr }));
}
Ok(())
}
}
impl Visit for char {
type Target = char;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
unsafe impl ZeroCopy for bool {
const ANY_BITS: bool = false;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[allow(clippy::missing_safety_doc)]
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
match validator.byte() {
0 | 1 => (),
repr => return Err(Error::new(ErrorKind::IllegalBool { repr })),
}
Ok(())
}
}
impl Visit for bool {
type Target = bool;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
macro_rules! impl_nonzero_number {
($ty:ident, $inner:ty) => {
#[doc = concat!(" [`ZeroCopy`] implementation for `", stringify!($ty), "`")]
#[doc = concat!("use std::num::", stringify!($ty), ";")]
#[doc = concat!(" field: ", stringify!($ty), ",")]
#[doc = concat!("let zero = ", stringify!($inner), "::to_ne_bytes(0);")]
#[doc = concat!("let zero = buf::aligned_buf::<", stringify!($ty), ">(&zero);")]
#[doc = concat!("let one = ", stringify!($inner), "::to_ne_bytes(1);")]
#[doc = concat!("let one = buf::aligned_buf::<", stringify!($ty), ">(&one);")]
unsafe impl ZeroCopy for ::core::num::$ty {
const ANY_BITS: bool = false;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
if validator.load_unaligned::<$inner>()? == 0 {
return Err(Error::new(ErrorKind::NonZeroZeroed {
range: validator.range::<::core::num::$ty>(),
}));
}
Ok(())
}
}
impl Visit for ::core::num::$ty {
type Target = ::core::num::$ty;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
#[doc = concat!(" [`ZeroCopy`] implementation for `Option<", stringify!($ty), ">`")]
#[doc = concat!("use std::num::", stringify!($ty), ";")]
#[doc = concat!(" field: Option<", stringify!($ty), ">,")]
#[doc = concat!("let zero = ", stringify!($inner), "::to_ne_bytes(0);")]
#[doc = concat!("let zero = buf::aligned_buf::<", stringify!($ty), ">(&zero);")]
#[doc = concat!("let one = ", stringify!($inner), "::to_ne_bytes(1);")]
#[doc = concat!("let one = buf::aligned_buf::<", stringify!($ty), ">(&one);")]
#[doc = concat!("assert_eq!(st.field, ", stringify!($ty), "::new(1));")]
unsafe impl ZeroCopy for Option<::core::num::$ty> {
const ANY_BITS: bool = true;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {}
#[inline]
unsafe fn validate(_: &mut Validator<'_, Self>) -> Result<(), Error> {
Ok(())
}
}
impl Visit for Option<::core::num::$ty> {
type Target = Option<::core::num::$ty>;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
};
}
impl_nonzero_number!(NonZeroUsize, usize);
impl_nonzero_number!(NonZeroIsize, isize);
impl_nonzero_number!(NonZeroU8, u8);
impl_nonzero_number!(NonZeroU16, u16);
impl_nonzero_number!(NonZeroU32, u32);
impl_nonzero_number!(NonZeroU64, u64);
impl_nonzero_number!(NonZeroU128, u128);
impl_nonzero_number!(NonZeroI8, i8);
impl_nonzero_number!(NonZeroI16, i16);
impl_nonzero_number!(NonZeroI32, i32);
impl_nonzero_number!(NonZeroI64, i64);
impl_nonzero_number!(NonZeroI128, i128);
macro_rules! impl_zst {
($({$($bounds:tt)*},)? $ty:ty, $expr:expr , {$example:ty $(, $import:path)?}) => {
#[doc = concat!(" [`ZeroCopy`] implementation for `", stringify!($ty), "`")]
$(#[doc = concat!("use ", stringify!($import), ";")])*
#[doc = concat!(" field: ", stringify!($example), ",")]
unsafe impl $(<$($bounds)*>)* ZeroCopy for $ty {
const ANY_BITS: bool = true;
const PADDED: bool = false;
#[inline]
unsafe fn pad(_: &mut Padder<'_, Self>) {
}
#[inline]
unsafe fn validate(_: &mut Validator<'_, Self>) -> Result<(), Error> {
Ok(())
}
}
impl $(<$($bounds)*>)* Visit for $ty {
type Target = $ty;
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}
};
}
impl_zst!((), (), { () });
impl_zst!({T}, PhantomData<T>, PhantomData, {PhantomData<u32>, std::marker::PhantomData});
unsafe impl<T, const N: usize> ZeroCopy for [T; N]
where
T: ZeroCopy,
{
const ANY_BITS: bool = T::ANY_BITS;
const PADDED: bool = T::PADDED;
#[inline]
unsafe fn pad(padder: &mut Padder<'_, Self>) {
if T::PADDED {
for _ in 0..N {
padder.pad::<T>();
}
}
}
#[allow(clippy::missing_safety_doc)]
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
crate::buf::validate_array::<_, T>(validator, N)?;
Ok(())
}
}
impl<T> Visit for [T; 0] {
type Target = [T; 0];
#[inline]
fn visit<V, O>(&self, _: &Buf, visitor: V) -> Result<O, Error>
where
V: FnOnce(&Self::Target) -> O,
{
Ok(visitor(self))
}
}