#![allow(clippy::len_without_is_empty)]
use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem::{align_of, size_of};
use core::str;
use crate::buf::{AnyValue, Buf, BufMut};
use crate::error::{Error, ErrorKind};
pub unsafe trait UnsizedZeroCopy {
const ALIGN: usize;
fn len(&self) -> usize;
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut;
fn read_from(buf: &Buf) -> Result<&Self, Error>;
}
pub unsafe trait ZeroCopy: Sized {
const ANY_BITS: bool = size_of::<Self>() == 0;
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut;
fn read_from(buf: &Buf) -> Result<&Self, Error>;
unsafe fn validate_aligned(buf: &Buf) -> Result<(), Error>;
}
unsafe impl UnsizedZeroCopy for str {
const ALIGN: usize = align_of::<u8>();
fn len(&self) -> usize {
<str>::len(self)
}
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut,
{
buf.extend_from_slice(self.as_bytes())
}
fn read_from(buf: &Buf) -> Result<&Self, Error> {
str::from_utf8(buf.as_bytes()).map_err(|error| Error::new(ErrorKind::Utf8Error { error }))
}
}
unsafe impl UnsizedZeroCopy for [u8] {
const ALIGN: usize = align_of::<u8>();
fn len(&self) -> usize {
<[_]>::len(self)
}
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut,
{
buf.extend_from_slice(self)
}
fn read_from(buf: &Buf) -> Result<&Self, Error> {
Ok(buf.as_bytes())
}
}
macro_rules! impl_number {
($ty:ty) => {
#[doc = concat!(" [`ZeroCopy`] implementation for `", stringify!($ty), "`")]
#[doc = concat!(" field: ", stringify!($ty), ",")]
#[doc = concat!("let size = size_of::<", stringify!($ty) ,">();")]
#[doc = concat!("let zero: ", stringify!($ty), " = 0;")]
#[doc = concat!("let one: ", stringify!($ty), " = 1;")]
unsafe impl ZeroCopy for $ty {
const ANY_BITS: bool = true;
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut,
{
buf.extend_from_slice(&<$ty>::to_ne_bytes(*self)[..])
}
fn read_from(buf: &Buf) -> Result<&Self, Error> {
if !buf.is_compatible(core::alloc::Layout::new::<$ty>()) {
return Err(Error::new(ErrorKind::LayoutMismatch {
layout: Layout::new::<$ty>(),
buf: buf.range(),
}));
}
Ok(unsafe { buf.cast() })
}
#[allow(clippy::missing_safety_doc)]
unsafe fn validate_aligned(_: &Buf) -> Result<(), Error> {
Ok(())
}
}
impl AnyValue for $ty {
type Target = $ty;
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_nonzero_number {
($ty:ident, $example:ty) => {
#[doc = concat!(" [`ZeroCopy`] implementation for `", stringify!($ty), "`")]
#[doc = concat!("use std::num::", stringify!($ty), ";")]
#[doc = concat!(" field: ", stringify!($ty), ",")]
#[doc = concat!("let size = size_of::<", stringify!($example) ,">();")]
#[doc = concat!("let zero: ", stringify!($example), " = 0;")]
#[doc = concat!("let one: ", stringify!($example), " = 1;")]
unsafe impl ZeroCopy for ::core::num::$ty {
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut,
{
buf.extend_from_slice(&self.get().to_ne_bytes()[..])
}
fn read_from(buf: &Buf) -> Result<&Self, Error> {
if !buf.is_compatible(core::alloc::Layout::new::<::core::num::$ty>()) {
return Err(Error::new(ErrorKind::LayoutMismatch {
layout: Layout::new::<::core::num::$ty>(),
buf: buf.range(),
}));
}
if buf.is_zeroed() {
return Err(Error::new(ErrorKind::NonZeroZeroed { range: buf.range() }));
}
Ok(unsafe { buf.cast() })
}
#[allow(clippy::missing_safety_doc)]
unsafe fn validate_aligned(buf: &Buf) -> Result<(), Error> {
if buf.is_zeroed() {
return Err(Error::new(ErrorKind::NonZeroZeroed { range: buf.range() }));
}
Ok(())
}
}
impl AnyValue for ::core::num::$ty {
type Target = ::core::num::$ty;
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 {
($({$name:ident},)? $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 $(<$name>)* ZeroCopy for $ty {
fn write_to<B: ?Sized>(&self, _: &mut B) -> Result<(), Error>
where
B: BufMut,
{
Ok(())
}
fn read_from(_: &Buf) -> Result<&Self, Error> {
Ok(&$expr)
}
#[allow(clippy::missing_safety_doc)]
unsafe fn validate_aligned(_: &Buf) -> Result<(), Error> {
Ok(())
}
}
impl $(<$name>)* AnyValue for $ty {
type Target = $ty;
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,
{
fn write_to<B: ?Sized>(&self, buf: &mut B) -> Result<(), Error>
where
B: BufMut,
{
for element in self {
element.write_to(buf)?;
}
Ok(())
}
fn read_from(buf: &Buf) -> Result<&Self, Error> {
crate::buf::validate_array::<T>(buf, N)?;
Ok(unsafe { buf.cast() })
}
#[allow(clippy::missing_safety_doc)]
unsafe fn validate_aligned(buf: &Buf) -> Result<(), Error> {
crate::buf::validate_array_aligned::<T>(buf)?;
Ok(())
}
}