Skip to content

Commit f07de91

Browse files
authored
Add BoxedUint::bits (#328)
Adds a function for counting the number of bits in a value, i.e. the index of the highest set bit. Since `BoxedUint` isn't `const fn`, this can be implemented in constant-time using `subtle`.
1 parent e334064 commit f07de91

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

src/uint/boxed.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod add;
44
mod add_mod;
55
mod bit_and;
6+
mod bits;
67
mod cmp;
78
pub(crate) mod encoding;
89
mod modular;
@@ -150,11 +151,6 @@ impl BoxedUint {
150151
self.limbs.len()
151152
}
152153

153-
/// Get the precision of this [`BoxedUint`] in bits.
154-
pub fn bits(&self) -> usize {
155-
self.limbs.len() * Limb::BITS
156-
}
157-
158154
/// Perform a carry chain-like operation over the limbs of the inputs,
159155
/// constructing a result from the returned limbs and carry which is
160156
/// widened to the same width as the widest input.

src/uint/boxed/bits.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//! Bit manipulation functions.
2+
3+
use crate::{BoxedUint, Limb, Zero};
4+
use subtle::{ConditionallySelectable, ConstantTimeEq};
5+
6+
impl BoxedUint {
7+
/// Calculate the number of bits needed to represent this number, i.e. the index of the highest
8+
/// set bit.
9+
///
10+
/// Use [`BoxedUint::bits_precision`] to get the total capacity of this integer.
11+
pub fn bits(&self) -> usize {
12+
// Use `u32` because `subtle` can't select on `usize` and it matches what `core` uses for
13+
// the return value of `leading_zeros`
14+
let mut leading_zeros = 0u32;
15+
let mut n = 0u32;
16+
17+
for limb in self.limbs.iter().rev() {
18+
n.conditional_assign(&(n + 1), !limb.is_zero() | !n.ct_eq(&0));
19+
20+
// Set `leading_zeros` for the first nonzero limb we encounter
21+
leading_zeros.conditional_assign(&(limb.leading_zeros() as u32), n.ct_eq(&1));
22+
}
23+
24+
Limb::BITS * (n as usize) - (leading_zeros as usize)
25+
}
26+
27+
/// Get the precision of this [`BoxedUint`] in bits.
28+
pub fn bits_precision(&self) -> usize {
29+
self.limbs.len() * Limb::BITS
30+
}
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use super::BoxedUint;
36+
use hex_literal::hex;
37+
38+
#[test]
39+
fn bits() {
40+
assert_eq!(0, BoxedUint::zero().bits());
41+
assert_eq!(128, BoxedUint::max(128).bits());
42+
43+
let n1 = BoxedUint::from_be_slice(&hex!("000000000029ffffffffffffffffffff"), 128).unwrap();
44+
assert_eq!(86, n1.bits());
45+
46+
let n2 = BoxedUint::from_be_slice(&hex!("00000000004000000000000000000000"), 128).unwrap();
47+
assert_eq!(87, n2.bits());
48+
}
49+
}

tests/boxed_uint_proptests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ proptest! {
4242
match Option::<BoxedUint>::from(a.checked_add(&b)) {
4343
Some(actual) => prop_assert_eq!(expected, to_biguint(&actual)),
4444
None => {
45-
let max = BoxedUint::max(a.bits());
45+
let max = BoxedUint::max(a.bits_precision());
4646
prop_assert!(expected > to_biguint(&max));
4747
}
4848
}

0 commit comments

Comments
 (0)