#![no_std]
use bytemuck::Pod;
use bytemuck::Zeroable;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodBool(pub u8);
impl PodBool {
pub const fn from_bool(b: bool) -> Self {
Self(if b { 1 } else { 0 })
}
pub const fn is_canonical(&self) -> bool {
self.0 == 0 || self.0 == 1
}
}
impl From<bool> for PodBool {
fn from(b: bool) -> Self {
Self::from_bool(b)
}
}
impl From<&bool> for PodBool {
fn from(b: &bool) -> Self {
Self(u8::from(*b))
}
}
impl From<&PodBool> for bool {
fn from(b: &PodBool) -> Self {
b.0 != 0
}
}
impl From<PodBool> for bool {
fn from(b: PodBool) -> Self {
b.0 != 0
}
}
#[macro_export]
macro_rules! impl_int_conversion {
($P:ty, $I:ty) => {
impl $P {
pub const fn from_primitive(n: $I) -> Self {
Self(n.to_le_bytes())
}
}
impl From<$I> for $P {
fn from(n: $I) -> Self {
Self::from_primitive(n)
}
}
impl From<$P> for $I {
fn from(pod: $P) -> Self {
Self::from_le_bytes(pod.0)
}
}
};
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodU16(pub [u8; 2]);
impl_int_conversion!(PodU16, u16);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodI16(pub [u8; 2]);
impl_int_conversion!(PodI16, i16);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodU32(pub [u8; 4]);
impl_int_conversion!(PodU32, u32);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodI32(pub [u8; 4]);
impl_int_conversion!(PodI32, i32);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodU64(pub [u8; 8]);
impl_int_conversion!(PodU64, u64);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodI64(pub [u8; 8]);
impl_int_conversion!(PodI64, i64);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodU128(pub [u8; 16]);
impl_int_conversion!(PodU128, u128);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodI128(pub [u8; 16]);
impl_int_conversion!(PodI128, i128);
#[cfg(test)]
extern crate std;
#[cfg(test)]
mod tests {
use bytemuck::try_from_bytes;
use super::*;
#[test]
fn pod_bool_roundtrip() {
for i in 0..=u8::MAX {
let value = *try_from_bytes::<PodBool>(&[i]).unwrap();
assert_eq!(i != 0, bool::from(value));
}
}
#[test]
fn pod_bool_non_canonical_equality_mismatch() {
let canonical_true = PodBool::from_bool(true);
let non_canonical_true = *try_from_bytes::<PodBool>(&[2]).unwrap();
assert!(bool::from(canonical_true));
assert!(bool::from(non_canonical_true));
assert_ne!(canonical_true, non_canonical_true);
assert!(canonical_true.is_canonical());
assert!(!non_canonical_true.is_canonical());
}
#[test]
fn pod_bool_is_canonical_boundary_values() {
assert!(PodBool(0).is_canonical());
assert!(PodBool(1).is_canonical());
assert!(!PodBool(2).is_canonical());
assert!(!PodBool(127).is_canonical());
assert!(!PodBool(255).is_canonical());
}
#[test]
fn pod_bool_from_bool_produces_canonical() {
assert!(PodBool::from_bool(false).is_canonical());
assert!(PodBool::from_bool(true).is_canonical());
assert!(PodBool::from(false).is_canonical());
assert!(PodBool::from(true).is_canonical());
}
#[test]
fn pod_bool_from_ref() {
let t = true;
let f = false;
assert_eq!(PodBool::from(&t), PodBool(1));
assert_eq!(PodBool::from(&f), PodBool(0));
}
#[test]
fn pod_bool_from_ref_roundtrip() {
let pod = PodBool(1);
assert!(bool::from(&pod));
let pod = PodBool(0);
assert!(!bool::from(&pod));
}
#[test]
fn pod_bool_default_is_false() {
let default = PodBool::default();
assert_eq!(default.0, 0);
assert!(!bool::from(default));
assert!(default.is_canonical());
}
#[test]
fn pod_u16_roundtrip() {
assert_eq!(1u16, u16::from(PodU16::from_primitive(1)));
}
#[test]
fn pod_i16_roundtrip() {
assert_eq!(-1i16, i16::from(PodI16::from_primitive(-1)));
}
#[test]
fn pod_u32_roundtrip() {
assert_eq!(7u32, u32::from(PodU32::from_primitive(7)));
}
#[test]
fn pod_i32_roundtrip() {
assert_eq!(-7i32, i32::from(PodI32::from_primitive(-7)));
}
#[test]
fn pod_u64_roundtrip() {
assert_eq!(9u64, u64::from(PodU64::from_primitive(9)));
}
#[test]
fn pod_i64_roundtrip() {
assert_eq!(-9i64, i64::from(PodI64::from_primitive(-9)));
}
#[test]
fn pod_u128_roundtrip() {
assert_eq!(11u128, u128::from(PodU128::from_primitive(11)));
}
#[test]
fn pod_i128_roundtrip() {
assert_eq!(-11i128, i128::from(PodI128::from_primitive(-11)));
}
#[test]
fn pod_u16_boundary_values() {
assert_eq!(0u16, u16::from(PodU16::from_primitive(0)));
assert_eq!(u16::MAX, u16::from(PodU16::from_primitive(u16::MAX)));
}
#[test]
fn pod_i16_boundary_values() {
assert_eq!(i16::MIN, i16::from(PodI16::from_primitive(i16::MIN)));
assert_eq!(i16::MAX, i16::from(PodI16::from_primitive(i16::MAX)));
assert_eq!(0i16, i16::from(PodI16::from_primitive(0)));
}
#[test]
fn pod_u32_boundary_values() {
assert_eq!(0u32, u32::from(PodU32::from_primitive(0)));
assert_eq!(u32::MAX, u32::from(PodU32::from_primitive(u32::MAX)));
}
#[test]
fn pod_i32_boundary_values() {
assert_eq!(i32::MIN, i32::from(PodI32::from_primitive(i32::MIN)));
assert_eq!(i32::MAX, i32::from(PodI32::from_primitive(i32::MAX)));
}
#[test]
fn pod_u64_boundary_values() {
assert_eq!(0u64, u64::from(PodU64::from_primitive(0)));
assert_eq!(u64::MAX, u64::from(PodU64::from_primitive(u64::MAX)));
}
#[test]
fn pod_i64_boundary_values() {
assert_eq!(i64::MIN, i64::from(PodI64::from_primitive(i64::MIN)));
assert_eq!(i64::MAX, i64::from(PodI64::from_primitive(i64::MAX)));
}
#[test]
fn pod_u128_boundary_values() {
assert_eq!(0u128, u128::from(PodU128::from_primitive(0)));
assert_eq!(u128::MAX, u128::from(PodU128::from_primitive(u128::MAX)));
}
#[test]
fn pod_i128_boundary_values() {
assert_eq!(i128::MIN, i128::from(PodI128::from_primitive(i128::MIN)));
assert_eq!(i128::MAX, i128::from(PodI128::from_primitive(i128::MAX)));
}
#[test]
fn pod_types_use_little_endian_byte_order() {
let u16_val = PodU16::from_primitive(0x0102);
assert_eq!(u16_val.0, [0x02, 0x01]);
let u32_val = PodU32::from_primitive(0x01020304);
assert_eq!(u32_val.0, [0x04, 0x03, 0x02, 0x01]);
let u64_val = PodU64::from_primitive(0x0102030405060708);
assert_eq!(u64_val.0, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn pod_types_bytemuck_from_bytes() {
let bytes_u16 = [0x39, 0x05]; let val = try_from_bytes::<PodU16>(&bytes_u16).unwrap();
assert_eq!(u16::from(*val), 1337);
let bytes_u32 = [0xEF, 0xBE, 0xAD, 0xDE]; let val = try_from_bytes::<PodU32>(&bytes_u32).unwrap();
assert_eq!(u32::from(*val), 0xDEAD_BEEF);
let bytes_i16 = [0xFF, 0xFF]; let val = try_from_bytes::<PodI16>(&bytes_i16).unwrap();
assert_eq!(i16::from(*val), -1);
}
#[test]
fn pod_default_is_zero() {
assert_eq!(u16::from(PodU16::default()), 0);
assert_eq!(i16::from(PodI16::default()), 0);
assert_eq!(u32::from(PodU32::default()), 0);
assert_eq!(i32::from(PodI32::default()), 0);
assert_eq!(u64::from(PodU64::default()), 0);
assert_eq!(i64::from(PodI64::default()), 0);
assert_eq!(u128::from(PodU128::default()), 0);
assert_eq!(i128::from(PodI128::default()), 0);
}
}