|
1 | | -use subtle::Choice; |
| 1 | +use subtle::{Choice, CtOption}; |
2 | 2 |
|
3 | | -use crate::Word; |
| 3 | +use crate::{NonZero, Uint, Word}; |
4 | 4 |
|
5 | 5 | /// A boolean value returned by constant-time `const fn`s. |
6 | 6 | // TODO: should be replaced by `subtle::Choice` or `CtOption` |
@@ -174,6 +174,125 @@ impl PartialEq for ConstChoice { |
174 | 174 | } |
175 | 175 | } |
176 | 176 |
|
| 177 | +/// An equivalent of `subtle::CtOption` usable in a `const fn` context. |
| 178 | +#[derive(Debug, Clone)] |
| 179 | +pub struct ConstOption<T> { |
| 180 | + value: T, |
| 181 | + is_some: ConstChoice, |
| 182 | +} |
| 183 | + |
| 184 | +impl<T> ConstOption<T> { |
| 185 | + #[inline] |
| 186 | + pub(crate) const fn new(value: T, is_some: ConstChoice) -> Self { |
| 187 | + Self { value, is_some } |
| 188 | + } |
| 189 | + |
| 190 | + #[inline] |
| 191 | + pub(crate) const fn some(value: T) -> Self { |
| 192 | + Self { |
| 193 | + value, |
| 194 | + is_some: ConstChoice::TRUE, |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + #[inline] |
| 199 | + pub(crate) const fn none(dummy_value: T) -> Self { |
| 200 | + Self { |
| 201 | + value: dummy_value, |
| 202 | + is_some: ConstChoice::FALSE, |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + /// Returns a reference to the contents of this structure. |
| 207 | + /// |
| 208 | + /// **Note:** if the second element is `None`, the first value may take any value. |
| 209 | + #[inline] |
| 210 | + pub(crate) const fn components_ref(&self) -> (&T, ConstChoice) { |
| 211 | + // Since Rust is not smart enough to tell that we would be moving the value, |
| 212 | + // and hence no destructors will be called, we have to return a reference instead. |
| 213 | + // See https://github.com/rust-lang/rust/issues/66753 |
| 214 | + (&self.value, self.is_some) |
| 215 | + } |
| 216 | + |
| 217 | + /// Returns a true [`ConstChoice`] if this value is `Some`. |
| 218 | + #[inline] |
| 219 | + pub const fn is_some(&self) -> ConstChoice { |
| 220 | + self.is_some |
| 221 | + } |
| 222 | + |
| 223 | + /// Returns a true [`ConstChoice`] if this value is `None`. |
| 224 | + #[inline] |
| 225 | + pub const fn is_none(&self) -> ConstChoice { |
| 226 | + self.is_some.not() |
| 227 | + } |
| 228 | + |
| 229 | + /// This returns the underlying value but panics if it is not `Some`. |
| 230 | + #[inline] |
| 231 | + pub fn unwrap(self) -> T { |
| 232 | + assert!(self.is_some.is_true_vartime()); |
| 233 | + self.value |
| 234 | + } |
| 235 | +} |
| 236 | + |
| 237 | +impl<T> From<ConstOption<T>> for CtOption<T> { |
| 238 | + #[inline] |
| 239 | + fn from(value: ConstOption<T>) -> Self { |
| 240 | + CtOption::new(value.value, value.is_some.into()) |
| 241 | + } |
| 242 | +} |
| 243 | + |
| 244 | +// Need specific implementations to work around the |
| 245 | +// "destructors cannot be evaluated at compile-time" error |
| 246 | +// See https://github.com/rust-lang/rust/issues/66753 |
| 247 | + |
| 248 | +impl<const LIMBS: usize> ConstOption<Uint<LIMBS>> { |
| 249 | + /// This returns the underlying value if it is `Some` or the provided value otherwise. |
| 250 | + #[inline] |
| 251 | + pub const fn unwrap_or(self, def: Uint<LIMBS>) -> Uint<LIMBS> { |
| 252 | + Uint::select(&def, &self.value, self.is_some) |
| 253 | + } |
| 254 | + |
| 255 | + /// Returns the contained value, consuming the `self` value. |
| 256 | + /// |
| 257 | + /// # Panics |
| 258 | + /// |
| 259 | + /// Panics if the value is none with a custom panic message provided by |
| 260 | + /// `msg`. |
| 261 | + #[inline] |
| 262 | + pub const fn expect(self, msg: &str) -> Uint<LIMBS> { |
| 263 | + assert!(self.is_some.is_true_vartime(), "{}", msg); |
| 264 | + self.value |
| 265 | + } |
| 266 | +} |
| 267 | + |
| 268 | +impl<const LIMBS: usize> ConstOption<(Uint<LIMBS>, Uint<LIMBS>)> { |
| 269 | + /// Returns the contained value, consuming the `self` value. |
| 270 | + /// |
| 271 | + /// # Panics |
| 272 | + /// |
| 273 | + /// Panics if the value is none with a custom panic message provided by |
| 274 | + /// `msg`. |
| 275 | + #[inline] |
| 276 | + pub const fn expect(self, msg: &str) -> (Uint<LIMBS>, Uint<LIMBS>) { |
| 277 | + assert!(self.is_some.is_true_vartime(), "{}", msg); |
| 278 | + self.value |
| 279 | + } |
| 280 | +} |
| 281 | + |
| 282 | +impl<const LIMBS: usize> ConstOption<NonZero<Uint<LIMBS>>> { |
| 283 | + /// Returns the contained value, consuming the `self` value. |
| 284 | + /// |
| 285 | + /// # Panics |
| 286 | + /// |
| 287 | + /// Panics if the value is none with a custom panic message provided by |
| 288 | + /// `msg`. |
| 289 | + #[inline] |
| 290 | + pub const fn expect(self, msg: &str) -> NonZero<Uint<LIMBS>> { |
| 291 | + assert!(self.is_some.is_true_vartime(), "{}", msg); |
| 292 | + self.value |
| 293 | + } |
| 294 | +} |
| 295 | + |
177 | 296 | #[cfg(test)] |
178 | 297 | mod tests { |
179 | 298 | use super::ConstChoice; |
|
0 commit comments