Skip to content

Commit 6083555

Browse files
authored
Make bit ops use u32 for shifts and bit counts (#373)
1 parent 4ea713f commit 6083555

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+448
-461
lines changed

src/ct_choice.rs

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,63 +15,132 @@ impl CtChoice {
1515
/// The truthy value.
1616
pub const TRUE: Self = Self(Word::MAX);
1717

18+
#[inline]
19+
pub(crate) const fn as_u32_mask(&self) -> u32 {
20+
#[allow(trivial_numeric_casts)]
21+
(self.0.wrapping_neg() as u32).wrapping_neg()
22+
}
23+
1824
/// Returns the truthy value if `value == Word::MAX`, and the falsy value if `value == 0`.
1925
/// Panics for other values.
20-
pub(crate) const fn from_mask(value: Word) -> Self {
26+
#[inline]
27+
pub(crate) const fn from_word_mask(value: Word) -> Self {
2128
debug_assert!(value == Self::FALSE.0 || value == Self::TRUE.0);
2229
Self(value)
2330
}
2431

2532
/// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`.
2633
/// Panics for other values.
27-
pub(crate) const fn from_lsb(value: Word) -> Self {
34+
#[inline]
35+
pub(crate) const fn from_word_lsb(value: Word) -> Self {
2836
debug_assert!(value == 0 || value == 1);
2937
Self(value.wrapping_neg())
3038
}
3139

40+
#[inline]
41+
pub(crate) const fn from_u32_lsb(value: u32) -> Self {
42+
debug_assert!(value == 0 || value == 1);
43+
#[allow(trivial_numeric_casts)]
44+
Self((value as Word).wrapping_neg())
45+
}
46+
47+
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
48+
#[inline]
49+
pub(crate) const fn from_u32_nonzero(value: u32) -> Self {
50+
Self::from_u32_lsb((value | value.wrapping_neg()) >> (u32::BITS - 1))
51+
}
52+
3253
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
33-
pub(crate) const fn from_usize_being_nonzero(value: usize) -> Self {
34-
const HI_BIT: u32 = usize::BITS - 1;
35-
Self::from_lsb(((value | value.wrapping_neg()) >> HI_BIT) as Word)
54+
#[inline]
55+
pub(crate) const fn from_word_nonzero(value: Word) -> Self {
56+
Self::from_word_lsb((value | value.wrapping_neg()) >> (Word::BITS - 1))
57+
}
58+
59+
/// Returns the truthy value if `x == y`, and the falsy value otherwise.
60+
#[inline]
61+
pub(crate) const fn from_u32_eq(x: u32, y: u32) -> Self {
62+
Self::from_u32_nonzero(x ^ y).not()
3663
}
3764

3865
/// Returns the truthy value if `x == y`, and the falsy value otherwise.
39-
pub(crate) const fn from_usize_equality(x: usize, y: usize) -> Self {
40-
Self::from_usize_being_nonzero(x.wrapping_sub(y)).not()
66+
#[inline]
67+
pub(crate) const fn from_word_eq(x: Word, y: Word) -> Self {
68+
Self::from_word_nonzero(x ^ y).not()
69+
}
70+
71+
/// Returns the truthy value if `x < y`, and the falsy value otherwise.
72+
#[inline]
73+
pub(crate) const fn from_word_lt(x: Word, y: Word) -> Self {
74+
let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (Word::BITS - 1);
75+
Self::from_word_lsb(bit)
4176
}
4277

4378
/// Returns the truthy value if `x < y`, and the falsy value otherwise.
44-
pub(crate) const fn from_usize_lt(x: usize, y: usize) -> Self {
45-
let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (usize::BITS - 1);
46-
Self::from_lsb(bit as Word)
79+
#[inline]
80+
pub(crate) const fn from_u32_lt(x: u32, y: u32) -> Self {
81+
let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (u32::BITS - 1);
82+
Self::from_u32_lsb(bit)
83+
}
84+
85+
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
86+
#[inline]
87+
pub(crate) const fn from_word_le(x: Word, y: Word) -> Self {
88+
let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (Word::BITS - 1);
89+
Self::from_word_lsb(bit)
90+
}
91+
92+
/// Returns the truthy value if `x <= y` and the falsy value otherwise.
93+
#[inline]
94+
pub(crate) const fn from_u32_le(x: u32, y: u32) -> Self {
95+
let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (u32::BITS - 1);
96+
Self::from_u32_lsb(bit)
4797
}
4898

99+
#[inline]
49100
pub(crate) const fn not(&self) -> Self {
50101
Self(!self.0)
51102
}
52103

104+
#[inline]
53105
pub(crate) const fn or(&self, other: Self) -> Self {
54106
Self(self.0 | other.0)
55107
}
56108

109+
#[inline]
57110
pub(crate) const fn and(&self, other: Self) -> Self {
58111
Self(self.0 & other.0)
59112
}
60113

61114
/// Return `b` if `self` is truthy, otherwise return `a`.
62-
pub(crate) const fn select(&self, a: Word, b: Word) -> Word {
115+
#[inline]
116+
pub(crate) const fn select_word(&self, a: Word, b: Word) -> Word {
63117
a ^ (self.0 & (a ^ b))
64118
}
65119

120+
/// Return `b` if `self` is truthy, otherwise return `a`.
121+
#[inline]
122+
pub(crate) const fn select_u32(&self, a: u32, b: u32) -> u32 {
123+
a ^ (self.as_u32_mask() & (a ^ b))
124+
}
125+
66126
/// Return `x` if `self` is truthy, otherwise return 0.
67-
pub(crate) const fn if_true(&self, x: Word) -> Word {
127+
#[inline]
128+
pub(crate) const fn if_true_word(&self, x: Word) -> Word {
68129
x & self.0
69130
}
70131

132+
/// Return `x` if `self` is truthy, otherwise return 0.
133+
#[inline]
134+
pub(crate) const fn if_true_u32(&self, x: u32) -> u32 {
135+
x & self.as_u32_mask()
136+
}
137+
138+
#[inline]
71139
pub(crate) const fn is_true_vartime(&self) -> bool {
72140
self.0 == CtChoice::TRUE.0
73141
}
74142

143+
#[inline]
75144
pub(crate) const fn to_u8(self) -> u8 {
76145
(self.0 as u8) & 1
77146
}
@@ -98,7 +167,7 @@ mod tests {
98167
fn select() {
99168
let a: Word = 1;
100169
let b: Word = 2;
101-
assert_eq!(CtChoice::TRUE.select(a, b), b);
102-
assert_eq!(CtChoice::FALSE.select(a, b), a);
170+
assert_eq!(CtChoice::TRUE.select_word(a, b), b);
171+
assert_eq!(CtChoice::FALSE.select_word(a, b), a);
103172
}
104173
}

src/limb.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub type Word = u64;
5454
pub type WideWord = u128;
5555

5656
/// Highest bit in a [`Limb`].
57-
pub(crate) const HI_BIT: usize = Limb::BITS - 1;
57+
pub(crate) const HI_BIT: u32 = Limb::BITS - 1;
5858

5959
/// Big integers are represented as an array of smaller CPU word-size integers
6060
/// called "limbs".
@@ -78,7 +78,7 @@ impl Limb {
7878

7979
/// Size of the inner integer in bits.
8080
#[cfg(target_pointer_width = "32")]
81-
pub const BITS: usize = 32;
81+
pub const BITS: u32 = 32;
8282
/// Size of the inner integer in bytes.
8383
#[cfg(target_pointer_width = "32")]
8484
pub const BYTES: usize = 4;
@@ -87,14 +87,14 @@ impl Limb {
8787

8888
/// Size of the inner integer in bits.
8989
#[cfg(target_pointer_width = "64")]
90-
pub const BITS: usize = 64;
90+
pub const BITS: u32 = 64;
9191
/// Size of the inner integer in bytes.
9292
#[cfg(target_pointer_width = "64")]
9393
pub const BYTES: usize = 8;
9494
}
9595

9696
impl Bounded for Limb {
97-
const BITS: usize = Self::BITS;
97+
const BITS: u32 = Self::BITS;
9898
const BYTES: usize = Self::BYTES;
9999
}
100100

src/limb/bits.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ use super::Limb;
22

33
impl Limb {
44
/// Calculate the number of bits needed to represent this number.
5-
pub const fn bits(self) -> usize {
6-
Limb::BITS - (self.0.leading_zeros() as usize)
5+
pub const fn bits(self) -> u32 {
6+
Limb::BITS - self.0.leading_zeros()
77
}
88

99
/// Calculate the number of leading zeros in the binary representation of this number.
10-
pub const fn leading_zeros(self) -> usize {
11-
self.0.leading_zeros() as usize
10+
pub const fn leading_zeros(self) -> u32 {
11+
self.0.leading_zeros()
1212
}
1313

1414
/// Calculate the number of trailing zeros in the binary representation of this number.
15-
pub const fn trailing_zeros(self) -> usize {
16-
self.0.trailing_zeros() as usize
15+
pub const fn trailing_zeros(self) -> u32 {
16+
self.0.trailing_zeros()
1717
}
1818

1919
/// Calculate the number of trailing ones the binary representation of this number.
20-
pub const fn trailing_ones(self) -> usize {
21-
self.0.trailing_ones() as usize
20+
pub const fn trailing_ones(self) -> u32 {
21+
self.0.trailing_ones()
2222
}
2323
}

src/limb/cmp.rs

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Limb comparisons
22
3-
use super::HI_BIT;
43
use crate::{CtChoice, Limb};
54
use core::cmp::Ordering;
65
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
@@ -28,42 +27,13 @@ impl Limb {
2827
/// Return `b` if `c` is truthy, otherwise return `a`.
2928
#[inline]
3029
pub(crate) const fn ct_select(a: Self, b: Self, c: CtChoice) -> Self {
31-
Self(c.select(a.0, b.0))
30+
Self(c.select_word(a.0, b.0))
3231
}
3332

3433
/// Returns the truthy value if `self != 0` and the falsy value otherwise.
3534
#[inline]
3635
pub(crate) const fn ct_is_nonzero(&self) -> CtChoice {
37-
let inner = self.0;
38-
CtChoice::from_lsb((inner | inner.wrapping_neg()) >> HI_BIT)
39-
}
40-
41-
/// Returns the truthy value if `lhs == rhs` and the falsy value otherwise.
42-
#[inline]
43-
pub(crate) const fn ct_eq(lhs: Self, rhs: Self) -> CtChoice {
44-
let x = lhs.0;
45-
let y = rhs.0;
46-
47-
// x ^ y == 0 if and only if x == y
48-
Self(x ^ y).ct_is_nonzero().not()
49-
}
50-
51-
/// Returns the truthy value if `lhs < rhs` and the falsy value otherwise.
52-
#[inline]
53-
pub(crate) const fn ct_lt(lhs: Self, rhs: Self) -> CtChoice {
54-
let x = lhs.0;
55-
let y = rhs.0;
56-
let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (Limb::BITS - 1);
57-
CtChoice::from_lsb(bit)
58-
}
59-
60-
/// Returns the truthy value if `lhs <= rhs` and the falsy value otherwise.
61-
#[inline]
62-
pub(crate) const fn ct_le(lhs: Self, rhs: Self) -> CtChoice {
63-
let x = lhs.0;
64-
let y = rhs.0;
65-
let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (Limb::BITS - 1);
66-
CtChoice::from_lsb(bit)
36+
CtChoice::from_word_nonzero(self.0)
6737
}
6838
}
6939

src/limb/rand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ impl RandomMod for Limb {
2121
fn random_mod(rng: &mut impl CryptoRngCore, modulus: &NonZero<Self>) -> Self {
2222
let mut bytes = <Self as Encoding>::Repr::default();
2323

24-
let n_bits = modulus.bits();
24+
let n_bits = modulus.bits() as usize;
2525
let n_bytes = (n_bits + 7) / 8;
26-
let mask = 0xff >> (8 * n_bytes - n_bits);
26+
let mask = 0xffu8 >> (8 * n_bytes - n_bits);
2727

2828
loop {
2929
rng.fill_bytes(&mut bytes[..n_bytes]);

src/limb/shl.rs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,30 @@
11
//! Limb left bitshift
22
3-
use crate::{Limb, Word};
3+
use crate::Limb;
44
use core::ops::{Shl, ShlAssign};
55

66
impl Limb {
7-
/// Computes `self << rhs`.
8-
/// Panics if `rhs` overflows `Limb::BITS`.
7+
/// Computes `self << shift`.
8+
/// Panics if `shift` overflows `Limb::BITS`.
99
#[inline(always)]
10-
pub const fn shl(self, rhs: Self) -> Self {
11-
Limb(self.0 << rhs.0)
10+
pub const fn shl(self, shift: u32) -> Self {
11+
Limb(self.0 << shift)
1212
}
1313
}
1414

15-
impl Shl for Limb {
15+
impl Shl<u32> for Limb {
1616
type Output = Self;
1717

1818
#[inline(always)]
19-
fn shl(self, rhs: Self) -> Self::Output {
20-
self.shl(rhs)
19+
fn shl(self, shift: u32) -> Self::Output {
20+
self.shl(shift)
2121
}
2222
}
2323

24-
impl Shl<usize> for Limb {
25-
type Output = Self;
26-
27-
#[inline(always)]
28-
fn shl(self, rhs: usize) -> Self::Output {
29-
self.shl(Limb(rhs as Word))
30-
}
31-
}
32-
33-
impl ShlAssign for Limb {
34-
#[inline(always)]
35-
fn shl_assign(&mut self, other: Self) {
36-
*self = self.shl(other);
37-
}
38-
}
39-
40-
impl ShlAssign<usize> for Limb {
24+
impl ShlAssign<u32> for Limb {
4125
#[inline(always)]
42-
fn shl_assign(&mut self, other: usize) {
43-
*self = self.shl(Limb(other as Word));
26+
fn shl_assign(&mut self, shift: u32) {
27+
*self = self.shl(shift);
4428
}
4529
}
4630

0 commit comments

Comments
 (0)