use core::fmt;
use crate::PinaPodError;
use crate::traits::ZcElem;
use crate::traits::ZcField;
use crate::traits::ZcValidate;
macro_rules! define_pod_float {
($(#[$struct_doc:meta])* $name:ident, $native:ty, $bits:ty, $size:expr) => {
$(#[$struct_doc])*
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
#[cfg_attr(
feature = "wincode",
derive(wincode::SchemaWrite, wincode::SchemaRead)
)]
pub struct $name([u8; $size]);
impl $name {
pub const ZERO: Self = Self([0u8; $size]);
pub const MIN_POSITIVE: Self = Self(<$native>::MIN_POSITIVE.to_bits().to_le_bytes());
pub const MAX: Self = Self(<$native>::MAX.to_bits().to_le_bytes());
#[inline(always)]
pub const fn new_from_array(array: [u8; $size]) -> Self {
Self(array)
}
#[inline(always)]
pub fn get(&self) -> $native {
<$native>::from_bits(<$bits>::from_le_bytes(self.0))
}
#[inline(always)]
pub fn set(&mut self, value: $native) {
self.0 = value.to_bits().to_le_bytes();
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
self.0 == [0u8; $size]
}
#[inline(always)]
pub const fn to_bits(&self) -> $bits {
<$bits>::from_le_bytes(self.0)
}
#[inline(always)]
pub const fn set_bits(&mut self, bits: $bits) {
self.0 = bits.to_le_bytes();
}
}
impl From<$native> for $name {
#[inline(always)]
fn from(value: $native) -> Self {
Self(value.to_bits().to_le_bytes())
}
}
impl From<$name> for $native {
#[inline(always)]
fn from(value: $name) -> Self {
value.get()
}
}
impl PartialEq for $name {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for $name {}
impl core::hash::Hash for $name {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl fmt::Binary for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Binary::fmt(&self.to_bits(), f)
}
}
impl fmt::LowerHex for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.to_bits(), f)
}
}
impl fmt::UpperHex for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(&self.to_bits(), f)
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get().fmt(f)
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.get(), f)
}
}
impl AsRef<[u8]> for $name {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl ZcValidate for $name {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> {
Ok(())
}
#[inline(always)]
fn validate_array<const N: usize>(_: &[Self; N]) -> Result<(), PinaPodError> {
Ok(())
}
#[inline(always)]
fn validate_slice(_: &[Self]) -> Result<(), PinaPodError> {
Ok(())
}
}
unsafe impl ZcElem for $name {}
unsafe impl ZcField for $name {
type Pod = Self;
}
unsafe impl ZcField for $native {
type Pod = $name;
}
const _: () = assert!(core::mem::align_of::<$name>() == 1);
const _: () = assert!(core::mem::size_of::<$name>() == $size);
const _: () = assert!(core::mem::size_of::<$name>() == core::mem::size_of::<$native>());
};
}
define_pod_float!(
PodF32,
f32,
u32,
4
);
define_pod_float!(
PodF64,
f64,
u64,
8
);
#[cfg(all(kani, feature = "kani"))]
mod kani_proofs {
use super::*;
macro_rules! prove_pod_float {
($pod:ident, $bits:ty, $module:ident) => {
mod $module {
use super::super::*;
#[kani::proof]
fn set_bits_then_read_preserves_the_pattern() {
let bits: $bits = kani::any();
let mut pod = $pod::ZERO;
pod.set_bits(bits);
assert!(pod.to_bits() == bits);
}
#[kani::proof]
fn new_from_array_is_little_endian() {
let bytes: [u8; core::mem::size_of::<$pod>()] = kani::any();
let pod = $pod::new_from_array(bytes);
assert!(pod.to_bits() == <$bits>::from_le_bytes(bytes));
assert!(pod.as_ref() == &bytes);
}
#[kani::proof]
fn zero_is_the_all_zero_pattern() {
assert!($pod::ZERO.is_zero());
assert!($pod::ZERO.to_bits() == 0);
assert!(!$pod::new_from_array([1; core::mem::size_of::<$pod>()]).is_zero());
}
}
};
}
prove_pod_float!(PodF32, u32, f32_proofs);
prove_pod_float!(PodF64, u64, f64_proofs);
}