use core::marker::Copy;
#[cfg(feature = "alloc")]
use crate::map::ZeroMapKV;
#[cfg(feature = "alloc")]
use crate::{ZeroSlice, ZeroVec};
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 == 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)
}
}
}
pub fn as_ref(&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_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError> {
let size = size_of::<Self>();
debug_assert!(N == size_of::<U>());
if bytes.len() % size != 0 {
return Err(crate::ule::UleError::length::<Self>(bytes.len()));
}
bytes.chunks(size).try_for_each(|chunk| {
if chunk == <U as NicheBytes<N>>::NICHE_BIT_PATTERN {
Ok(())
} else {
U::validate_bytes(chunk)
}
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
#[allow(clippy::exhaustive_structs)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NichedOption<U, const N: usize>(pub Option<U>);
impl<U, const N: usize> Default for NichedOption<U, N> {
fn default() -> Self {
Self(None)
}
}
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))
}
}
#[cfg(feature = "alloc")]
impl<'a, T: AsULE + 'static, const N: usize> ZeroMapKV<'a> for NichedOption<T, N>
where
T::ULE: NicheBytes<N>,
{
type Container = ZeroVec<'a, NichedOption<T, N>>;
type Slice = ZeroSlice<NichedOption<T, N>>;
type GetType = <NichedOption<T, N> as AsULE>::ULE;
type OwnedType = Self;
}
impl<T, const N: usize> IntoIterator for NichedOption<T, N> {
type IntoIter = <Option<T> as IntoIterator>::IntoIter;
type Item = T;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}