Skip to content

Commit 5cd6669

Browse files
committed
Add constant-time leading_zeros(), trailing_zeros(), bits(), and bit() for Uint
1 parent fc7a4fe commit 5cd6669

2 files changed

Lines changed: 134 additions & 4 deletions

File tree

src/limb/bits.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,14 @@ impl Limb {
55
pub const fn bits(self) -> usize {
66
Limb::BIT_SIZE - (self.0.leading_zeros() as usize)
77
}
8+
9+
/// 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
12+
}
13+
14+
/// 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
17+
}
818
}

src/uint/bits.rs

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,139 @@ impl<const LIMBS: usize> Uint<LIMBS> {
3030
)
3131
.0 as usize
3232
}
33+
34+
/// Calculate the number of leading zeros in the binary representation of this number.
35+
pub const fn leading_zeros(self) -> usize {
36+
let limbs = self.as_limbs();
37+
38+
let mut count: Word = 0;
39+
let mut i = LIMBS;
40+
let mut mask = Word::MAX;
41+
while i > 0 {
42+
i -= 1;
43+
let l = limbs[i];
44+
let z = l.leading_zeros() as Word;
45+
count += z & mask;
46+
mask &= !l.is_nonzero();
47+
}
48+
49+
count as usize
50+
}
51+
52+
/// Calculate the number of trailing zeros in the binary representation of this number.
53+
pub const fn trailing_zeros(self) -> usize {
54+
let limbs = self.as_limbs();
55+
56+
let mut count: Word = 0;
57+
let mut i = 0;
58+
let mut mask = Word::MAX;
59+
while i < LIMBS {
60+
let l = limbs[i];
61+
let z = l.trailing_zeros() as Word;
62+
count += z & mask;
63+
mask &= !l.is_nonzero();
64+
i += 1;
65+
}
66+
67+
count as usize
68+
}
69+
70+
/// Calculate the number of bits needed to represent this number.
71+
pub const fn bits(self) -> usize {
72+
LIMBS * Limb::BIT_SIZE - self.leading_zeros()
73+
}
74+
75+
/// Get the value of the bit at position `index`, as a 0- or 1-valued Word.
76+
/// Returns 0 for indices out of range.
77+
pub const fn bit(self, index: usize) -> Word {
78+
let limb_num = Limb((index / Limb::BIT_SIZE) as Word);
79+
let index_in_limb = index % Limb::BIT_SIZE;
80+
let index_mask = 1 << index_in_limb;
81+
82+
let limbs = self.as_words();
83+
84+
let mut result: Word = 0;
85+
let mut i = 0;
86+
while i < LIMBS {
87+
let bit = limbs[i] & index_mask;
88+
let is_right_limb = Limb::ct_eq(limb_num, Limb(i as Word));
89+
result |= bit & is_right_limb;
90+
i += 1;
91+
}
92+
93+
result >> index_in_limb
94+
}
3395
}
3496

3597
#[cfg(test)]
3698
mod tests {
37-
use crate::U128;
99+
use crate::U256;
100+
101+
fn uint_with_bits_at(positions: &[usize]) -> U256 {
102+
let mut result = U256::ZERO;
103+
for pos in positions {
104+
result |= U256::ONE << *pos;
105+
}
106+
result
107+
}
38108

39109
#[test]
40-
fn bit_vartime_ok() {
41-
let u = U128::from_be_hex("f0010000000000000001000000010000");
110+
fn bit_vartime() {
111+
let u = uint_with_bits_at(&[16, 48, 112, 127, 255]);
42112
assert_eq!(u.bit_vartime(0), 0);
43113
assert_eq!(u.bit_vartime(1), 0);
44114
assert_eq!(u.bit_vartime(16), 1);
45115
assert_eq!(u.bit_vartime(127), 1);
46-
assert_eq!(u.bit_vartime(130), 0);
116+
assert_eq!(u.bit_vartime(255), 1);
117+
assert_eq!(u.bit_vartime(256), 0);
118+
assert_eq!(u.bit_vartime(260), 0);
119+
}
120+
121+
#[test]
122+
fn bit() {
123+
let u = uint_with_bits_at(&[16, 48, 112, 127, 255]);
124+
assert_eq!(u.bit(0), 0);
125+
assert_eq!(u.bit(1), 0);
126+
assert_eq!(u.bit(16), 1);
127+
assert_eq!(u.bit(127), 1);
128+
assert_eq!(u.bit(255), 1);
129+
assert_eq!(u.bit(256), 0);
130+
assert_eq!(u.bit(260), 0);
131+
}
132+
133+
#[test]
134+
fn leading_zeros() {
135+
let u = uint_with_bits_at(&[256 - 16, 256 - 79, 256 - 207]);
136+
assert_eq!(u.leading_zeros() as u32, 15);
137+
138+
let u = uint_with_bits_at(&[256 - 79, 256 - 207]);
139+
assert_eq!(u.leading_zeros() as u32, 78);
140+
141+
let u = uint_with_bits_at(&[256 - 207]);
142+
assert_eq!(u.leading_zeros() as u32, 206);
143+
144+
let u = uint_with_bits_at(&[256 - 1, 256 - 75, 256 - 150]);
145+
assert_eq!(u.leading_zeros() as u32, 0);
146+
147+
let u = U256::ZERO;
148+
assert_eq!(u.leading_zeros() as u32, 256);
149+
}
150+
151+
#[test]
152+
fn trailing_zeros() {
153+
let u = uint_with_bits_at(&[16, 79, 150]);
154+
assert_eq!(u.trailing_zeros() as u32, 16);
155+
156+
let u = uint_with_bits_at(&[79, 150]);
157+
assert_eq!(u.trailing_zeros() as u32, 79);
158+
159+
let u = uint_with_bits_at(&[150, 207]);
160+
assert_eq!(u.trailing_zeros() as u32, 150);
161+
162+
let u = uint_with_bits_at(&[0, 150, 207]);
163+
assert_eq!(u.trailing_zeros() as u32, 0);
164+
165+
let u = U256::ZERO;
166+
assert_eq!(u.trailing_zeros() as u32, 256);
47167
}
48168
}

0 commit comments

Comments
 (0)