1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
//! Heap-allocated big unsigned integers.
mod add;
mod cmp;
use crate::{Limb, Word};
use alloc::{vec, vec::Vec};
use core::fmt;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// Fixed-precision heap-allocated big unsigned integer.
///
/// Alternative to the stack-allocated [`Uint`][`crate::Uint`] but with a
/// fixed precision chosen at runtime instead of compile time.
///
/// Unlike many other heap-allocated big integer libraries, this type is not
/// arbitrary precision and will wrap at its fixed-precision rather than
/// automatically growing.
#[derive(Clone, Default)]
pub struct BoxedUint {
/// Inner limb vector. Stored from least significant to most significant.
limbs: Vec<Limb>,
}
impl BoxedUint {
/// Get the value `0`, represented as succinctly as possible.
pub fn zero() -> Self {
Self::default()
}
/// Get the value `1`, represented as succinctly as possible.
pub fn one() -> Self {
Self {
limbs: vec![Limb::ONE; 1],
}
}
/// Create a new [`BoxedUint`] with the given number of bits of precision.
///
/// Returns `None` if the number of bits is not a multiple of the
/// [`Limb`] size.
pub fn new(bits_precision: usize) -> Option<Self> {
if bits_precision == 0 || bits_precision % Limb::BITS != 0 {
return None;
}
let nlimbs = bits_precision / Limb::BITS;
Some(Self {
limbs: vec![Limb::ZERO; nlimbs],
})
}
/// Get the maximum value for a given number of bits of precision.
///
/// Returns `None` if the number of bits is not a multiple of the
/// [`Limb`] size.
pub fn max(bits_precision: usize) -> Option<Self> {
let mut ret = Self::new(bits_precision)?;
for limb in &mut ret.limbs {
*limb = Limb::MAX;
}
Some(ret)
}
/// Create a [`BoxedUint`] from an array of [`Word`]s (i.e. word-sized unsigned
/// integers).
#[inline]
pub fn from_words(words: &[Word]) -> Self {
Self {
limbs: words.iter().copied().map(Into::into).collect(),
}
}
/// Create an array of [`Word`]s (i.e. word-sized unsigned integers) from
/// a [`BoxedUint`].
#[inline]
pub fn to_words(&self) -> Vec<Word> {
self.limbs.iter().copied().map(Into::into).collect()
}
/// Borrow the inner limbs as a slice of [`Word`]s.
pub fn as_words(&self) -> &[Word] {
// SAFETY: `Limb` is a `repr(transparent)` newtype for `Word`
#[allow(trivial_casts, unsafe_code)]
unsafe {
&*((self.limbs.as_slice() as *const _) as *const [Word])
}
}
/// Borrow the inner limbs as a mutable array of [`Word`]s.
pub fn as_words_mut(&mut self) -> &mut [Word] {
// SAFETY: `Limb` is a `repr(transparent)` newtype for `Word`
#[allow(trivial_casts, unsafe_code)]
unsafe {
&mut *((self.limbs.as_mut_slice() as *mut _) as *mut [Word])
}
}
/// Borrow the limbs of this [`BoxedUint`].
pub fn as_limbs(&self) -> &[Limb] {
self.limbs.as_ref()
}
/// Borrow the limbs of this [`BoxedUint`] mutably.
pub fn as_limbs_mut(&mut self) -> &mut [Limb] {
self.limbs.as_mut()
}
/// Convert this [`BoxedUint`] into its inner limbs.
pub fn to_limbs(&self) -> Vec<Limb> {
self.limbs.clone()
}
/// Convert this [`BoxedUint`] into its inner limbs.
pub fn into_limbs(self) -> Vec<Limb> {
self.limbs
}
/// Get the precision of this [`BoxedUint`] in bits.
pub fn bits(&self) -> usize {
self.limbs.len() * Limb::BITS
}
/// Sort two [`BoxedUint`]s by precision, returning a tuple of the shorter
/// followed by the longer, or the original order if their precision is
/// equal.
fn sort_by_precision<'a>(a: &'a Self, b: &'a Self) -> (&'a Self, &'a Self) {
if a.limbs.len() <= b.limbs.len() {
(a, b)
} else {
(b, a)
}
}
/// Perform a carry chain-like operation over the limbs of the inputs,
/// constructing a result from the returned limbs and carry.
///
/// If one of the two values has fewer limbs than the other, passes
/// [`Limb::ZERO`] as the value for that limb.
fn chain<F>(a: &Self, b: &Self, mut carry: Limb, f: F) -> (Self, Limb)
where
F: Fn(Limb, Limb, Limb) -> (Limb, Limb),
{
let (shorter, longer) = Self::sort_by_precision(a, b);
let mut limbs = Vec::with_capacity(longer.limbs.len());
for i in 0..longer.limbs.len() {
let &a = shorter.limbs.get(i).unwrap_or(&Limb::ZERO);
let &b = longer.limbs.get(i).unwrap_or(&Limb::ZERO);
let (limb, c) = f(a, b, carry);
limbs.push(limb);
carry = c;
}
(Self { limbs }, carry)
}
}
impl AsRef<[Word]> for BoxedUint {
fn as_ref(&self) -> &[Word] {
self.as_words()
}
}
impl AsMut<[Word]> for BoxedUint {
fn as_mut(&mut self) -> &mut [Word] {
self.as_words_mut()
}
}
impl AsRef<[Limb]> for BoxedUint {
fn as_ref(&self) -> &[Limb] {
self.as_limbs()
}
}
impl AsMut<[Limb]> for BoxedUint {
fn as_mut(&mut self) -> &mut [Limb] {
self.as_limbs_mut()
}
}
impl fmt::Debug for BoxedUint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BoxedUint(0x{self:X})")
}
}
impl fmt::Display for BoxedUint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(self, f)
}
}
impl fmt::LowerHex for BoxedUint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.limbs.is_empty() {
return fmt::LowerHex::fmt(&Limb::ZERO, f);
}
for limb in self.limbs.iter().rev() {
fmt::LowerHex::fmt(limb, f)?;
}
Ok(())
}
}
impl fmt::UpperHex for BoxedUint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.limbs.is_empty() {
return fmt::LowerHex::fmt(&Limb::ZERO, f);
}
for limb in self.limbs.iter().rev() {
fmt::UpperHex::fmt(limb, f)?;
}
Ok(())
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for BoxedUint {
fn zeroize(&mut self) {
self.limbs.zeroize();
}
}