#![no_std]
use core::mem::MaybeUninit;
#[derive(Debug)]
#[repr(C)]
pub struct Optional<T> {
val: MaybeUninit<T>,
is_some: bool,
}
const _: () = {
const fn check<T, const SIZE: usize>() {
assert!(size_of::<Optional<T>>() == SIZE);
}
check::<u8, 2>();
check::<u16, 4>();
check::<u32, 8>();
check::<i8, 2>();
check::<i16, 4>();
check::<i32, 8>();
check::<f32, 8>();
};
impl<T> Optional<T> {
pub const fn some(val: T) -> Self {
Self {
val: MaybeUninit::new(val),
is_some: true,
}
}
pub const fn none() -> Self {
Self {
val: MaybeUninit::uninit(),
is_some: false,
}
}
pub fn from_opt(v: Option<T>) -> Self {
match v {
Some(v) => Self::some(v),
None => Self::none(),
}
}
pub const fn to_opt(self) -> Option<T> {
if self.is_some {
Some(unsafe { self.val.assume_init() })
} else {
None
}
}
pub const fn is_some(&self) -> bool {
self.is_some
}
pub const fn is_none(&self) -> bool {
!self.is_some
}
}
impl<T> From<Option<T>> for Optional<T> {
fn from(v: Option<T>) -> Self {
Self::from_opt(v)
}
}
impl<T> From<Optional<T>> for Option<T> {
fn from(o: Optional<T>) -> Self {
o.to_opt()
}
}
impl<T> From<T> for Optional<T> {
fn from(v: T) -> Self {
Self::some(v)
}
}
trait Sealed {}
#[expect(private_bounds)]
pub trait Raw: Sealed {
type T;
}
macro_rules! to_packable {
(u8, $val:expr) => {
$val as u16
};
(u16, $val:expr) => {
$val as u32
};
(u32, $val:expr) => {
$val as u64
};
(i8, $val:expr) => {
to_packable!(u8, $val)
};
(i16, $val:expr) => {
to_packable!(u16, $val)
};
(i32, $val:expr) => {
to_packable!(u32, $val)
};
(f32, $val:expr) => {
$val.to_bits() as u64
};
}
macro_rules! from_packable {
(u8, $val:expr) => {
$val as u8
};
(u16, $val:expr) => {
$val as u16
};
(u32, $val:expr) => {
$val as u32
};
(i8, $val:expr) => {
$val as i8
};
(i16, $val:expr) => {
$val as i16
};
(i32, $val:expr) => {
$val as i32
};
(f32, $val:expr) => {
f32::from_bits($val as _)
};
}
macro_rules! impl_raw {
($T:tt, $Packed:ty) => {
impl Sealed for $T {}
impl Raw for $T {
type T = $Packed;
}
impl Optional<$T> {
pub const fn into_raw(self) -> $Packed {
if self.is_some {
let v = unsafe { self.val.assume_init() };
let v = to_packable!($T, v);
v | ((self.is_some as $Packed) << <$Packed>::BITS / 2)
} else {
0
}
}
pub const fn from_raw(v: $Packed) -> Self {
let is_some = v & (1 << <$Packed>::BITS / 2) != 0;
if is_some {
Self::some(from_packable!($T, v))
} else {
Self::none()
}
}
}
};
}
impl_raw!(u8, u16);
impl_raw!(u16, u32);
impl_raw!(u32, u64);
impl_raw!(i8, u16);
impl_raw!(i16, u32);
impl_raw!(i32, u64);
impl_raw!(f32, u64);