//! A table entry that is empty or holds a value, like `Option`, but whose //! empty state is guaranteed to be all zero bytes. The kernel's tables are //! one static; if every empty entry is zero, the whole static starts zero //! and costs nothing in the kernel image (`.bss`). `Option` makes no such //! promise (its `None` is whatever the compiler picks). /// `#[repr(u8)]` fixes the layout: a tag byte, `Empty` being 0, then the /// value (left uninitialised when empty). #[cfg(not(kani))] #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Slot { #[default] Empty = 0, Full(T) = 1, } /// For the proofs, the same behaviour over `Option`: only the byte layout /// differs, which no proof depends on, and CBMC is far faster on `Option` /// than on an enum with an uninitialised payload. #[cfg(kani)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Slot(Option); #[cfg(kani)] impl Default for Slot { fn default() -> Self { Self(None) } } #[cfg(kani)] #[allow(non_upper_case_globals, non_snake_case)] impl Slot { pub const Empty: Self = Self(None); pub const fn Full(value: T) -> Self { Self(Some(value)) } pub const fn is_full(&self) -> bool { self.0.is_some() } pub const fn as_ref(&self) -> Option<&T> { self.0.as_ref() } pub fn as_mut(&mut self) -> Option<&mut T> { self.0.as_mut() } pub fn take(&mut self) -> Option { self.0.take() } pub fn set(&mut self, value: T) { self.0 = Some(value); } } #[cfg(kani)] impl Slot { pub const fn get(&self) -> Option { self.0 } } #[cfg(kani)] impl From> for Slot { fn from(o: Option) -> Self { Self(o) } } #[cfg(not(kani))] impl Slot { pub const fn is_full(&self) -> bool { matches!(self, Slot::Full(_)) } pub const fn as_ref(&self) -> Option<&T> { match self { Slot::Full(v) => Some(v), Slot::Empty => None, } } pub fn as_mut(&mut self) -> Option<&mut T> { match self { Slot::Full(v) => Some(v), Slot::Empty => None, } } /// The value, leaving the slot empty. pub fn take(&mut self) -> Option { match core::mem::replace(self, Slot::Empty) { Slot::Full(v) => Some(v), Slot::Empty => None, } } pub fn set(&mut self, value: T) { *self = Slot::Full(value); } } #[cfg(not(kani))] impl Slot { pub const fn get(&self) -> Option { match self { Slot::Full(v) => Some(*v), Slot::Empty => None, } } } #[cfg(not(kani))] impl From> for Slot { fn from(o: Option) -> Self { match o { Some(v) => Slot::Full(v), None => Slot::Empty, } } } #[cfg(test)] mod tests { use super::*; #[test] fn a_slot_behaves_like_an_option() { let mut s: Slot = Slot::Empty; assert!(!s.is_full() && s.get().is_none()); s.set(5); *s.as_mut().unwrap() += 1; assert_eq!(s.get(), Some(6)); assert_eq!(s.take(), Some(6)); assert_eq!(s, Slot::Empty); assert_eq!(Slot::from(Some(1)), Slot::Full(1)); } }