Skip to main content

rostl_primitives/
ooption.rs

1//! Implements an option type with constant-time conditional move operations.
2
3use crate::traits::Cmov;
4use bytemuck::{Pod, Zeroable};
5
6/// An alternative option implementation that is easier to use in constant-time algorithms.
7/// This type is designed to be used in scenarios where you need to conditionally move
8/// between two values without leaking information about which value is present.
9#[repr(C)]
10#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Zeroable)]
11pub struct OOption<T>
12where
13  T: Cmov + Pod + Zeroable,
14{
15  /// The underlying value of the option.
16  pub value: T,
17  /// A boolean flag indicating whether the option contains a value.
18  pub is_some: bool,
19}
20unsafe impl<T> Pod for OOption<T> where T: Cmov + Pod + Zeroable {}
21
22impl<T> OOption<T>
23where
24  T: Cmov + Pod + Zeroable,
25{
26  /// Creates a new `OOption` with the given value and presence flag.
27  pub const fn new(value: T, is_some: bool) -> Self {
28    Self { value, is_some }
29  }
30
31  /// Returns whether the option contains a value.
32  pub const fn is_some(&self) -> bool {
33    self.is_some
34  }
35
36  /// Returns `value` if `is_some()`, otherwise panics.
37  pub fn unwrap(&self) -> T {
38    assert!(self.is_some(), "Called `unwrap` on an `OOption` that is `None`.");
39    let mut ret = T::zeroed();
40    ret.cmov(&self.value, self.is_some);
41    ret
42  }
43}
44
45impl<T> OOption<T>
46where
47  T: Cmov + Pod + Zeroable + Default,
48{
49  /// Returns the contained value if present, otherwise returns a default value.
50  pub fn unwrap_or_default(&self) -> T {
51    let mut ret = T::default();
52    ret.cmov(&self.value, self.is_some);
53    ret
54  }
55}
56
57impl<T> Cmov for OOption<T>
58where
59  T: Cmov + Pod + Zeroable,
60{
61  fn cmov(&mut self, other: &Self, choice: bool) {
62    self.value.cmov(&other.value, choice);
63    self.is_some.cmov(&other.is_some, choice);
64  }
65
66  fn cxchg(&mut self, other: &mut Self, choice: bool) {
67    let tmp_value = self.value;
68    self.value.cmov(&other.value, choice);
69    other.value.cmov(&tmp_value, choice);
70
71    let tmp_is_some = self.is_some;
72    self.is_some.cmov(&other.is_some, choice);
73    other.is_some.cmov(&tmp_is_some, choice);
74  }
75}