use core::num::{
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
use std::mem::MaybeUninit;
use std::ptr::Alignment;
use crate::primitive::{
ArchivedChar, ArchivedF32, ArchivedF64, ArchivedI16, ArchivedI32, ArchivedI64, ArchivedI128,
ArchivedIsize, ArchivedNonZeroI16, ArchivedNonZeroI32, ArchivedNonZeroI64, ArchivedNonZeroI128,
ArchivedNonZeroIsize, ArchivedNonZeroU16, ArchivedNonZeroU32, ArchivedNonZeroU64,
ArchivedNonZeroU128, ArchivedNonZeroUsize, ArchivedU16, ArchivedU32, ArchivedU64, ArchivedU128,
ArchivedUsize, FixedIsize, FixedUsize,
};
use crate::{Serialize, SerializeError, VerifyError};
macro_rules! impl_archive_self_primitives {
($($type:ty;)+) => {
$(
impl Serialize for $type {
type Archived = Self;
const ALIGN: Alignment = Alignment::MIN;
const COPY_OPTIMIZATION: bool = true;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(*self) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(_: *const Self, _: *const u8) -> Result<(), VerifyError> {
Ok(())
}
}
)+
}
}
impl_archive_self_primitives! {
();
i8;
u8;
}
impl Serialize for bool {
type Archived = bool;
const ALIGN: Alignment = Alignment::MIN;
const COPY_OPTIMIZATION: bool = true;
unsafe fn serialize(&self, out: *mut MaybeUninit<bool>, _: *mut MaybeUninit<u8>) -> usize {
unsafe { *out = MaybeUninit::new(*self) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(this: *const bool, _: *const u8) -> Result<(), VerifyError> {
let byte = unsafe { *this.cast::<u8>() };
match byte {
0 | 1 => Ok(()),
_ => Err(VerifyError::InvalidBool { byte }),
}
}
}
macro_rules! impl_archive_self_nonzero_primitives {
($($type:ty;)+) => {
$(
impl Serialize for $type {
type Archived = Self;
const ALIGN: Alignment = Alignment::MIN;
const COPY_OPTIMIZATION: bool = true;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(*self) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(
this: *const Self,
_: *const u8,
) -> Result<(), VerifyError> {
if unsafe { *this }.get() == 0 {
Err(VerifyError::NonZeroCheckError)
} else {
Ok(())
}
}
}
)+
}
}
impl_archive_self_nonzero_primitives! {
NonZeroI8;
NonZeroU8;
}
#[cfg(any(
all(not(feature = "big_endian"), target_endian = "little"),
all(feature = "big_endian", target_endian = "big"),
))]
const MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE: bool = true;
#[cfg(any(
all(feature = "big_endian", target_endian = "little"),
all(not(feature = "big_endian"), target_endian = "big"),
))]
const MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE: bool = false;
macro_rules! impl_multibyte_primitives {
($($archived:ident: $type:ty,)+) => {
$(
impl Serialize for $type {
type Archived = $archived;
const ALIGN: Alignment = Alignment::of::<Self::Archived>();
const COPY_OPTIMIZATION: bool = MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self::Archived>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(Self::Archived::from_native(*self)) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(
_: *const Self::Archived,
_: *const u8
) -> Result<(), VerifyError> {
Ok(())
}
}
)+
};
}
impl_multibyte_primitives! {
ArchivedI16: i16,
ArchivedI32: i32,
ArchivedI64: i64,
ArchivedI128: i128,
ArchivedU16: u16,
ArchivedU32: u32,
ArchivedU64: u64,
ArchivedU128: u128,
ArchivedF32: f32,
ArchivedF64: f64,
}
impl Serialize for char {
type Archived = ArchivedChar;
const ALIGN: Alignment = Alignment::of::<ArchivedChar>();
const COPY_OPTIMIZATION: bool = MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<ArchivedChar>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(Self::Archived::from_native(*self)) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(this: *const ArchivedChar, _: *const u8) -> Result<(), VerifyError> {
if char::try_from(unsafe { *this }.to_native() as u32).is_ok() {
Ok(())
} else {
Err(VerifyError::InvalidChar)
}
}
}
macro_rules! impl_multibyte_nonzero_primitives {
($($archived:ident: $type:ty,)+) => {
$(
impl Serialize for $type {
type Archived = $archived;
const ALIGN: Alignment = Alignment::of::<Self::Archived>();
const COPY_OPTIMIZATION: bool = MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self::Archived>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(Self::Archived::from_native(*self)) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
Ok(0)
}
#[inline]
unsafe fn verify(
this: *const Self::Archived,
_: *const u8
) -> Result<(), VerifyError> {
if unsafe { *this }.get() == 0 {
Err(VerifyError::NonZeroCheckError)
} else {
Ok(())
}
}
}
)+
};
}
impl_multibyte_nonzero_primitives! {
ArchivedNonZeroI16: NonZeroI16,
ArchivedNonZeroI32: NonZeroI32,
ArchivedNonZeroI64: NonZeroI64,
ArchivedNonZeroI128: NonZeroI128,
ArchivedNonZeroU16: NonZeroU16,
ArchivedNonZeroU32: NonZeroU32,
ArchivedNonZeroU64: NonZeroU64,
ArchivedNonZeroU128: NonZeroU128,
}
#[cfg(any(
all(target_pointer_width = "16", feature = "pointer_width_16"),
all(
target_pointer_width = "32",
not(any(feature = "pointer_width_16", feature = "pointer_width_64")),
),
all(target_pointer_width = "64", feature = "pointer_width_64"),
))]
const POINTER_WIDTH_EQUALS_ARCHIVED_POINTER_WIDTH: bool = true;
#[cfg(not(any(
all(target_pointer_width = "16", feature = "pointer_width_16"),
all(
target_pointer_width = "32",
not(any(feature = "pointer_width_16", feature = "pointer_width_64")),
),
all(target_pointer_width = "64", feature = "pointer_width_64"),
)))]
const POINTER_WIDTH_EQUALS_ARCHIVED_POINTER_WIDTH: bool = false;
macro_rules! impl_ptr_sized {
($($archived:ident: $type:ty, $fixed:ty,)+) => {
$(
impl Serialize for $type {
type Archived = $archived;
const ALIGN: Alignment = Alignment::of::<Self::Archived>();
const COPY_OPTIMIZATION: bool =
MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE
&& POINTER_WIDTH_EQUALS_ARCHIVED_POINTER_WIDTH;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self::Archived>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe { *out = MaybeUninit::new(Self::Archived::from_native(*self as _)) };
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
if (<$fixed>::MIN as _..=<$fixed>::MAX as _).contains(self) {
Ok(0)
} else {
Err(SerializeError::SizedOutOfRange)
}
}
#[inline]
unsafe fn verify(
_: *const Self::Archived,
_: *const u8
) -> Result<(), VerifyError> {
Ok(())
}
}
)+
};
}
impl_ptr_sized! {
ArchivedUsize: usize, FixedUsize,
ArchivedIsize: isize, FixedIsize,
}
macro_rules! impl_non_zero_ptr_sized {
($($archived:ident: $type:ty, $fixed:ty,)+) => {
$(
impl Serialize for $type {
type Archived = $archived;
const ALIGN: Alignment = Alignment::of::<Self::Archived>();
const COPY_OPTIMIZATION: bool =
MULTIBYTE_PRIMITIVES_ARE_TRIVIALLY_COPYABLE
&& POINTER_WIDTH_EQUALS_ARCHIVED_POINTER_WIDTH;
unsafe fn serialize(
&self,
out: *mut MaybeUninit<Self::Archived>,
_: *mut MaybeUninit<u8>,
) -> usize {
unsafe {
*out = MaybeUninit::new(Self::Archived::new_unchecked(self.get() as _))
};
0
}
fn serialized_size(&self, _: usize) -> Result<usize, SerializeError> {
if (<$fixed>::MIN as _..=<$fixed>::MAX as _).contains(&self.get()) {
Ok(0)
} else {
Err(SerializeError::SizedOutOfRange)
}
}
#[inline]
unsafe fn verify(
this: *const Self::Archived,
_: *const u8
) -> Result<(), VerifyError> {
if unsafe { *this }.get() == 0 {
Err(VerifyError::NonZeroCheckError)
} else {
Ok(())
}
}
}
)+
};
}
impl_non_zero_ptr_sized! {
ArchivedNonZeroUsize: NonZeroUsize, FixedUsize,
ArchivedNonZeroIsize: NonZeroIsize, FixedIsize,
}
#[cfg(test)]
mod tests {
use core::num::{
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
use crate::test::{roundtrip, roundtrip_with};
#[test]
fn roundtrip_portable_primitives() {
roundtrip(&());
roundtrip(&true);
roundtrip(&false);
roundtrip(&123i8);
roundtrip(&123u8);
roundtrip(&NonZeroI8::new(123i8).unwrap());
roundtrip(&NonZeroU8::new(123u8).unwrap());
}
#[test]
fn roundtrip_multibyte_primitives() {
roundtrip(&12345i16);
roundtrip(&1234567890i32);
roundtrip(&1234567890123456789i64);
roundtrip(&123456789012345678901234567890123456789i128);
roundtrip(&12345u16);
roundtrip(&1234567890u32);
roundtrip(&12345678901234567890u64);
roundtrip(&123456789012345678901234567890123456789u128);
roundtrip(&1234567f32);
roundtrip(&12345678901234f64);
roundtrip(&'x');
roundtrip(&'🥺');
roundtrip(&NonZeroI16::new(12345i16).unwrap());
roundtrip(&NonZeroI32::new(1234567890i32).unwrap());
roundtrip(&NonZeroI64::new(1234567890123456789i64).unwrap());
roundtrip(&NonZeroI128::new(123456789012345678901234567890123456789i128).unwrap());
roundtrip(&NonZeroU16::new(12345u16).unwrap());
roundtrip(&NonZeroU32::new(1234567890u32).unwrap());
roundtrip(&NonZeroU64::new(12345678901234567890u64).unwrap());
roundtrip(&NonZeroU128::new(123456789012345678901234567890123456789u128).unwrap());
}
#[test]
fn roundtrip_sizes() {
roundtrip_with(&12345isize, |a, b| {
assert_eq!(*a, isize::try_from(b.to_native()).unwrap())
});
roundtrip_with(&12345usize, |a, b| {
assert_eq!(*a, usize::try_from(b.to_native()).unwrap())
});
roundtrip_with(&NonZeroIsize::new(12345isize).unwrap(), |a, b| {
assert_eq!(*a, NonZeroIsize::try_from(b.to_native()).unwrap())
});
roundtrip_with(&NonZeroUsize::new(12345usize).unwrap(), |a, b| {
assert_eq!(*a, NonZeroUsize::try_from(b.to_native()).unwrap())
});
}
}