Skip to content

Commit da71cbc

Browse files
authored
ctutils: add u128 methods to Choice (#1277)
Adds the methods `crypto-bigint` needs for `WideWord` on 64-bit platforms to `Choice`: - `from_u128_le` - `from_u128_lsb` - `select_u128` It would probably be good if `u128` had full parity with `u32` and `u64`, but this just does what's needed for `crypto-bigint` for now.
1 parent 3773be9 commit da71cbc

File tree

1 file changed

+55
-0
lines changed

1 file changed

+55
-0
lines changed

ctutils/src/choice.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,20 @@ impl Choice {
202202
Self::from_u64_lsb((value | value.wrapping_neg()) >> (u64::BITS - 1))
203203
}
204204

205+
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
206+
#[inline]
207+
pub const fn from_u128_le(x: u128, y: u128) -> Self {
208+
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
209+
let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (u128::BITS - 1);
210+
Self::from_u128_lsb(bit)
211+
}
212+
213+
/// Initialize from the least significant bit of a `u128`.
214+
#[inline]
215+
pub const fn from_u128_lsb(value: u128) -> Self {
216+
Self::new((value & 1) as u8)
217+
}
218+
205219
//
206220
// `const fn` predication methods
207221
//
@@ -233,6 +247,15 @@ impl Choice {
233247
a ^ (self.to_u64_mask() & (a ^ b))
234248
}
235249

250+
/// `const fn` helper: return `b` if `self` is truthy, otherwise return `a`.
251+
///
252+
/// Only use this instead of the [`CtSelect`] trait in the event you're in a `const fn` context
253+
/// and can't use the trait. The former will provide better constant-time assurances.
254+
#[inline]
255+
pub const fn select_u128(self, a: u128, b: u128) -> u128 {
256+
a ^ (self.to_u128_mask() & (a ^ b))
257+
}
258+
236259
/// Create a `u32` bitmask.
237260
///
238261
/// # Returns
@@ -252,6 +275,16 @@ impl Choice {
252275
pub const fn to_u64_mask(self) -> u64 {
253276
(self.0 as u64 & 1).wrapping_neg()
254277
}
278+
279+
/// Create a `u128` bitmask.
280+
///
281+
/// # Returns
282+
/// - `0` for `Choice::FALSE`
283+
/// - `u128::MAX` for `Choice::TRUE`
284+
#[inline]
285+
pub const fn to_u128_mask(self) -> u128 {
286+
(self.0 as u128 & 1).wrapping_neg()
287+
}
255288
}
256289

257290
impl BitAnd for Choice {
@@ -501,6 +534,20 @@ mod tests {
501534
assert_eq!(Choice::from_u64_nonzero(2), Choice::TRUE);
502535
}
503536

537+
#[test]
538+
fn from_u128_le() {
539+
assert_eq!(Choice::from_u128_le(0, 0), Choice::TRUE);
540+
assert_eq!(Choice::from_u128_le(1, 0), Choice::FALSE);
541+
assert_eq!(Choice::from_u128_le(1, 1), Choice::TRUE);
542+
assert_eq!(Choice::from_u128_le(1, 2), Choice::TRUE);
543+
}
544+
545+
#[test]
546+
fn from_u128_lsb() {
547+
assert_eq!(Choice::from_u128_lsb(0), Choice::FALSE);
548+
assert_eq!(Choice::from_u128_lsb(1), Choice::TRUE);
549+
}
550+
504551
#[test]
505552
fn select_i64() {
506553
let a: i64 = 1;
@@ -524,4 +571,12 @@ mod tests {
524571
assert_eq!(Choice::TRUE.select_u64(a, b), b);
525572
assert_eq!(Choice::FALSE.select_u64(a, b), a);
526573
}
574+
575+
#[test]
576+
fn select_u128() {
577+
let a: u128 = 1;
578+
let b: u128 = 2;
579+
assert_eq!(Choice::TRUE.select_u128(a, b), b);
580+
assert_eq!(Choice::FALSE.select_u128(a, b), a);
581+
}
527582
}

0 commit comments

Comments
 (0)