Skip to content

Commit 673d98a

Browse files
committed
ctutils: Choice::from_u128* constructors
Gives `u128` feature parity with `u32` and `u64` for `Choice` constructors by adding the following: - `from_u128_eq` - `from_u128_lt` - `from_u128_nonzero` This also cleans up the implementation by extracting some internal macros (`bitle!`, `bitlt`!`, `bitnz!`) to write comparison predicates.
1 parent f06e060 commit 673d98a

File tree

1 file changed

+71
-17
lines changed

1 file changed

+71
-17
lines changed

ctutils/src/choice.rs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
use crate::{CtEq, CtSelect};
22
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
33

4+
/// Bitwise less-than-or equal: returns `1` if `x <= y`, and otherwise returns `0`.
5+
///
6+
/// See "Hacker's Delight" 2nd edition, section 2-12 (Comparison predicates)
7+
macro_rules! bitle {
8+
($x:expr, $y:expr, $bits:expr) => {
9+
(((!$x) | $y) & (($x ^ $y) | !($y.wrapping_sub($x)))) >> ($bits - 1)
10+
};
11+
}
12+
13+
/// Bitwise less-than: returns `1` if `x < y`, and otherwise returns `0`.
14+
///
15+
/// See "Hacker's Delight" 2nd edition, section 2-12 (Comparison predicates)
16+
macro_rules! bitlt {
17+
($x:expr, $y:expr, $bits:expr) => {
18+
(((!$x) & $y) | (((!$x) | $y) & $x.wrapping_sub($y))) >> ($bits - 1)
19+
};
20+
}
21+
22+
/// Bitwise non-zero: returns `1` if `x != 0`, and otherwise returns `0`.
23+
macro_rules! bitnz {
24+
($value:expr, $bits:expr) => {
25+
($value | $value.wrapping_neg()) >> ($bits - 1)
26+
};
27+
}
28+
429
/// Constant-time analogue of `bool` providing a "best effort" optimization barrier.
530
///
631
/// Attempts to hint to the compiler and its codegen backends that optimizations should not be
@@ -149,9 +174,7 @@ impl Choice {
149174
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
150175
#[inline]
151176
pub const fn from_u32_le(x: u32, y: u32) -> Self {
152-
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
153-
let bit = (((!x) | y) & ((x ^ y) | !y.wrapping_sub(x))) >> (u32::BITS - 1);
154-
Self::from_u32_lsb(bit)
177+
Self::from_u32_lsb(bitle!(x, y, u32::BITS))
155178
}
156179

157180
/// Initialize from the least significant bit of a `u32`.
@@ -163,15 +186,13 @@ impl Choice {
163186
/// Returns the truthy value if `x < y`, and the falsy value otherwise.
164187
#[inline]
165188
pub const fn from_u32_lt(x: u32, y: u32) -> Self {
166-
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
167-
let bit = (((!x) & y) | (((!x) | y) & x.wrapping_sub(y))) >> (u32::BITS - 1);
168-
Self::from_u32_lsb(bit)
189+
Self::from_u32_lsb(bitlt!(x, y, u32::BITS))
169190
}
170191

171192
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
172193
#[inline]
173194
pub const fn from_u32_nonzero(value: u32) -> Self {
174-
Self::from_u32_lsb((value | value.wrapping_neg()) >> (u32::BITS - 1))
195+
Self::from_u32_lsb(bitnz!(value, u32::BITS))
175196
}
176197

177198
/// Returns the truthy value if `x == y`, and the falsy value otherwise.
@@ -183,9 +204,7 @@ impl Choice {
183204
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
184205
#[inline]
185206
pub const fn from_u64_le(x: u64, y: u64) -> Self {
186-
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
187-
let bit = (((!x) | y) & ((x ^ y) | !y.wrapping_sub(x))) >> (u64::BITS - 1);
188-
Self::from_u64_lsb(bit)
207+
Self::from_u64_lsb(bitle!(x, y, u64::BITS))
189208
}
190209

191210
/// Initialize from the least significant bit of a `u64`.
@@ -197,23 +216,25 @@ impl Choice {
197216
/// Returns the truthy value if `x < y`, and the falsy value otherwise.
198217
#[inline]
199218
pub const fn from_u64_lt(x: u64, y: u64) -> Self {
200-
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
201-
let bit = (((!x) & y) | (((!x) | y) & x.wrapping_sub(y))) >> (u64::BITS - 1);
202-
Self::from_u64_lsb(bit)
219+
Self::from_u64_lsb(bitlt!(x, y, u64::BITS))
203220
}
204221

205222
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
206223
#[inline]
207224
pub const fn from_u64_nonzero(value: u64) -> Self {
208-
Self::from_u64_lsb((value | value.wrapping_neg()) >> (u64::BITS - 1))
225+
Self::from_u64_lsb(bitnz!(value, u64::BITS))
226+
}
227+
228+
/// Returns the truthy value if `x == y`, and the falsy value otherwise.
229+
#[inline]
230+
pub const fn from_u128_eq(x: u128, y: u128) -> Self {
231+
Self::from_u128_nonzero(x ^ y).not()
209232
}
210233

211234
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
212235
#[inline]
213236
pub const fn from_u128_le(x: u128, y: u128) -> Self {
214-
// See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates)
215-
let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (u128::BITS - 1);
216-
Self::from_u128_lsb(bit)
237+
Self::from_u128_lsb(bitle!(x, y, u128::BITS))
217238
}
218239

219240
/// Initialize from the least significant bit of a `u128`.
@@ -222,6 +243,18 @@ impl Choice {
222243
Self::new((value & 1) as u8)
223244
}
224245

246+
/// Returns the truthy value if `x < y`, and the falsy value otherwise.
247+
#[inline]
248+
pub const fn from_u128_lt(x: u128, y: u128) -> Self {
249+
Self::from_u128_lsb(bitlt!(x, y, u128::BITS))
250+
}
251+
252+
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
253+
#[inline]
254+
pub const fn from_u128_nonzero(value: u128) -> Self {
255+
Self::from_u128_lsb(bitnz!(value, u128::BITS))
256+
}
257+
225258
//
226259
// `const fn` predication methods
227260
//
@@ -550,6 +583,12 @@ mod tests {
550583
assert_eq!(Choice::from_u64_nonzero(2), Choice::TRUE);
551584
}
552585

586+
#[test]
587+
fn from_u128_eq() {
588+
assert_eq!(Choice::from_u128_eq(0, 1), Choice::FALSE);
589+
assert_eq!(Choice::from_u128_eq(1, 1), Choice::TRUE);
590+
}
591+
553592
#[test]
554593
fn from_u128_le() {
555594
assert_eq!(Choice::from_u128_le(0, 0), Choice::TRUE);
@@ -564,6 +603,21 @@ mod tests {
564603
assert_eq!(Choice::from_u128_lsb(1), Choice::TRUE);
565604
}
566605

606+
#[test]
607+
fn from_u128_lt() {
608+
assert_eq!(Choice::from_u128_lt(0, 0), Choice::FALSE);
609+
assert_eq!(Choice::from_u128_lt(1, 0), Choice::FALSE);
610+
assert_eq!(Choice::from_u128_lt(1, 1), Choice::FALSE);
611+
assert_eq!(Choice::from_u128_lt(1, 2), Choice::TRUE);
612+
}
613+
614+
#[test]
615+
fn from_u128_nonzero() {
616+
assert_eq!(Choice::from_u128_nonzero(0), Choice::FALSE);
617+
assert_eq!(Choice::from_u128_nonzero(1), Choice::TRUE);
618+
assert_eq!(Choice::from_u128_nonzero(2), Choice::TRUE);
619+
}
620+
567621
#[test]
568622
fn select_i64() {
569623
let a: i64 = 1;

0 commit comments

Comments
 (0)