@@ -9,7 +9,8 @@ use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign,
99/// This is used as a "belt-and-suspenders" defense in addition to mechanisms like
1010/// constant-time predication intrinsics provided by the `cmov` crate, and is never expected to be
1111/// the only line of defense.
12- #[ derive( Copy , Clone , Debug ) ]
12+ // TODO(tarcieri): remove `Eq`/`PartialEq` when `crypto-bigint` is updated
13+ #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
1314pub struct Choice ( u8 ) ;
1415
1516impl Choice {
@@ -59,6 +60,26 @@ impl Choice {
5960 core:: hint:: black_box ( self . 0 )
6061 }
6162
63+ /// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
64+ ///
65+ /// This does not apply `black_box` to the output.
66+ // TODO(tarcieri): deprecate/remove this in favor of `to_bool` when MSRV is Rust 1.86
67+ pub const fn to_bool_vartime ( self ) -> bool {
68+ self . 0 != 0
69+ }
70+
71+ /// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
72+ ///
73+ /// This does not apply `black_box` to the output.
74+ // TODO(tarcieri): deprecate/remove this in favor of `to_u8` when MSRV is Rust 1.86
75+ pub const fn to_u8_vartime ( self ) -> u8 {
76+ self . 0
77+ }
78+
79+ //
80+ // Bitwise ops
81+ //
82+
6283 /// Apply an `and` conditional to the given [`Choice`]s.
6384 #[ inline]
6485 pub const fn and ( self , rhs : Choice ) -> Choice {
@@ -83,6 +104,128 @@ impl Choice {
83104 // NOTE: assumes self.0 is `0` or `1` as checked in constructor
84105 Self ( self . 0 ^ 1 )
85106 }
107+
108+ //
109+ // Comparison ops
110+ //
111+
112+ /// `const fn` not equal operation.
113+ #[ inline]
114+ pub const fn ne ( self , other : Self ) -> Self {
115+ Self :: xor ( self , other)
116+ }
117+
118+ /// `const fn` equality operation.
119+ #[ inline]
120+ pub const fn eq ( self , other : Self ) -> Self {
121+ Self :: ne ( self , other) . not ( )
122+ }
123+
124+ //
125+ // `const fn` constructor methods
126+ //
127+
128+ /// Returns the truthy value if `x == y`, and the falsy value otherwise.
129+ #[ inline]
130+ pub const fn from_i64_eq ( x : i64 , y : i64 ) -> Self {
131+ Self :: from_u64_nonzero ( x as u64 ^ y as u64 ) . not ( )
132+ }
133+
134+ /// Returns the truthy value if `x == y`, and the falsy value otherwise.
135+ #[ inline]
136+ pub const fn from_u32_eq ( x : u32 , y : u32 ) -> Self {
137+ Self :: from_u32_nonzero ( x ^ y) . not ( )
138+ }
139+
140+ /// Returns the truthy value if `x <= y` and the falsy value otherwise.
141+ #[ inline]
142+ pub const fn from_u32_le ( x : u32 , y : u32 ) -> Self {
143+ // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
144+ let bit = ( ( ( !x) | y) & ( ( x ^ y) | !y. wrapping_sub ( x) ) ) >> ( u32:: BITS - 1 ) ;
145+ Self :: from_u32_lsb ( bit)
146+ }
147+
148+ /// Initialize from the least significant bit of a `u32`.
149+ #[ inline]
150+ pub const fn from_u32_lsb ( value : u32 ) -> Self {
151+ Self :: new ( ( value & 0x1 ) as u8 )
152+ }
153+
154+ /// Returns the truthy value if `x < y`, and the falsy value otherwise.
155+ #[ inline]
156+ pub const fn from_u32_lt ( x : u32 , y : u32 ) -> Self {
157+ // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
158+ let bit = ( ( ( !x) & y) | ( ( ( !x) | y) & x. wrapping_sub ( y) ) ) >> ( u32:: BITS - 1 ) ;
159+ Self :: from_u32_lsb ( bit)
160+ }
161+
162+ /// Returns the truthy value if `value != 0`, and the falsy value otherwise.
163+ #[ inline]
164+ pub const fn from_u32_nonzero ( value : u32 ) -> Self {
165+ Self :: from_u32_lsb ( ( value | value. wrapping_neg ( ) ) >> ( u32:: BITS - 1 ) )
166+ }
167+
168+ /// Initialize from the least significant bit of a `u64`.
169+ #[ inline]
170+ pub const fn from_u64_lsb ( value : u64 ) -> Self {
171+ Self :: new ( ( value & 0x1 ) as u8 )
172+ }
173+
174+ /// Returns the truthy value if `value != 0`, and the falsy value otherwise.
175+ #[ inline]
176+ pub const fn from_u64_nonzero ( value : u64 ) -> Self {
177+ Self :: from_u64_lsb ( ( value | value. wrapping_neg ( ) ) >> ( u64:: BITS - 1 ) )
178+ }
179+
180+ //
181+ // `const fn` predication methods
182+ //
183+
184+ /// Return `x` if `self` is truthy, otherwise return 0.
185+ #[ inline]
186+ pub const fn if_true_u32 ( self , x : u32 ) -> u32 {
187+ self . select_u32 ( 0 , x)
188+ }
189+
190+ /// Return `b` if `self` is truthy, otherwise return `a`.
191+ #[ inline]
192+ pub const fn select_i64 ( self , a : i64 , b : i64 ) -> i64 {
193+ self . select_u64 ( a as u64 , b as u64 ) as i64
194+ }
195+
196+ /// Return `b` if `self` is truthy, otherwise return `a`.
197+ #[ inline]
198+ pub const fn select_u32 ( self , a : u32 , b : u32 ) -> u32 {
199+ a ^ ( self . to_u32_mask ( ) & ( a ^ b) )
200+ }
201+
202+ /// Return `b` if `self` is truthy, otherwise return `a`.
203+ #[ inline]
204+ pub const fn select_u64 ( self , a : u64 , b : u64 ) -> u64 {
205+ a ^ ( self . to_u64_mask ( ) & ( a ^ b) )
206+ }
207+
208+ /// Create a `u32` bitmask.
209+ ///
210+ /// # Returns
211+ /// - `0` for `Choice::FALSE`
212+ /// - `u32::MAX` for `Choice::TRUE`
213+ #[ inline]
214+ #[ allow( trivial_numeric_casts) ]
215+ pub ( crate ) const fn to_u32_mask ( self ) -> u32 {
216+ ( self . 0 as u32 & 1 ) . wrapping_neg ( )
217+ }
218+
219+ /// Create a `u64` bitmask.
220+ ///
221+ /// # Returns
222+ /// - `0` for `Choice::FALSE`
223+ /// - `u64::MAX` for `Choice::TRUE`
224+ #[ inline]
225+ #[ allow( trivial_numeric_casts) ]
226+ pub ( crate ) const fn to_u64_mask ( self ) -> u64 {
227+ ( self . 0 as u64 & 1 ) . wrapping_neg ( )
228+ }
86229}
87230
88231impl BitAnd for Choice {
@@ -253,4 +396,26 @@ mod tests {
253396 assert_eq ! ( Choice :: new( 0 ) . not( ) . to_u8( ) , 1 ) ;
254397 assert_eq ! ( Choice :: new( 1 ) . not( ) . to_u8( ) , 0 ) ;
255398 }
399+
400+ #[ test]
401+ fn from_u64_lsb ( ) {
402+ assert_eq ! ( Choice :: from_u64_lsb( 0 ) , Choice :: FALSE ) ;
403+ assert_eq ! ( Choice :: from_u64_lsb( 1 ) , Choice :: TRUE ) ;
404+ }
405+
406+ #[ test]
407+ fn select_u32 ( ) {
408+ let a: u32 = 1 ;
409+ let b: u32 = 2 ;
410+ assert_eq ! ( Choice :: TRUE . select_u32( a, b) , b) ;
411+ assert_eq ! ( Choice :: FALSE . select_u32( a, b) , a) ;
412+ }
413+
414+ #[ test]
415+ fn select_u64 ( ) {
416+ let a: u64 = 1 ;
417+ let b: u64 = 2 ;
418+ assert_eq ! ( Choice :: TRUE . select_u64( a, b) , b) ;
419+ assert_eq ! ( Choice :: FALSE . select_u64( a, b) , a) ;
420+ }
256421}
0 commit comments