Skip to content

Commit d0f31a1

Browse files
committed
perf: non-allocating mul_mod
1 parent b041f09 commit d0f31a1

File tree

2 files changed

+15
-14
lines changed

2 files changed

+15
-14
lines changed

src/base_convert.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
9595
///
9696
/// * [`BaseConvertError::InvalidBase`] if the base is less than 2.
9797
/// * [`BaseConvertError::InvalidDigit`] if a digit is out of range.
98-
/// * [`BaseConvertError::Overflow`] if the number is too large to
99-
/// fit.
98+
/// * [`BaseConvertError::Overflow`] if the number is too large to fit.
10099
#[inline]
101100
pub fn from_base_le<I>(base: u64, digits: I) -> Result<Self, BaseConvertError>
102101
where
@@ -155,8 +154,7 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
155154
///
156155
/// * [`BaseConvertError::InvalidBase`] if the base is less than 2.
157156
/// * [`BaseConvertError::InvalidDigit`] if a digit is out of range.
158-
/// * [`BaseConvertError::Overflow`] if the number is too large to
159-
/// fit.
157+
/// * [`BaseConvertError::Overflow`] if the number is too large to fit.
160158
#[inline]
161159
pub fn from_base_be<I: IntoIterator<Item = u64>>(
162160
base: u64,

src/modular.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,28 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
5252
/// some pre-computation.
5353
#[inline]
5454
#[must_use]
55-
#[cfg(feature = "alloc")] // see comments below
5655
pub fn mul_mod(self, rhs: Self, mut modulus: Self) -> Self {
5756
if modulus == Self::ZERO {
5857
return Self::ZERO;
5958
}
59+
60+
// Allocate at least `nlimbs(2 * BITS)` limbs to store the product. This array
61+
// casting is a workaround for `generic_const_exprs` not being stable.
62+
let mut product = [[0u64; 2]; LIMBS];
63+
let product_len = crate::nlimbs(2 * BITS);
64+
debug_assert!(2 * LIMBS >= product_len);
65+
// SAFETY: `[[u64; 2]; LIMBS] == [u64; 2 * LIMBS] >= [u64; nlimbs(2 * BITS)]`.
66+
let product = unsafe {
67+
core::slice::from_raw_parts_mut(product.as_mut_ptr().cast::<u64>(), product_len)
68+
};
69+
6070
// Compute full product.
61-
// The challenge here is that Rust doesn't allow us to create a
62-
// `Uint<2 * BITS, _>` for the intermediate result. Otherwise
63-
// we could just use a `widening_mul`. So instead we allocate from heap.
64-
// Alternatively we could use `alloca`, but that is blocked on
65-
// See <https://github.com/rust-lang/rust/issues/48055>
66-
let mut product = vec![0; crate::nlimbs(2 * BITS)];
67-
let overflow = algorithms::addmul(&mut product, self.as_limbs(), rhs.as_limbs());
71+
let overflow = algorithms::addmul(product, self.as_limbs(), rhs.as_limbs());
6872
debug_assert!(!overflow);
6973

7074
// Compute modulus using `div_rem`.
7175
// This stores the remainder in the divisor, `modulus`.
72-
algorithms::div(&mut product, &mut modulus.limbs);
76+
algorithms::div(product, &mut modulus.limbs);
7377

7478
modulus
7579
}
@@ -79,7 +83,6 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
7983
/// Returns zero if the modulus is zero.
8084
#[inline]
8185
#[must_use]
82-
#[cfg(feature = "alloc")] // see comments in mul_mod
8386
pub fn pow_mod(mut self, mut exp: Self, modulus: Self) -> Self {
8487
if modulus == Self::ZERO || modulus <= Self::from(1) {
8588
// Also covers Self::BITS == 0

0 commit comments

Comments
 (0)