use core::mem::{offset_of, size_of};
use alloc::string::ToString;
use anyhow::Result;
use crate::{OwnedBuf, Ref, ZeroCopy};
fn load_bytes<T>(bytes: &[u8]) -> Result<T, crate::Error>
where
T: ZeroCopy + Copy,
{
let mut buf = OwnedBuf::with_alignment::<u64>();
buf.extend_from_slice(bytes).unwrap();
buf.load(Ref::<T>::new(0u32)).copied()
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed(4))]
#[zero_copy(crate)]
struct PackedBool {
a: u8,
b: bool,
c: u16,
d: u8,
}
#[test]
fn packed_n_layout() {
assert_eq!(offset_of!(PackedBool, a), 0);
assert_eq!(offset_of!(PackedBool, b), 1);
assert_eq!(offset_of!(PackedBool, c), 2);
assert_eq!(offset_of!(PackedBool, d), 4);
assert_eq!(size_of::<PackedBool>(), 6);
assert_eq!(offset_of!(PackedPad, a), 0);
assert_eq!(offset_of!(PackedPad, b), 1);
assert_eq!(offset_of!(PackedPad, c), 4);
assert_eq!(size_of::<PackedPad>(), 8);
}
#[test]
fn packed_n_validate_rejects_invalid_field() {
let Err(error) = load_bytes::<PackedBool>(&[0, 2, 0, 0, 0, 0]) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
}
#[test]
fn packed_n_validate_accepts_valid_field() {
let value = load_bytes::<PackedBool>(&[7, 1, 0x34, 0x12, 2, 0]).unwrap();
let (a, b, d) = (value.a, value.b, value.d);
assert_eq!((a, b, d), (7, true, 2));
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed(4))]
#[zero_copy(crate)]
struct PackedPad {
a: u8,
b: u8,
c: u32,
}
#[test]
fn packed_n_pad_preserves_fields() -> Result<()> {
const _: () = assert!(PackedPad::PADDED);
let mut buf = OwnedBuf::new();
buf.store(&PackedPad {
a: 1,
b: 2,
c: u32::from_ne_bytes([3, 4, 5, 6]),
})?;
assert_eq!(buf.as_slice(), &[1, 2, 0, 0, 3, 4, 5, 6]);
Ok(())
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C)]
#[zero_copy(crate)]
struct Inner {
x: u16,
y: bool,
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed)]
#[zero_copy(crate)]
struct PackedInner {
a: u8,
inner: Inner,
}
#[test]
fn nested_in_packed_layout() {
assert_eq!(offset_of!(Inner, x), 0);
assert_eq!(offset_of!(Inner, y), 2);
assert_eq!(size_of::<Inner>(), 4);
assert_eq!(offset_of!(PackedInner, inner), 1);
assert_eq!(size_of::<PackedInner>(), 5);
assert_eq!(offset_of!(PackedEnum, e), 1);
assert_eq!(size_of::<Enum>(), 6);
assert_eq!(size_of::<PackedEnum>(), 7);
assert_eq!(offset_of!(PackedEnum16, e), 1);
assert_eq!(size_of::<PackedEnum16>(), 4);
}
#[test]
fn nested_struct_in_packed_rejects_invalid_field() {
let Err(error) = load_bytes::<PackedInner>(&[0, 0, 0, 2, 0]) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
}
#[test]
fn nested_struct_in_packed_accepts_valid_field() {
let value = load_bytes::<PackedInner>(&[0, 0, 0, 1, 2]).unwrap();
let inner = value.inner;
assert!(inner.y);
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(u8)]
#[zero_copy(crate)]
enum Enum {
A(u16, bool),
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed)]
#[zero_copy(crate)]
struct PackedEnum {
a: u8,
e: Enum,
}
#[test]
fn nested_enum_in_packed_rejects_invalid_field() {
let Err(error) = load_bytes::<PackedEnum>(&[0, 0, 0, 0, 0, 2, 0]) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
}
#[test]
fn nested_enum_in_packed_accepts_valid_field() {
let value = load_bytes::<PackedEnum>(&[0, 0, 0, 0, 2, 1, 0]).unwrap();
let Enum::A(_, b) = value.e;
assert!(b);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, ZeroCopy)]
#[repr(u16)]
#[zero_copy(crate)]
enum Enum16 {
A = 0,
B = 1,
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed)]
#[zero_copy(crate)]
struct PackedEnum16 {
a: u8,
e: Enum16,
b: u8,
}
#[test]
fn nested_enum_discriminant_in_packed() {
let [d0, d1] = 1u16.to_ne_bytes();
let value = load_bytes::<PackedEnum16>(&[0, d0, d1, 0xff]).unwrap();
let e = value.e;
assert_eq!(e, Enum16::B);
let [d0, d1] = 5u16.to_ne_bytes();
assert!(load_bytes::<PackedEnum16>(&[0, d0, d1, 0]).is_err());
}
#[test]
fn unaligned_load_validates_relative_to_value() -> Result<()> {
let mut buf = OwnedBuf::with_alignment::<u64>();
buf.extend_from_slice(&[0, 0, 0, 2, 0])?;
let Err(error) = buf.load_at_unaligned::<Inner>(1) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
buf.clear();
buf.extend_from_slice(&[0, 0, 0, 1, 2])?;
assert!(buf.load_at_unaligned::<Inner>(1)?.y);
Ok(())
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed)]
#[zero_copy(crate)]
struct PackedRef {
a: u8,
r: Ref<[u8]>,
}
#[test]
fn nested_ref_in_packed() -> Result<()> {
assert_eq!(offset_of!(PackedRef, r), 1);
let mut buf = OwnedBuf::with_alignment::<u64>();
let slice = buf.store_slice(&[1u8, 2, 3])?;
let packed = buf.store(&PackedRef { a: 7, r: slice })?;
let packed = *buf.load(packed)?;
let r = packed.r;
assert_eq!(buf.load(r)?, &[1, 2, 3]);
Ok(())
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C)]
#[zero_copy(crate)]
struct IgnoredAligned {
a: u8,
#[zero_copy(ignore)]
_z: [u64; 0],
b: bool,
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(C, packed(2))]
#[zero_copy(crate)]
struct PackedIgnoredAligned {
a: u8,
#[zero_copy(ignore)]
_z: [u64; 0],
b: bool,
}
#[derive(Clone, Copy, ZeroCopy)]
#[repr(u8)]
#[zero_copy(crate)]
enum EnumIgnoredAligned {
A {
#[zero_copy(ignore)]
_z: [u64; 0],
b: bool,
},
}
#[test]
fn ignored_aligned_layout() {
assert_eq!(offset_of!(IgnoredAligned, b), 8);
assert_eq!(size_of::<IgnoredAligned>(), 16);
assert_eq!(offset_of!(PackedIgnoredAligned, b), 2);
assert_eq!(size_of::<PackedIgnoredAligned>(), 4);
assert_eq!(size_of::<EnumIgnoredAligned>(), 16);
}
#[test]
fn ignored_aligned_rejects_invalid_field() {
let mut bytes = [0u8; 16];
bytes[8] = 2;
let Err(error) = load_bytes::<IgnoredAligned>(&bytes) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
}
#[test]
fn ignored_aligned_accepts_valid_field() {
let mut bytes = [0u8; 16];
bytes[1] = 2;
bytes[8] = 1;
assert!(load_bytes::<IgnoredAligned>(&bytes).unwrap().b);
}
#[test]
fn ignored_aligned_pad_preserves_fields() -> Result<()> {
let mut buf = OwnedBuf::new();
buf.store(&IgnoredAligned {
a: 1,
_z: [],
b: true,
})?;
assert_eq!(
buf.as_slice(),
&[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
);
Ok(())
}
#[test]
fn packed_ignored_aligned_validates_field() {
let Err(error) = load_bytes::<PackedIgnoredAligned>(&[0, 0, 2, 0]) else {
panic!("expected an invalid bool error");
};
assert_eq!(error.to_string(), "Invalid bool representation 2");
let value = load_bytes::<PackedIgnoredAligned>(&[0, 2, 1, 0]).unwrap();
let b = value.b;
assert!(b);
}
#[test]
fn packed_ignored_aligned_pad_preserves_fields() -> Result<()> {
let mut buf = OwnedBuf::new();
buf.store(&PackedIgnoredAligned {
a: 1,
_z: [],
b: true,
})?;
assert_eq!(buf.as_slice(), &[1, 0, 1, 0]);
Ok(())
}
#[test]
fn enum_ignored_aligned_validates_field() {
let mut bytes = [0u8; 16];
bytes[8] = 2;
assert!(load_bytes::<EnumIgnoredAligned>(&bytes).is_err());
let mut bytes = [0u8; 16];
bytes[1] = 2;
bytes[8] = 1;
let EnumIgnoredAligned::A { b, .. } = load_bytes::<EnumIgnoredAligned>(&bytes).unwrap();
assert!(b);
}
#[test]
fn enum_ignored_aligned_pad_preserves_fields() -> Result<()> {
let mut buf = OwnedBuf::new();
buf.store(&EnumIgnoredAligned::A { _z: [], b: true })?;
assert_eq!(
buf.as_slice(),
&[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
);
Ok(())
}
#[allow(clippy::enum_clike_unportable_variant)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, ZeroCopy)]
#[repr(usize)]
#[zero_copy(crate)]
enum EnumUsize {
A = 1,
B = usize::MAX,
}
#[test]
fn repr_usize_enum() -> Result<()> {
const _: () = assert!(size_of::<EnumUsize>() == size_of::<usize>());
let mut buf = OwnedBuf::new();
let a = buf.store(&EnumUsize::A)?;
let b = buf.store(&EnumUsize::B)?;
assert_eq!(buf.load(a)?, &EnumUsize::A);
assert_eq!(buf.load(b)?, &EnumUsize::B);
assert!(load_bytes::<EnumUsize>(&2usize.to_ne_bytes()).is_err());
Ok(())
}