use core::mem::MaybeUninit;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PodOption<T: Copy, const PFX: usize = 1> {
tag: [u8; PFX],
value: MaybeUninit<T>,
}
const _: () = assert!(core::mem::align_of::<PodOption<u8>>() == 1);
const _: () = assert!(core::mem::align_of::<PodOption<u8, 4>>() == 1);
const _: () = assert!(core::mem::size_of::<PodOption<[u8; 32], 4>>() == 36);
impl<T: Copy, const PFX: usize> PodOption<T, PFX> {
const _PFX_CHECK: () = assert!(
PFX == 1 || PFX == 2 || PFX == 4,
"PodOption<T, PFX>: PFX must be 1, 2, or 4"
);
#[inline(always)]
fn decode_tag(&self) -> u32 {
#[allow(clippy::let_unit_value)]
let _ = Self::_PFX_CHECK;
match PFX {
1 => self.tag[0] as u32,
2 => u16::from_le_bytes([self.tag[0], self.tag[1]]) as u32,
_ => u32::from_le_bytes([self.tag[0], self.tag[1], self.tag[2], self.tag[3]]),
}
}
#[inline(always)]
fn encode_tag(v: u32) -> [u8; PFX] {
#[allow(clippy::let_unit_value)]
let _ = Self::_PFX_CHECK;
let mut buf = [0u8; PFX];
match PFX {
1 => buf[0] = v as u8,
2 => {
let bytes = (v as u16).to_le_bytes();
buf[0] = bytes[0];
buf[1] = bytes[1];
}
_ => {
let bytes = v.to_le_bytes();
buf[..4].copy_from_slice(&bytes);
}
}
buf
}
#[inline(always)]
pub fn none() -> Self {
Self {
tag: [0u8; PFX],
value: MaybeUninit::zeroed(),
}
}
#[inline(always)]
pub fn some(value: T) -> Self {
Self {
tag: Self::encode_tag(1),
value: MaybeUninit::new(value),
}
}
#[inline(always)]
pub fn is_some(&self) -> bool {
self.decode_tag() == 1
}
#[inline(always)]
pub fn is_none(&self) -> bool {
!self.is_some()
}
#[inline(always)]
pub fn get(&self) -> Option<T> {
if self.is_some() {
Some(unsafe { self.value.assume_init() })
} else {
None
}
}
#[inline(always)]
pub fn get_ref(&self) -> Option<&T> {
if self.is_some() {
Some(unsafe { self.value.assume_init_ref() })
} else {
None
}
}
#[inline(always)]
pub fn value_unchecked(&self) -> &T {
unsafe { self.value.assume_init_ref() }
}
#[inline(always)]
pub fn set(&mut self, value: Option<T>) {
match value {
Some(v) => {
self.tag = Self::encode_tag(1);
self.value = MaybeUninit::new(v);
}
None => {
self.tag = [0u8; PFX];
self.value = MaybeUninit::zeroed();
}
}
}
pub fn raw_tag(&self) -> u32 {
self.decode_tag()
}
#[inline(always)]
pub fn tag_valid(&self) -> bool {
self.raw_tag() <= 1
}
#[inline(always)]
pub unsafe fn assume_init_ref(&self) -> &T {
self.value.assume_init_ref()
}
pub fn take(&mut self) -> Option<T> {
let result = self.get();
self.tag = [0u8; PFX];
self.value = MaybeUninit::zeroed();
result
}
pub fn replace(&mut self, value: T) -> Option<T> {
let old = self.get();
self.tag = Self::encode_tag(1);
self.value = MaybeUninit::new(value);
old
}
pub fn clear(&mut self) {
self.tag = [0u8; PFX];
self.value = MaybeUninit::zeroed();
}
pub fn unwrap_or(self, default: T) -> T {
match self.get() {
Some(v) => v,
None => default,
}
}
pub fn map_or<U>(&self, default: U, f: impl FnOnce(T) -> U) -> U {
match self.get() {
Some(v) => f(v),
None => default,
}
}
}
impl<T: Copy, const PFX: usize> Default for PodOption<T, PFX> {
fn default() -> Self {
Self::none()
}
}
impl<T: Copy + PartialEq, const PFX: usize> PartialEq for PodOption<T, PFX> {
fn eq(&self, other: &Self) -> bool {
match (self.get(), other.get()) {
(Some(a), Some(b)) => a == b,
(None, None) => true,
_ => false,
}
}
}
impl<T: Copy + Eq, const PFX: usize> Eq for PodOption<T, PFX> {}
impl<T: Copy + PartialEq, const PFX: usize> PartialEq<Option<T>> for PodOption<T, PFX> {
fn eq(&self, other: &Option<T>) -> bool {
match (self.get(), other) {
(Some(a), Some(b)) => a == *b,
(None, None) => true,
_ => false,
}
}
}
impl<T: Copy + core::fmt::Debug, const PFX: usize> core::fmt::Debug for PodOption<T, PFX> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.get() {
Some(v) => write!(f, "Some({:?})", v),
None => write!(f, "None"),
}
}
}
#[cfg(all(kani, feature = "kani"))]
mod kani_proofs {
use super::*;
macro_rules! pfx_proofs {
($base:ident, $body:expr) => {
mod $base {
use super::*;
#[kani::proof]
fn pfx1() {
const PFX: usize = 1;
$body
}
#[kani::proof]
fn pfx2() {
const PFX: usize = 2;
$body
}
#[kani::proof]
fn pfx4() {
const PFX: usize = 4;
$body
}
}
};
}
pfx_proofs!(some_roundtrip, {
let v: u8 = kani::any();
let pod = PodOption::<u8, PFX>::some(v);
assert!(pod.is_some());
assert!(!pod.is_none());
assert!(pod.get() == Some(v), "some roundtrip must preserve value");
});
pfx_proofs!(none_roundtrip, {
let pod = PodOption::<u8, PFX>::none();
assert!(pod.is_none());
assert!(!pod.is_some());
assert!(pod.get() == None, "none must return None");
});
pfx_proofs!(set_some_then_get, {
let v: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::none();
pod.set(Some(v));
assert!(
pod.get() == Some(v),
"set(Some(v)) then get() must return Some(v)"
);
});
pfx_proofs!(set_none_then_get, {
let v: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::some(v);
pod.set(None);
assert!(pod.get() == None, "set(None) then get() must return None");
});
pfx_proofs!(take_returns_value_and_clears, {
let v: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::some(v);
let taken = pod.take();
assert!(taken == Some(v), "take must return the value");
assert!(pod.is_none(), "take must clear to None");
});
pfx_proofs!(replace_returns_old, {
let old: u8 = kani::any();
let new: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::some(old);
let returned = pod.replace(new);
assert!(returned == Some(old), "replace must return old value");
assert!(pod.get() == Some(new), "replace must set new value");
});
pfx_proofs!(replace_on_none_returns_none, {
let v: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::none();
let returned = pod.replace(v);
assert!(returned == None, "replace on None must return None");
assert!(pod.get() == Some(v), "replace must set value");
});
pfx_proofs!(unwrap_or_some, {
let v: u8 = kani::any();
let default: u8 = kani::any();
let pod = PodOption::<u8, PFX>::some(v);
assert!(
pod.unwrap_or(default) == v,
"unwrap_or on Some must return value"
);
});
pfx_proofs!(unwrap_or_none, {
let default: u8 = kani::any();
let pod = PodOption::<u8, PFX>::none();
assert!(
pod.unwrap_or(default) == default,
"unwrap_or on None must return default"
);
});
pfx_proofs!(default_is_none, {
let pod = PodOption::<u8, PFX>::default();
assert!(pod.is_none(), "default must be None");
assert!(pod.raw_tag() == 0, "default tag must be 0");
});
pfx_proofs!(clear_makes_none, {
let v: u8 = kani::any();
let mut pod = PodOption::<u8, PFX>::some(v);
pod.clear();
assert!(pod.is_none(), "clear must make None");
});
pfx_proofs!(get_ref_borrow, {
let v: u8 = kani::any();
let pod = PodOption::<u8, PFX>::some(v);
assert!(pod.get_ref() == Some(&v), "get_ref must borrow the value");
let none_pod = PodOption::<u8, PFX>::none();
assert!(none_pod.get_ref().is_none(), "get_ref on None must be None");
});
#[kani::proof]
fn invalid_tag_pfx1() {
let tag: u8 = kani::any();
kani::assume(tag != 0 && tag != 1);
let mut buf = [0u8; 2]; buf[0] = tag;
buf[1] = kani::any();
let pod = unsafe { &*(buf.as_ptr() as *const PodOption<u8, 1>) };
assert!(!pod.is_some(), "invalid tag must not be Some");
assert!(pod.get() == None, "invalid tag must return None from get()");
}
#[kani::proof]
fn tag_rejection_pfx4() {
let tag: u32 = kani::any();
kani::assume(tag > 1);
let mut buf = [0u8; 5]; buf[..4].copy_from_slice(&tag.to_le_bytes());
buf[4] = kani::any();
let pod = unsafe { &*(buf.as_ptr() as *const PodOption<u8, 4>) };
assert!(!pod.is_some(), "tag > 1 must not be Some");
}
#[kani::proof]
fn none_zeroed_pfx4() {
let pod = PodOption::<u8, 4>::none();
let bytes = unsafe {
core::slice::from_raw_parts(&pod as *const _ as *const u8, core::mem::size_of_val(&pod))
};
for &b in bytes {
assert!(b == 0, "none() must produce all-zero bytes");
}
}
#[kani::proof]
fn value_unchecked_reference_sound() {
let v: u8 = kani::any();
let pod = PodOption::<u8, 1>::some(v);
let r = pod.value_unchecked();
assert!(*r == v, "value_unchecked on Some must return the value");
let none_pod = PodOption::<u8, 1>::none();
let r2 = none_pod.value_unchecked();
assert!(*r2 == 0, "value_unchecked on None returns zeroed bytes");
}
}