use core::{marker::Copy, mem::size_of};
use super::{AsULE, ULE};
pub trait NicheBytes<const N: usize> {
const NICHE_BIT_PATTERN: [u8; N];
}
#[repr(C)]
pub union NichedOptionULE<U: NicheBytes<N> + ULE, const N: usize> {
niche: [u8; N],
valid: U,
}
impl<U: NicheBytes<N> + ULE + core::fmt::Debug, const N: usize> core::fmt::Debug
for NichedOptionULE<U, N>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.get().fmt(f)
}
}
impl<U: NicheBytes<N> + ULE, const N: usize> NichedOptionULE<U, N> {
pub fn new(opt: Option<U>) -> Self {
assert!(N == core::mem::size_of::<U>());
match opt {
Some(u) => Self { valid: u },
None => Self {
niche: <U as NicheBytes<N>>::NICHE_BIT_PATTERN,
},
}
}
pub fn get(self) -> Option<U> {
unsafe {
if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
None
} else {
Some(self.valid)
}
}
}
}
impl<U: NicheBytes<N> + ULE, const N: usize> Copy for NichedOptionULE<U, N> {}
impl<U: NicheBytes<N> + ULE, const N: usize> Clone for NichedOptionULE<U, N> {
fn clone(&self) -> Self {
*self
}
}
impl<U: NicheBytes<N> + ULE + PartialEq, const N: usize> PartialEq for NichedOptionULE<U, N> {
fn eq(&self, other: &Self) -> bool {
self.get().eq(&other.get())
}
}
impl<U: NicheBytes<N> + ULE + Eq, const N: usize> Eq for NichedOptionULE<U, N> {}
unsafe impl<U: NicheBytes<N> + ULE, const N: usize> ULE for NichedOptionULE<U, N> {
fn validate_byte_slice(bytes: &[u8]) -> Result<(), crate::ZeroVecError> {
let size = size_of::<Self>();
debug_assert!(N == core::mem::size_of::<U>());
if bytes.len() % size != 0 {
return Err(crate::ZeroVecError::length::<Self>(bytes.len()));
}
bytes.chunks(size).try_for_each(|chunk| {
if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
Ok(())
} else {
U::validate_byte_slice(chunk)
}
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
#[non_exhaustive]
pub struct NichedOption<U, const N: usize>(pub Option<U>);
impl<U, const N: usize> NichedOption<U, N> {
pub const fn new(o: Option<U>) -> Self {
Self(o)
}
}
impl<U, const N: usize> Default for NichedOption<U, N> {
fn default() -> Self {
Self(None)
}
}
impl<U, const N: usize> From<Option<U>> for NichedOption<U, N> {
fn from(o: Option<U>) -> Self {
Self(o)
}
}
impl<U: AsULE, const N: usize> AsULE for NichedOption<U, N>
where
U::ULE: NicheBytes<N>,
{
type ULE = NichedOptionULE<U::ULE, N>;
fn to_unaligned(self) -> Self::ULE {
NichedOptionULE::new(self.0.map(U::to_unaligned))
}
fn from_unaligned(unaligned: Self::ULE) -> Self {
Self(unaligned.get().map(U::from_unaligned))
}
}