-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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`.
- Loading branch information
Showing
3 changed files
with
51 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
//! Bit manipulation functions. | ||
|
||
use crate::{BoxedUint, Limb, Zero}; | ||
use subtle::{ConditionallySelectable, ConstantTimeEq}; | ||
|
||
impl BoxedUint { | ||
/// Calculate the number of bits needed to represent this number, i.e. the index of the highest | ||
/// set bit. | ||
/// | ||
/// Use [`BoxedUint::bits_precision`] to get the total capacity of this integer. | ||
pub fn bits(&self) -> usize { | ||
// Use `u32` because `subtle` can't select on `usize` and it matches what `core` uses for | ||
// the return value of `leading_zeros` | ||
let mut leading_zeros = 0u32; | ||
let mut n = 0u32; | ||
|
||
for limb in self.limbs.iter().rev() { | ||
n.conditional_assign(&(n + 1), !limb.is_zero() | !n.ct_eq(&0)); | ||
|
||
// Set `leading_zeros` for the first nonzero limb we encounter | ||
leading_zeros.conditional_assign(&(limb.leading_zeros() as u32), n.ct_eq(&1)); | ||
} | ||
|
||
Limb::BITS * (n as usize) - (leading_zeros as usize) | ||
} | ||
|
||
/// Get the precision of this [`BoxedUint`] in bits. | ||
pub fn bits_precision(&self) -> usize { | ||
self.limbs.len() * Limb::BITS | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::BoxedUint; | ||
use hex_literal::hex; | ||
|
||
#[test] | ||
fn bits() { | ||
assert_eq!(0, BoxedUint::zero().bits()); | ||
assert_eq!(128, BoxedUint::max(128).bits()); | ||
|
||
let n1 = BoxedUint::from_be_slice(&hex!("000000000029ffffffffffffffffffff"), 128).unwrap(); | ||
assert_eq!(86, n1.bits()); | ||
|
||
let n2 = BoxedUint::from_be_slice(&hex!("00000000004000000000000000000000"), 128).unwrap(); | ||
assert_eq!(87, n2.bits()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters