@@ -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,122 @@ 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 `b` if `self` is truthy, otherwise return `a`.
185+ #[ inline]
186+ pub const fn select_i64 ( self , a : i64 , b : i64 ) -> i64 {
187+ self . select_u64 ( a as u64 , b as u64 ) as i64
188+ }
189+
190+ /// Return `b` if `self` is truthy, otherwise return `a`.
191+ #[ inline]
192+ pub const fn select_u32 ( self , a : u32 , b : u32 ) -> u32 {
193+ a ^ ( self . to_u32_mask ( ) & ( a ^ b) )
194+ }
195+
196+ /// Return `b` if `self` is truthy, otherwise return `a`.
197+ #[ inline]
198+ pub const fn select_u64 ( self , a : u64 , b : u64 ) -> u64 {
199+ a ^ ( self . to_u64_mask ( ) & ( a ^ b) )
200+ }
201+
202+ /// Create a `u32` bitmask.
203+ ///
204+ /// # Returns
205+ /// - `0` for `Choice::FALSE`
206+ /// - `u32::MAX` for `Choice::TRUE`
207+ #[ inline]
208+ #[ allow( trivial_numeric_casts) ]
209+ pub ( crate ) const fn to_u32_mask ( self ) -> u32 {
210+ ( self . 0 as u32 & 1 ) . wrapping_neg ( )
211+ }
212+
213+ /// Create a `u64` bitmask.
214+ ///
215+ /// # Returns
216+ /// - `0` for `Choice::FALSE`
217+ /// - `u64::MAX` for `Choice::TRUE`
218+ #[ inline]
219+ #[ allow( trivial_numeric_casts) ]
220+ pub ( crate ) const fn to_u64_mask ( self ) -> u64 {
221+ ( self . 0 as u64 & 1 ) . wrapping_neg ( )
222+ }
86223}
87224
88225impl BitAnd for Choice {
@@ -253,4 +390,84 @@ mod tests {
253390 assert_eq ! ( Choice :: new( 0 ) . not( ) . to_u8( ) , 1 ) ;
254391 assert_eq ! ( Choice :: new( 1 ) . not( ) . to_u8( ) , 0 ) ;
255392 }
393+
394+ #[ test]
395+ fn from_i64_eq ( ) {
396+ assert_eq ! ( Choice :: from_i64_eq( 0 , 1 ) , Choice :: FALSE ) ;
397+ assert_eq ! ( Choice :: from_i64_eq( 1 , 1 ) , Choice :: TRUE ) ;
398+ }
399+
400+ #[ test]
401+ fn from_u32_eq ( ) {
402+ assert_eq ! ( Choice :: from_u32_eq( 0 , 1 ) , Choice :: FALSE ) ;
403+ assert_eq ! ( Choice :: from_u32_eq( 1 , 1 ) , Choice :: TRUE ) ;
404+ }
405+
406+ #[ test]
407+ fn from_u32_le ( ) {
408+ assert_eq ! ( Choice :: from_u32_le( 0 , 0 ) , Choice :: TRUE ) ;
409+ assert_eq ! ( Choice :: from_u32_le( 1 , 0 ) , Choice :: FALSE ) ;
410+ assert_eq ! ( Choice :: from_u32_le( 1 , 1 ) , Choice :: TRUE ) ;
411+ assert_eq ! ( Choice :: from_u32_le( 1 , 2 ) , Choice :: TRUE ) ;
412+ }
413+
414+ #[ test]
415+ fn from_u32_lsb ( ) {
416+ assert_eq ! ( Choice :: from_u32_lsb( 0 ) , Choice :: FALSE ) ;
417+ assert_eq ! ( Choice :: from_u32_lsb( 1 ) , Choice :: TRUE ) ;
418+ assert_eq ! ( Choice :: from_u32_lsb( 2 ) , Choice :: FALSE ) ;
419+ assert_eq ! ( Choice :: from_u32_lsb( 3 ) , Choice :: TRUE ) ;
420+ }
421+
422+ #[ test]
423+ fn from_u32_lt ( ) {
424+ assert_eq ! ( Choice :: from_u32_lt( 0 , 0 ) , Choice :: FALSE ) ;
425+ assert_eq ! ( Choice :: from_u32_lt( 1 , 0 ) , Choice :: FALSE ) ;
426+ assert_eq ! ( Choice :: from_u32_lt( 1 , 1 ) , Choice :: FALSE ) ;
427+ assert_eq ! ( Choice :: from_u32_lt( 1 , 2 ) , Choice :: TRUE ) ;
428+ }
429+
430+ #[ test]
431+ fn from_u32_nonzero ( ) {
432+ assert_eq ! ( Choice :: from_u32_nonzero( 0 ) , Choice :: FALSE ) ;
433+ assert_eq ! ( Choice :: from_u32_nonzero( 1 ) , Choice :: TRUE ) ;
434+ assert_eq ! ( Choice :: from_u32_nonzero( 2 ) , Choice :: TRUE ) ;
435+ }
436+
437+ #[ test]
438+ fn from_u64_lsb ( ) {
439+ assert_eq ! ( Choice :: from_u64_lsb( 0 ) , Choice :: FALSE ) ;
440+ assert_eq ! ( Choice :: from_u64_lsb( 1 ) , Choice :: TRUE ) ;
441+ }
442+
443+ #[ test]
444+ fn from_u64_nonzero ( ) {
445+ assert_eq ! ( Choice :: from_u64_nonzero( 0 ) , Choice :: FALSE ) ;
446+ assert_eq ! ( Choice :: from_u64_nonzero( 1 ) , Choice :: TRUE ) ;
447+ assert_eq ! ( Choice :: from_u64_nonzero( 2 ) , Choice :: TRUE ) ;
448+ }
449+
450+ #[ test]
451+ fn select_i64 ( ) {
452+ let a: i64 = 1 ;
453+ let b: i64 = 2 ;
454+ assert_eq ! ( Choice :: TRUE . select_i64( a, b) , b) ;
455+ assert_eq ! ( Choice :: FALSE . select_i64( a, b) , a) ;
456+ }
457+
458+ #[ test]
459+ fn select_u32 ( ) {
460+ let a: u32 = 1 ;
461+ let b: u32 = 2 ;
462+ assert_eq ! ( Choice :: TRUE . select_u32( a, b) , b) ;
463+ assert_eq ! ( Choice :: FALSE . select_u32( a, b) , a) ;
464+ }
465+
466+ #[ test]
467+ fn select_u64 ( ) {
468+ let a: u64 = 1 ;
469+ let b: u64 = 2 ;
470+ assert_eq ! ( Choice :: TRUE . select_u64( a, b) , b) ;
471+ assert_eq ! ( Choice :: FALSE . select_u64( a, b) , a) ;
472+ }
256473}
0 commit comments