From acea36e0b45a6dd26d9254e94f0540ffa05cf9ce Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 23 Nov 2025 09:23:25 -0800 Subject: [PATCH 1/5] Implement `Split`/`Concat` for all `Uint`s --- benches/int.rs | 14 +++--- src/int/gcd.rs | 30 ++++++------- src/int/mul.rs | 12 ++--- src/int/mul_uint.rs | 7 +-- src/modular.rs | 8 ++-- src/modular/bingcd/xgcd.rs | 38 ++++++++-------- src/traits.rs | 71 +++++++----------------------- src/uint.rs | 52 ---------------------- src/uint/boxed/div.rs | 8 +--- src/uint/boxed/mul.rs | 8 +--- src/uint/concat.rs | 89 +++++++++++++++++++++++--------------- src/uint/div.rs | 37 +++------------- src/uint/extra_sizes.rs | 48 -------------------- src/uint/from.rs | 17 ++------ src/uint/gcd.rs | 55 ++++++++++------------- src/uint/macros.rs | 74 ------------------------------- src/uint/mul.rs | 45 ++++++------------- src/uint/mul_int.rs | 7 +-- src/uint/split.rs | 79 +++++++++++++++++++++------------ tests/monty_form.rs | 8 ++-- 20 files changed, 229 insertions(+), 478 deletions(-) diff --git a/benches/int.rs b/benches/int.rs index 922e05176..31c07253a 100644 --- a/benches/int.rs +++ b/benches/int.rs @@ -4,7 +4,7 @@ use rand_core::SeedableRng; use std::hint::black_box; use std::ops::Div; -use crypto_bigint::{I128, I256, I512, I1024, I2048, I4096, NonZero, Random}; +use crypto_bigint::{I128, I256, I512, I1024, I2048, I4096, NonZero, Random, nlimbs}; fn bench_mul(c: &mut Criterion) { let mut rng = ChaCha8Rng::from_seed([7u8; 32]); @@ -66,7 +66,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I128xI128", |b| { b.iter_batched( || (I128::random(&mut rng), I128::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I128::LIMBS }, { I256::LIMBS }>(&y)), BatchSize::SmallInput, ) }); @@ -74,7 +74,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I256xI256", |b| { b.iter_batched( || (I256::random(&mut rng), I256::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I256::LIMBS }, { I512::LIMBS }>(&y)), BatchSize::SmallInput, ) }); @@ -82,7 +82,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I512xI512", |b| { b.iter_batched( || (I512::random(&mut rng), I512::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I512::LIMBS }, { I1024::LIMBS }>(&y)), BatchSize::SmallInput, ) }); @@ -90,7 +90,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I1024xI1024", |b| { b.iter_batched( || (I1024::random(&mut rng), I1024::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I1024::LIMBS }, { I2048::LIMBS }>(&y)), BatchSize::SmallInput, ) }); @@ -98,7 +98,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I2048xI2048", |b| { b.iter_batched( || (I2048::random(&mut rng), I2048::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I2048::LIMBS }, { I4096::LIMBS }>(&y)), BatchSize::SmallInput, ) }); @@ -106,7 +106,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I4096xI4096", |b| { b.iter_batched( || (I4096::random(&mut rng), I4096::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul(&y)), + |(x, y)| black_box(x.concatenating_mul::<{ I4096::LIMBS }, { nlimbs!(8192) }>(&y)), BatchSize::SmallInput, ) }); diff --git a/src/int/gcd.rs b/src/int/gcd.rs index abc6e7d56..78ac920bf 100644 --- a/src/int/gcd.rs +++ b/src/int/gcd.rs @@ -312,7 +312,7 @@ impl Xgcd for OddInt { #[cfg(all(test, not(miri)))] mod tests { use crate::int::gcd::{IntXgcdOutput, NonZeroIntXgcdOutput, OddIntXgcdOutput}; - use crate::{ConcatMixed, Gcd, Int, Uint}; + use crate::{ConcatenatingMul, Gcd, Int, Uint}; use num_traits::Zero; impl From> for IntXgcdOutput { @@ -409,7 +409,7 @@ mod tests { rhs: Int, output: IntXgcdOutput, ) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let gcd = lhs.gcd(&rhs); assert_eq!(gcd, output.gcd); @@ -437,20 +437,20 @@ mod tests { assert_eq!( x.concatenating_mul(&lhs) .wrapping_add(&y.concatenating_mul(&rhs)), - *gcd.resize().as_int() + *gcd.resize::().as_int() ); } mod test_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatMixed, Gcd, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatenatingMul, Gcd, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, + U4096, U8192, Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, Int: Gcd>, { xgcd_test(lhs, rhs, lhs.xgcd(&rhs)) @@ -458,7 +458,7 @@ mod tests { fn run_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, Int: Gcd>, { test(Int::MIN, Int::MIN); @@ -505,13 +505,13 @@ mod tests { mod test_nonzero_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, - Uint, + ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let output = lhs.to_nz().unwrap().xgcd(&rhs.to_nz().unwrap()); xgcd_test(lhs, rhs, output.into()); @@ -519,7 +519,7 @@ mod tests { fn run_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { test(Int::MIN, Int::MIN); test(Int::MIN, Int::MINUS_ONE); @@ -556,13 +556,13 @@ mod tests { mod test_odd_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, - Uint, + ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let output = lhs.to_odd().unwrap().xgcd(&rhs.to_nz().unwrap()); xgcd_test(lhs, rhs, output.into()); @@ -570,7 +570,7 @@ mod tests { fn run_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let neg_max = Int::MAX.wrapping_neg(); test(neg_max, neg_max); diff --git a/src/int/mul.rs b/src/int/mul.rs index 9364ef7b6..643011640 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -4,7 +4,7 @@ use core::ops::{Mul, MulAssign}; use num_traits::WrappingMul; use subtle::CtOption; -use crate::{Checked, CheckedMul, ConcatMixed, ConstChoice, ConstCtOption, Int, Uint, Zero}; +use crate::{Checked, CheckedMul, ConstChoice, ConstCtOption, Int, Uint, Zero}; impl Int { /// Compute "wide" multiplication as a 3-tuple `(lo, hi, negate)`. @@ -51,10 +51,7 @@ impl Int { pub const fn concatenating_mul( &self, rhs: &Int, - ) -> Int - where - Uint: ConcatMixed, MixedOutput = Uint>, - { + ) -> Int { let (lhs_abs, lhs_sign) = self.abs_sign(); let (rhs_abs, rhs_sign) = rhs.abs_sign(); let product_abs = lhs_abs.concatenating_mul(&rhs_abs); @@ -76,10 +73,7 @@ impl Int { /// Squaring operations. impl Int { /// Square self, returning a concatenated "wide" result. - pub fn widening_square(&self) -> Uint - where - Uint: ConcatMixed, MixedOutput = Uint>, - { + pub fn widening_square(&self) -> Uint { self.abs().widening_square() } diff --git a/src/int/mul_uint.rs b/src/int/mul_uint.rs index e0868fbc9..f95ac235b 100644 --- a/src/int/mul_uint.rs +++ b/src/int/mul_uint.rs @@ -1,7 +1,7 @@ use core::ops::Mul; use subtle::CtOption; -use crate::{CheckedMul, ConcatMixed, ConstChoice, ConstCtOption, Int, Uint}; +use crate::{CheckedMul, ConstChoice, ConstCtOption, Int, Uint}; impl Int { /// Compute "wide" multiplication between an [`Int`] and [`Uint`] as 3-tuple `(lo, hi, negate)`. @@ -70,10 +70,7 @@ impl Int { pub const fn concatenating_mul_uint( &self, rhs: &Uint, - ) -> Int - where - Uint: ConcatMixed, MixedOutput = Uint>, - { + ) -> Int { let (lhs_abs, lhs_sign) = self.abs_sign(); let product_abs = lhs_abs.concatenating_mul(rhs); diff --git a/src/modular.rs b/src/modular.rs index 809814d3b..3646f88da 100644 --- a/src/modular.rs +++ b/src/modular.rs @@ -55,7 +55,7 @@ pub trait Retrieve { #[cfg(test)] mod tests { use crate::{ - NonZero, U64, U128, U256, Uint, const_monty_params, + NonZero, U64, U128, U256, U512, Uint, const_monty_params, modular::{ const_monty_form::{ConstMontyForm, ConstMontyParams}, mul::{mul_montgomery_form, square_montgomery_form}, @@ -122,7 +122,7 @@ mod tests { #[test] fn test_reducing_r2_wide() { // Divide the value ONE^2 by R, which should equal ONE - let (lo, hi) = Modulus256::PARAMS.one.square().split(); + let (lo, hi) = Modulus256::PARAMS.one.square::<{ nlimbs!(512) }>().split(); assert_eq!( montgomery_reduction::<{ Modulus256::LIMBS }>( &(lo, hi), @@ -158,7 +158,7 @@ mod tests { // Computing xR mod modulus without Montgomery reduction let (lo, hi) = x.widening_mul(&Modulus256::PARAMS.one); - let c = lo.concat(&hi); + let c: U512 = lo.concat(&hi); let red = c.rem_vartime(&NonZero::new(Modulus256::PARAMS.modulus.0.concat(&U256::ZERO)).unwrap()); let (lo, hi) = red.split(); @@ -287,7 +287,7 @@ mod tests { // Computing xR mod modulus without Montgomery reduction let (lo, hi) = x.widening_mul(&Modulus256::PARAMS.one); - let c = lo.concat(&hi); + let c: U512 = lo.concat(&hi); let red = c.rem_vartime(&NonZero::new(Modulus256::PARAMS.modulus.0.concat(&U256::ZERO)).unwrap()); let (lo, hi) = red.split(); diff --git a/src/modular/bingcd/xgcd.rs b/src/modular/bingcd/xgcd.rs index fd5631ec2..0813352ba 100644 --- a/src/modular/bingcd/xgcd.rs +++ b/src/modular/bingcd/xgcd.rs @@ -369,7 +369,7 @@ impl Uint { #[cfg(all(test, not(miri)))] mod tests { use crate::modular::bingcd::xgcd::PatternXgcdOutput; - use crate::{ConcatMixed, Gcd, Uint}; + use crate::{ConcatenatingMul, Gcd, Uint}; use core::ops::Div; use num_traits::Zero; @@ -584,7 +584,7 @@ mod tests { rhs: Uint, output: PatternXgcdOutput, ) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { // Test the gcd assert_eq!(lhs.gcd(&rhs), output.gcd, "{lhs} {rhs}"); @@ -598,7 +598,7 @@ mod tests { let (x, y) = output.bezout_coefficients(); assert_eq!( x.concatenating_mul_uint(&lhs) + y.concatenating_mul_uint(&rhs), - *output.gcd.resize().as_int(), + *output.gcd.resize::().as_int(), ); // Test the Bezout coefficients for minimality @@ -613,15 +613,15 @@ mod tests { mod test_binxgcd_nz { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::{ - ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, - Uint, + ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn binxgcd_nz_test( lhs: Uint, rhs: Uint, ) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let output = lhs.to_odd().unwrap().binxgcd_nz(&rhs.to_nz().unwrap()); test_xgcd(lhs, rhs, output); @@ -629,7 +629,7 @@ mod tests { fn binxgcd_nz_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let max_int = *Int::MAX.as_uint(); let int_abs_min = Int::MIN.abs(); @@ -665,15 +665,15 @@ mod tests { mod test_classic_binxgcd { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::{ - ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, - Uint, + ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn classic_binxgcd_test( lhs: Uint, rhs: Uint, ) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let output = lhs .to_odd() @@ -685,7 +685,7 @@ mod tests { fn classic_binxgcd_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let max_int = *Int::MAX.as_uint(); @@ -718,13 +718,13 @@ mod tests { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::modular::bingcd::xgcd::{DOUBLE_SUMMARY_LIMBS, SUMMARY_BITS, SUMMARY_LIMBS}; use crate::{ - ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, - Uint, + ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn test(lhs: Uint, rhs: Uint) where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let output = lhs .to_odd() @@ -736,7 +736,7 @@ mod tests { fn run_tests() where - Uint: ConcatMixed, MixedOutput = Uint>, + Uint: ConcatenatingMul>, { let upper_bound = *Int::MAX.as_uint(); test(Uint::ONE, Uint::ONE); @@ -771,10 +771,10 @@ mod tests { a.compact::(U256::BITS) < b.compact::(U256::BITS) ); - test(a, b); + test::<{ U256::LIMBS }, { U512::LIMBS }>(a, b); // Case #2: a < b but a.compact() > b.compact() - test(b, a); + test::<{ U256::LIMBS }, { U512::LIMBS }>(b, a); // Case #3: a > b but a.compact() = b.compact() let a = U256::from_be_hex( @@ -788,10 +788,10 @@ mod tests { a.compact::(U256::BITS), b.compact::(U256::BITS) ); - test(a, b); + test::<{ U256::LIMBS }, { U512::LIMBS }>(a, b); // Case #4: a < b but a.compact() = b.compact() - test(b, a); + test::<{ U256::LIMBS }, { U512::LIMBS }>(b, a); } #[test] diff --git a/src/traits.rs b/src/traits.rs index 1a49c2f15..9f6889b09 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -595,45 +595,17 @@ pub trait CheckedSub: Sized { fn checked_sub(&self, rhs: &Rhs) -> CtOption; } -/// Concatenate two numbers into a "wide" double-width value, using the `hi` value as the most -/// significant portion of the resulting value. -pub trait Concat: ConcatMixed { - /// Concatenated output: twice the width of `Self`. - type Output: Integer; - - /// Concatenate the two halves, with `self` as least significant and `hi` as the most significant. - fn concat(&self, hi: &Self) -> Self::Output { - self.concat_mixed(hi) - } -} - -/// Concatenate two numbers into a "wide" combined-width value, using the `hi` value as the most -/// significant value. -pub trait ConcatMixed { - /// Concatenated output: combination of `Self` and `Hi`. - type MixedOutput: Integer; - +/// Concatenate two numbers into a "wide" combined-width value. +pub trait Concat { /// Concatenate the two values, with `self` as least significant and `hi` as the most /// significant. - fn concat_mixed(&self, hi: &Hi) -> Self::MixedOutput; + fn concat(&self, hi: &Hi) -> Output; } -/// Split a number in half, returning the least significant half followed by the most significant. -pub trait Split: SplitMixed { - /// Split output: low/high components of the value. - type Output; - - /// Split this number in half, returning its low and high components respectively. - fn split(&self) -> (Self::Output, Self::Output) { - self.split_mixed() - } -} - -/// Split a number into parts, returning the least significant part followed by the most -/// significant. -pub trait SplitMixed { +/// Split a number into parts. +pub trait Split { /// Split this number into parts, returning its low and high components respectively. - fn split_mixed(&self) -> (Lo, Hi); + fn split(&self) -> (Lo, Hi); } /// Encoding support. @@ -727,12 +699,6 @@ pub trait DivRemLimb: Sized { fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb); } -/// Support for calculating the remainder of two differently sized integers. -pub trait RemMixed: Sized { - /// Calculate the remainder of `self` by the `reductor`. - fn rem_mixed(&self, reductor: &NonZero) -> Reductor; -} - /// Modular reduction from a larger value `T`. /// /// This can be seen as fixed modular reduction, where the modulus is fixed at compile time @@ -900,33 +866,26 @@ pub trait Invert { /// Widening multiply: returns a value with a number of limbs equal to the sum of the inputs. #[deprecated(since = "0.7.0", note = "please use `ConcatenatingMul` instead")] -pub trait WideningMul: Sized { - /// Output of the widening multiplication. - type Output: Integer; - +pub trait WideningMul: Sized { /// Perform widening multiplication. - fn widening_mul(&self, rhs: Rhs) -> Self::Output; + fn widening_mul(&self, rhs: Rhs) -> Output; } #[allow(deprecated)] -impl WideningMul for T +impl WideningMul for T where - T: ConcatenatingMul, + T: ConcatenatingMul, { - type Output = >::Output; - - fn widening_mul(&self, rhs: Rhs) -> Self::Output { + fn widening_mul(&self, rhs: Rhs) -> Output { self.concatenating_mul(rhs) } } -/// Widening multiply: returns a value with a number of limbs equal to the sum of the inputs. -pub trait ConcatenatingMul: Sized { - /// Output of the widening multiplication. - type Output: Integer; - +/// Widening multiply: returns a value with a number of limbs greater than or equal to +/// the sum of the inputs. +pub trait ConcatenatingMul: Sized { /// Perform widening multiplication. - fn concatenating_mul(&self, rhs: Rhs) -> Self::Output; + fn concatenating_mul(&self, rhs: Rhs) -> Output; } /// Left shifts, variable time in `shift`. diff --git a/src/uint.rs b/src/uint.rs index c27ae6a81..3effff4c2 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -475,58 +475,6 @@ impl_uint_aliases! { (U544, 544, "544-bit") // For NIST P-521 } -#[cfg(target_pointer_width = "32")] -impl_uint_concat_split_even! { - U64, -} - -// Implement concat and split for double-width Uint sizes: these should be -// multiples of 128 bits. -impl_uint_concat_split_even! { - U128, - U256, - U384, - U512, - U640, - U768, - U896, - U1024, - U1280, - U1536, - U1792, - U2048, - U3072, - U3584, - U4096, - U4224, - U4352, - U6144, - U8192, - U16384, -} - -// Implement mixed concat, split and reduce for combinations not implemented by -// impl_uint_concat_split_even. The numbers represent the size of each -// component Uint in multiple of 64 bits. For example, -// (U256, [1, 3]) will allow splitting U256 into (U64, U192) as well as -// (U192, U64), while the (U128, U128) combination is already covered. -impl_uint_concat_split_mixed! { - (U192, [1, 2]), - (U256, [1, 3]), - (U320, [1, 2, 3, 4]), - (U384, [1, 2, 4, 5]), - (U448, [1, 2, 3, 4, 5, 6]), - (U512, [1, 2, 3, 5, 6, 7]), - (U576, [1, 2, 3, 4, 5, 6, 7, 8]), - (U640, [1, 2, 3, 4, 6, 7, 8, 9]), - (U704, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), - (U768, [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]), - (U832, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), - (U896, [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13]), - (U960, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]), - (U1024, [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]), -} - #[cfg(feature = "extra-sizes")] mod extra_sizes; mod mul_int; diff --git a/src/uint/boxed/div.rs b/src/uint/boxed/div.rs index 8d3820690..6ff7e4ff2 100644 --- a/src/uint/boxed/div.rs +++ b/src/uint/boxed/div.rs @@ -2,7 +2,7 @@ use crate::{ BoxedUint, CheckedDiv, ConstantTimeSelect, DivRemLimb, DivVartime, Limb, NonZero, Reciprocal, - RemLimb, RemMixed, UintRef, Wrapping, + RemLimb, UintRef, Wrapping, }; use core::ops::{Div, DivAssign, Rem, RemAssign}; use subtle::CtOption; @@ -287,12 +287,6 @@ impl RemLimb for BoxedUint { } } -impl RemMixed for BoxedUint { - fn rem_mixed(&self, reductor: &NonZero) -> BoxedUint { - Self::div_rem_vartime(self, reductor).1 - } -} - #[cfg(test)] mod tests { use crate::{DivVartime, Resize, Zero}; diff --git a/src/uint/boxed/mul.rs b/src/uint/boxed/mul.rs index d4add6dee..aaa914fde 100644 --- a/src/uint/boxed/mul.rs +++ b/src/uint/boxed/mul.rs @@ -161,18 +161,14 @@ impl MulAssign<&Wrapping> for Wrapping { } } -impl ConcatenatingMul for BoxedUint { - type Output = Self; - +impl ConcatenatingMul for BoxedUint { #[inline] fn concatenating_mul(&self, rhs: BoxedUint) -> Self { self.mul(&rhs) } } -impl ConcatenatingMul<&BoxedUint> for BoxedUint { - type Output = Self; - +impl ConcatenatingMul for BoxedUint { #[inline] fn concatenating_mul(&self, rhs: &BoxedUint) -> Self { self.mul(rhs) diff --git a/src/uint/concat.rs b/src/uint/concat.rs index d16a37cf9..04c42e9c0 100644 --- a/src/uint/concat.rs +++ b/src/uint/concat.rs @@ -1,50 +1,71 @@ -use crate::{Concat, ConcatMixed, Limb, Uint}; +use crate::{Concat, Uint}; + +const fn concat( + lo: &Uint, + hi: &Uint, +) -> Uint { + const { + if L + H != O { + panic!(concat![ + "The size of the declared type of `Uint` concatenation is not equal to ", + "the sum of the sizes of argument types. ", + ]); + } + } + + let mut result = Uint::::ZERO; + + let mut i = 0; + while i < L { + result.limbs[i] = lo.limbs[i]; + i += 1; + } + while i < L + H { + result.limbs[i] = hi.limbs[i - L]; + i += 1; + } + + result +} + +impl Concat, Uint> for Uint { + /// Concatenate the two values, with `self` as least significant and `hi` as the most + /// significant. + /// + ///
+ /// The sum of input lengths must be equal to the output length. + ///
+ fn concat(&self, hi: &Uint) -> Uint { + self.concat_mixed(hi) + } +} impl Uint { /// Concatenate the two values, with `self` as least significant and `hi` as the most /// significant. - pub const fn concat(&self, hi: &Self) -> Uint - where - Self: Concat>, - { - Uint::concat_mixed(self, hi) + /// + ///
+ /// The sum of input lengths must be equal to the output length. + ///
+ pub const fn concat(&self, hi: &Self) -> Uint { + concat(self, hi) } - /// Concatenate the two values, with `lo` as least significant and `hi` + /// Concatenate the two values, with `self` as least significant and `hi` /// as the most significant. + /// + ///
+ /// The sum of input lengths must be equal to the output length. + ///
#[inline] - pub const fn concat_mixed(lo: &Uint, hi: &Uint) -> Uint - where - Self: ConcatMixed, MixedOutput = Uint>, - { - let top = L + H; - let top = if top < O { top } else { O }; - let mut limbs = [Limb::ZERO; O]; - let mut i = 0; - - while i < top { - if i < L { - limbs[i] = lo.limbs[i]; - } else { - limbs[i] = hi.limbs[i - L]; - } - i += 1; - } - - Uint { limbs } + pub const fn concat_mixed(&self, hi: &Uint) -> Uint { + concat(self, hi) } } -impl Concat for T -where - T: ConcatMixed, -{ - type Output = Self::MixedOutput; -} - #[cfg(test)] mod tests { - use crate::{ConcatMixed, U64, U128, U192}; + use crate::{U64, U128, U192}; #[test] fn concat() { diff --git a/src/uint/div.rs b/src/uint/div.rs index aa674520a..fd5e5bbc4 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -668,9 +668,8 @@ impl RemLimb for Uint { #[cfg(test)] mod tests { - use crate::{ - DivVartime, Limb, NonZero, RemMixed, U64, U128, U256, U512, U896, U1024, Uint, Word, Zero, - }; + use crate::{DivVartime, Limb, NonZero, U64, U128, U256, U512, U896, U1024, Uint, Word, Zero}; + use core::ops::Rem; #[cfg(feature = "rand")] use {crate::Random, chacha20::ChaCha8Rng, rand_core::RngCore, rand_core::SeedableRng}; @@ -932,9 +931,9 @@ mod tests { "7A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", ]); let y = U128::from_u64(1234567890987654321); - let rem = x.rem_mixed(&y.to_nz().unwrap()); + let rem = x.rem(&y.to_nz().unwrap()); - let y2: U1024 = U128::concat_mixed(&y, &U896::ZERO); + let y2: U1024 = y.concat_mixed(&U896::ZERO); let rem_control = x.rem(&NonZero::new(y2).unwrap()); assert_eq!(rem.bits(), rem_control.bits()); @@ -955,9 +954,9 @@ mod tests { "7A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", ]); let y = U512::from_u64(1234567890987654321); - let rem: U512 = x.rem_mixed(&y.to_nz().unwrap()); + let rem: U512 = x.rem(&y.to_nz().unwrap()); - let y_wide = U512::concat_mixed(&y, &U512::ZERO); + let y_wide = y.concat_mixed(&U512::ZERO); let rem_control: U1024 = x.rem(&NonZero::new(y_wide).unwrap()); assert_eq!(rem.bits(), rem_control.bits()); @@ -969,30 +968,6 @@ mod tests { ); } - #[test] - fn rem_mixed_through_traits() { - struct A { - t: T, - u: U, - } - impl A - where - T: RemMixed, - U: Clone + Zero, - { - fn reduce_t_by_u(&self) -> U { - let rhs = &NonZero::new(self.u.clone()).unwrap(); - self.t.rem_mixed(rhs) - } - } - - let a = A { - t: U1024::from(1234567890u64), - u: U128::from(456u64), - }; - assert_eq!(a.reduce_t_by_u(), U128::from(330u64)); - } - #[test] fn div_vartime_through_traits() { struct A { diff --git a/src/uint/extra_sizes.rs b/src/uint/extra_sizes.rs index fb639c713..a0b57ad99 100644 --- a/src/uint/extra_sizes.rs +++ b/src/uint/extra_sizes.rs @@ -110,51 +110,3 @@ impl_uint_aliases! { (U8064, 8064, "8064-bit"), (U8128, 8128, "8128-bit") } - -impl_uint_concat_split_even! { - U1152, - U1408, - U1664, - U1920, - U2176, - U2304, - U2432, - U2560, - U2688, - U2816, - U2944, - U3200, - U3328, - U3456, - U3712, - U3840, - U3968, - U4480, - U4608, - U4736, - U4864, - U4992, - U5120, - U5248, - U5376, - U5504, - U5632, - U5760, - U5888, - U6016, - U6272, - U6400, - U6528, - U6656, - U6784, - U6912, - U7040, - U7168, - U7296, - U7424, - U7552, - U7680, - U7808, - U7936, - U8064, -} diff --git a/src/uint/from.rs b/src/uint/from.rs index beabee201..7ebe2c703 100644 --- a/src/uint/from.rs +++ b/src/uint/from.rs @@ -1,6 +1,6 @@ //! `From`-like conversions for [`Uint`]. -use crate::{ConcatMixed, Limb, SplitMixed, U64, U128, Uint, WideWord, Word}; +use crate::{Limb, U64, U128, Uint, WideWord, Word}; impl Uint { /// Create a [`Uint`] from a `u8` (const-friendly) @@ -196,28 +196,19 @@ impl From for Uint { } } -impl From<(Uint, Uint)> for Uint -where - Uint: ConcatMixed, MixedOutput = Uint>, -{ +impl From<(Uint, Uint)> for Uint { fn from(nums: (Uint, Uint)) -> Uint { nums.0.concat_mixed(&nums.1) } } -impl From<&(Uint, Uint)> for Uint -where - Uint: ConcatMixed, MixedOutput = Uint>, -{ +impl From<&(Uint, Uint)> for Uint { fn from(nums: &(Uint, Uint)) -> Uint { nums.0.concat_mixed(&nums.1) } } -impl From> for (Uint, Uint) -where - Uint: SplitMixed, Uint>, -{ +impl From> for (Uint, Uint) { fn from(num: Uint) -> (Uint, Uint) { num.split_mixed() } diff --git a/src/uint/gcd.rs b/src/uint/gcd.rs index 2fe1934e0..db62565ec 100644 --- a/src/uint/gcd.rs +++ b/src/uint/gcd.rs @@ -428,15 +428,10 @@ mod tests { } mod xgcd { - use crate::{ - Concat, Gcd, Int, U64, U128, U256, U512, U1024, U2048, U4096, U8192, U16384, Uint, - }; + use crate::{Gcd, Int, U64, U128, U256, U512, U1024, U2048, U4096, U8192, U16384, Uint}; use core::ops::Div; - fn test(lhs: Uint, rhs: Uint) - where - Uint: Concat>, - { + fn test(lhs: Uint, rhs: Uint) { let output = lhs.xgcd(&rhs); assert_eq!(output.gcd, lhs.gcd(&rhs)); @@ -447,32 +442,30 @@ mod tests { let (x, y) = output.bezout_coefficients(); assert_eq!( - x.concatenating_mul_uint(&lhs) + y.concatenating_mul_uint(&rhs), + x.concatenating_mul_uint::(&lhs) + + y.concatenating_mul_uint::(&rhs), *output.gcd.resize().as_int() ); } - fn run_tests() - where - Uint: Concat>, - { + fn run_tests() { let min = Int::MIN.abs(); - test(Uint::ZERO, Uint::ZERO); - test(Uint::ZERO, Uint::ONE); - test(Uint::ZERO, min); - test(Uint::ZERO, Uint::MAX); - test(Uint::ONE, Uint::ZERO); - test(Uint::ONE, Uint::ONE); - test(Uint::ONE, min); - test(Uint::ONE, Uint::MAX); - test(min, Uint::ZERO); - test(min, Uint::ONE); - test(min, Int::MIN.abs()); - test(min, Uint::MAX); - test(Uint::MAX, Uint::ZERO); - test(Uint::MAX, Uint::ONE); - test(Uint::MAX, min); - test(Uint::MAX, Uint::MAX); + test::(Uint::ZERO, Uint::ZERO); + test::(Uint::ZERO, Uint::ONE); + test::(Uint::ZERO, min); + test::(Uint::ZERO, Uint::MAX); + test::(Uint::ONE, Uint::ZERO); + test::(Uint::ONE, Uint::ONE); + test::(Uint::ONE, min); + test::(Uint::ONE, Uint::MAX); + test::(min, Uint::ZERO); + test::(min, Uint::ONE); + test::(min, Int::MIN.abs()); + test::(min, Uint::MAX); + test::(Uint::MAX, Uint::ZERO); + test::(Uint::MAX, Uint::ONE); + test::(Uint::MAX, min); + test::(Uint::MAX, Uint::MAX); } #[test] @@ -496,7 +489,7 @@ mod tests { let b = U256::from_be_hex( "000000000000345EAEDFA8CA03C1F0F5B578A787FE2D23B82A807F178B37FD8E", ); - test(a, b); + test::<{ nlimbs!(256) }, { nlimbs!(512) }>(a, b); // Sent in by @kayabaNerve (https://github.com/RustCrypto/crypto-bigint/pull/761#issuecomment-2771581512) let a = U256::from_be_hex( @@ -505,7 +498,7 @@ mod tests { let b = U256::from_be_hex( "000000000000072B69C9DD0AA15F135675EA9C5180CF8FF0A59298CFC92E87FA", ); - test(a, b); + test::<{ nlimbs!(256) }, { nlimbs!(512) }>(a, b); // Sent in by @kayabaNerve (https://github.com/RustCrypto/crypto-bigint/pull/761#issuecomment-2782912608) let a = U512::from_be_hex(concat![ @@ -516,7 +509,7 @@ mod tests { "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C8283", "9D671CD581C69BC5E697F5E45BCD07C52EC373A8BDC598B4493F50A1380E1281" ]); - test(a, b); + test::<{ nlimbs!(512) }, { nlimbs!(1024) }>(a, b); } } diff --git a/src/uint/macros.rs b/src/uint/macros.rs index 2682fc579..f389a4a3f 100644 --- a/src/uint/macros.rs +++ b/src/uint/macros.rs @@ -46,77 +46,3 @@ macro_rules! impl_uint_aliases { )+ }; } - -macro_rules! impl_uint_concat_split_mixed { - ($name:ident, $size:literal) => { - impl $crate::traits::ConcatMixed> for Uint<{ <$name>::LIMBS - U64::LIMBS * $size }> - { - type MixedOutput = $name; - - fn concat_mixed(&self, hi: &Uint<{ U64::LIMBS * $size }>) -> Self::MixedOutput { - Uint::concat_mixed(self, hi) - } - } - - impl $crate::traits::SplitMixed, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>> for $name - { - fn split_mixed(&self) -> (Uint<{ U64::LIMBS * $size }>, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>) { - self.split_mixed() - } - } - - impl $crate::traits::RemMixed> for $name - { - fn rem_mixed(&self, reductor: &NonZero>) -> Uint<{ U64::LIMBS * $size }> { - self.div_rem_vartime(reductor).1 - } - } - }; - ($name:ident, [ $($size:literal),+ ]) => { - $( - impl_uint_concat_split_mixed!($name, $size); - )+ - }; - ($( ($name:ident, $sizes:tt), )+) => { - $( - impl_uint_concat_split_mixed!($name, $sizes); - )+ - }; -} - -macro_rules! impl_uint_concat_split_even { - ($name:ident) => { - impl $crate::traits::ConcatMixed::LIMBS / 2 }>> for Uint<{ <$name>::LIMBS / 2 }> - { - type MixedOutput = $name; - - fn concat_mixed(&self, hi: &Uint<{ <$name>::LIMBS / 2 }>) -> Self::MixedOutput { - Uint::concat_mixed(self, hi) - } - } - - impl $crate::traits::SplitMixed::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>> for $name - { - fn split_mixed(&self) -> (Uint<{ <$name>::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>) { - self.split_mixed() - } - } - - impl $crate::traits::RemMixed::LIMBS / 2 }>> for $name - { - fn rem_mixed(&self, reductor: &NonZero::LIMBS / 2 }>>) -> Uint<{ <$name>::LIMBS / 2 }> { - self.div_rem_vartime(reductor).1 - } - } - - impl $crate::traits::Split for $name - { - type Output = Uint<{ <$name>::LIMBS / 2 }>; - } - }; - ($($name:ident,)+) => { - $( - impl_uint_concat_split_even!($name); - )+ - } -} diff --git a/src/uint/mul.rs b/src/uint/mul.rs index ec732015c..9d5aca980 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -5,8 +5,8 @@ use core::ops::{Mul, MulAssign}; use subtle::CtOption; use crate::{ - Checked, CheckedMul, Concat, ConcatMixed, ConcatenatingMul, ConstChoice, ConstCtOption, Limb, - Uint, UintRef, Wrapping, WrappingMul, + Checked, CheckedMul, ConcatenatingMul, ConstChoice, ConstCtOption, Limb, Uint, UintRef, + Wrapping, WrappingMul, }; pub(crate) mod karatsuba; @@ -17,12 +17,9 @@ impl Uint { pub const fn concatenating_mul( &self, rhs: &Uint, - ) -> Uint - where - Self: ConcatMixed, MixedOutput = Uint>, - { + ) -> Uint { let (lo, hi) = self.widening_mul(rhs); - Uint::concat_mixed(&lo, &hi) + lo.concat_mixed(&hi) } /// Compute "wide" multiplication as a 2-tuple containing the `(lo, hi)` components of the product, whose sizes @@ -76,12 +73,9 @@ impl Uint { } /// Square self, returning a concatenated "wide" result. - pub const fn widening_square(&self) -> Uint - where - Self: ConcatMixed, MixedOutput = Uint>, - { + pub const fn widening_square(&self) -> Uint { let (lo, hi) = self.square_wide(); - Uint::concat_mixed(&lo, &hi) + lo.concat_mixed(&hi) } /// Square self, checking that the result fits in the original [`Uint`] size. @@ -103,12 +97,9 @@ impl Uint { } } -impl Uint -where - Self: Concat>, -{ +impl Uint { /// Square self, returning a concatenated "wide" result. - pub const fn square(&self) -> Uint { + pub const fn square(&self) -> Uint { let (lo, hi) = self.square_wide(); lo.concat(&hi) } @@ -190,27 +181,19 @@ impl MulAssign<&Checked>> for Checked - ConcatenatingMul> for Uint -where - Self: ConcatMixed, MixedOutput = Uint>, + ConcatenatingMul, Uint> for Uint { - type Output = >>::MixedOutput; - #[inline] - fn concatenating_mul(&self, rhs: Uint) -> Self::Output { + fn concatenating_mul(&self, rhs: Uint) -> Uint { self.concatenating_mul(&rhs) } } impl - ConcatenatingMul<&Uint> for Uint -where - Self: ConcatMixed, MixedOutput = Uint>, + ConcatenatingMul, &Uint> for Uint { - type Output = >>::MixedOutput; - #[inline] - fn concatenating_mul(&self, rhs: &Uint) -> Self::Output { + fn concatenating_mul(&self, rhs: &Uint) -> Uint { self.concatenating_mul(rhs) } } @@ -349,7 +332,7 @@ mod tests { #[test] fn square() { let n = U64::from_u64(0xffff_ffff_ffff_ffff); - let (lo, hi) = n.square().split(); + let (lo, hi) = n.square::<{ nlimbs!(128) }>().split(); assert_eq!(lo, U64::from_u64(1)); assert_eq!(hi, U64::from_u64(0xffff_ffff_ffff_fffe)); } @@ -357,7 +340,7 @@ mod tests { #[test] fn square_larger() { let n = U256::MAX; - let (lo, hi) = n.square().split(); + let (lo, hi) = n.square::<{ nlimbs!(512) }>().split(); assert_eq!(lo, U256::ONE); assert_eq!(hi, U256::MAX.wrapping_sub(&U256::ONE)); } diff --git a/src/uint/mul_int.rs b/src/uint/mul_int.rs index ab7228103..c20b92078 100644 --- a/src/uint/mul_int.rs +++ b/src/uint/mul_int.rs @@ -1,4 +1,4 @@ -use crate::{ConcatMixed, ConstChoice, ConstCtOption, Int, Uint}; +use crate::{ConstChoice, ConstCtOption, Int, Uint}; impl Uint { /// Compute "wide" multiplication between an [`Uint`] and [`Int`] as 3-tuple `(lo, hi, negate)`. @@ -20,10 +20,7 @@ impl Uint { pub const fn concatenating_mul_int( &self, rhs: &Int, - ) -> Int - where - Uint: ConcatMixed, MixedOutput = Uint>, - { + ) -> Int { let (rhs_abs, rhs_sign) = rhs.abs_sign(); let product_abs = self.concatenating_mul(&rhs_abs); diff --git a/src/uint/split.rs b/src/uint/split.rs index a9aa638ed..77afb6e01 100644 --- a/src/uint/split.rs +++ b/src/uint/split.rs @@ -1,36 +1,61 @@ -use crate::{Limb, Split, SplitMixed, Uint}; +use crate::{Split, Uint}; + +const fn split( + value: &Uint, +) -> (Uint, Uint) { + const { + if L + H != I { + panic!(concat![ + "The sum of the sizes of the declared types of `Uint` split is not equal to ", + "the size of the input type. ", + ]); + } + } + + let mut lo = Uint::::ZERO; + let mut hi = Uint::::ZERO; + + let mut i = 0; + while i < L { + lo.limbs[i] = value.limbs[i]; + i += 1; + } + while i < L + H { + hi.limbs[i - L] = value.limbs[i]; + i += 1; + } + + (lo, hi) +} + +impl Split, Uint> for Uint { + /// Split this number into low and high components respectively. + /// + ///
+ /// The sum of output lengths must be equal to the input length. + ///
+ fn split(&self) -> (Uint, Uint) { + self.split_mixed() + } +} impl Uint { /// Split this number in half into low and high components. - pub const fn split(&self) -> (Uint, Uint) - where - Self: Split>, - { - self.split_mixed() + /// + ///
+ /// The sum of output lengths must be equal to the input length. + ///
+ pub const fn split(&self) -> (Uint, Uint) { + split::(self) } /// Split this number into low and high components respectively. - #[inline] - pub const fn split_mixed(&self) -> (Uint, Uint) - where - Self: SplitMixed, Uint>, - { - let top = L + H; - let top = if top < I { top } else { I }; - let mut lo = [Limb::ZERO; L]; - let mut hi = [Limb::ZERO; H]; - let mut i = 0; - - while i < top { - if i < L { - lo[i] = self.limbs[i]; - } else { - hi[i - L] = self.limbs[i]; - } - i += 1; - } - - (Uint { limbs: lo }, Uint { limbs: hi }) + /// + ///
+ /// The sum of output lengths must be equal to the input length. + ///
+ pub const fn split_mixed(&self) -> (Uint, Uint) { + split::(self) } } diff --git a/tests/monty_form.rs b/tests/monty_form.rs index 1c27ec601..3d8ceaddf 100644 --- a/tests/monty_form.rs +++ b/tests/monty_form.rs @@ -151,7 +151,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U128::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one = r_monty_inv.retrieve().concatenating_mul(&r); + let one: U256 = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U256::ONE, @@ -190,7 +190,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U256::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one = r_monty_inv.retrieve().concatenating_mul(&r); + let one: U512 = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U512::ONE, @@ -229,7 +229,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one = r_monty_inv.retrieve().concatenating_mul(&r); + let one: U2048 = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U2048::ONE, @@ -268,7 +268,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one = r_monty_inv.retrieve().concatenating_mul(&r); + let one: U4096 = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U4096::ONE, From c6161c2839733a70e2c78a8b7d4dbeda87361050 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Fri, 21 Nov 2025 16:19:37 -0800 Subject: [PATCH 2/5] Implement `Encoding` for all `Uint`s --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/lib.rs | 2 + src/limb.rs | 8 +++ src/traits.rs | 2 +- src/uint.rs | 15 +++-- src/uint/div.rs | 10 ++- src/uint/encoding.rs | 157 ++++++++++++++++++++++++++++++++----------- src/uint/macros.rs | 36 ---------- tests/monty_form.rs | 20 +++--- tests/uint.rs | 4 +- 11 files changed, 167 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f376140a7..c5962ac7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,12 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + [[package]] name = "bytes" version = "1.10.1" @@ -241,6 +247,7 @@ name = "crypto-bigint" version = "0.7.0-rc.10" dependencies = [ "bincode", + "bytemuck", "chacha20", "criterion", "der", diff --git a/Cargo.toml b/Cargo.toml index ec4ad9cd7..e8bb584ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ rust-version = "1.85" [dependencies] subtle = { version = "2.6", default-features = false } +bytemuck = { version = "1.24", default-features = false, features = ["must_cast", "must_cast_extra"] } # optional dependencies der = { version = "0.8.0-rc.9", optional = true, default-features = false } diff --git a/src/lib.rs b/src/lib.rs index f81f682e9..234b5edb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,6 +167,8 @@ #[macro_use] extern crate alloc; +pub use uint::encoding::{EncodedUint, TryFromSliceError}; + #[cfg(feature = "rand_core")] pub use rand_core; #[cfg(feature = "rlp")] diff --git a/src/limb.rs b/src/limb.rs index b884ad858..62dd364c5 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -231,6 +231,14 @@ impl Serialize for Limb { #[cfg(feature = "zeroize")] impl zeroize::DefaultIsZeroes for Limb {} +// SAFETY: `Limb` is a newtype of an integer POD type +#[allow(unsafe_code)] +unsafe impl bytemuck::Zeroable for Limb {} + +// SAFETY: `Limb` is a newtype of an integer POD type +#[allow(unsafe_code)] +unsafe impl bytemuck::Pod for Limb {} + #[cfg(test)] mod tests { #[cfg(feature = "alloc")] diff --git a/src/traits.rs b/src/traits.rs index 9f6889b09..11452cd08 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -616,7 +616,7 @@ pub trait Encoding: Sized { + Copy + Clone + Sized - + for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>; + + for<'a> TryFrom<&'a [u8], Error: core::error::Error>; /// Decode from big endian bytes. fn from_be_bytes(bytes: Self::Repr) -> Self; diff --git a/src/uint.rs b/src/uint.rs index 3effff4c2..896720197 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -16,10 +16,13 @@ pub use extra_sizes::*; pub(crate) use ref_type::UintRef; use crate::{ - Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, Encoding, FixedInteger, - Int, Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm, + Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, FixedInteger, Int, + Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm, }; +#[cfg(feature = "serde")] +use crate::Encoding; + #[macro_use] mod macros; @@ -413,10 +416,10 @@ where where D: Deserializer<'de>, { - let mut buffer = Self::ZERO.to_le_bytes(); + let mut buffer = Encoding::to_le_bytes(&Self::ZERO); serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?; - Ok(Self::from_le_bytes(buffer)) + Ok(Encoding::from_le_bytes(buffer)) } } @@ -429,7 +432,7 @@ where where S: Serializer, { - serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer) + serdect::slice::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer) } } @@ -603,7 +606,7 @@ mod tests { let be_bytes = a.to_be_bytes(); let le_bytes = a.to_le_bytes(); for i in 0..16 { - assert_eq!(le_bytes[i], be_bytes[15 - i]); + assert_eq!(le_bytes.as_ref()[i], be_bytes.as_ref()[15 - i]); } let a_from_be = U128::from_be_bytes(be_bytes); diff --git a/src/uint/div.rs b/src/uint/div.rs index fd5e5bbc4..b8d34104b 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -872,10 +872,16 @@ mod tests { ); let rem = U256::rem_wide(lo_hi, &modulus); // Lower half is zero - assert_eq!(rem.to_be_bytes()[0..16], U128::ZERO.to_be_bytes()); + assert_eq!( + &rem.to_be_bytes().as_ref()[0..16], + U128::ZERO.to_be_bytes().as_ref() + ); // Upper half let expected = U128::from_be_hex("203F80FE03F80FE03F80FE03F80FE041"); - assert_eq!(rem.to_be_bytes()[16..], expected.to_be_bytes()); + assert_eq!( + &rem.to_be_bytes().as_ref()[16..], + expected.to_be_bytes().as_ref() + ); let remv = U256::rem_wide_vartime(lo_hi, &modulus); assert_eq!(rem, remv); diff --git a/src/uint/encoding.rs b/src/uint/encoding.rs index 478d257b7..cd04a6e11 100644 --- a/src/uint/encoding.rs +++ b/src/uint/encoding.rs @@ -6,18 +6,17 @@ mod der; #[cfg(feature = "rlp")] mod rlp; +use core::fmt; + #[cfg(feature = "alloc")] use alloc::{string::String, vec::Vec}; use super::Uint; -use crate::{DecodeError, Limb, Word}; +use crate::{DecodeError, Encoding, Limb, Word}; #[cfg(feature = "alloc")] use crate::{ConstChoice, NonZero, Reciprocal, UintRef, WideWord}; -#[cfg(feature = "hybrid-array")] -use crate::Encoding; - #[cfg(feature = "alloc")] const RADIX_ENCODING_LIMBS_LARGE: usize = 16; #[cfg(feature = "alloc")] @@ -204,58 +203,140 @@ impl Uint { let mut buf = *self; radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref()) } + + /// Serialize as big endian bytes. + pub const fn to_be_bytes(&self) -> EncodedUint { + EncodedUint::new_be(self) + } + + /// Serialize as little endian bytes. + pub const fn to_le_bytes(&self) -> EncodedUint { + EncodedUint::new_le(self) + } } -/// Encode a [`Uint`] to a big endian byte array of the given size. -pub(crate) const fn uint_to_be_bytes( - uint: &Uint, -) -> [u8; BYTES] { - if BYTES != LIMBS * Limb::BYTES { - panic!("BYTES != LIMBS * Limb::BYTES"); +/// [`Uint`] encoded as bytes. +// Until const generic expressions are stable, we cannot statically declare a `u8` array +// of the size `LIMBS * Limb::BYTES`. +// So instead we use the array of words, and treat it as an array of bytes. +// It's a little hacky, but it works, because the array is guaranteed to be contiguous. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct EncodedUint([Word; LIMBS]); + +impl EncodedUint { + const fn new_le(value: &Uint) -> Self { + let mut buffer = [0; LIMBS]; + let mut i = 0; + + while i < LIMBS { + let src_bytes = &value.limbs[i].0.to_le_bytes(); + + // We could cast the whole `buffer` to bytes at once, + // but IndexMut does not work in const context. + let dst_bytes: &mut [u8] = + bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[i])); + + // `copy_from_slice` can be used here when MSRV moves past 1.87 + let mut j = 0; + while j < Limb::BYTES { + dst_bytes[j] = src_bytes[j]; + j += 1; + } + + i += 1; + } + Self(buffer) } - let mut ret = [0u8; BYTES]; - let mut i = 0; + const fn new_be(value: &Uint) -> Self { + let mut buffer = [0; LIMBS]; + let mut i = 0; + while i < LIMBS { + let src_bytes = &value.limbs[i].0.to_be_bytes(); - while i < LIMBS { - let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes(); - let mut j = 0; + // We could cast the whole `buffer` to bytes at once, + // but IndexMut does not work in const context. + let dst_bytes: &mut [u8] = + bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i])); - while j < Limb::BYTES { - ret[i * Limb::BYTES + j] = limb_bytes[j]; - j += 1; + // `copy_from_slice` can be used here when MSRV moves past 1.87 + let mut j = 0; + while j < Limb::BYTES { + dst_bytes[j] = src_bytes[j]; + j += 1; + } + + i += 1; } + Self(buffer) + } +} - i += 1; +impl Default for EncodedUint { + fn default() -> Self { + Self([0; LIMBS]) } +} - ret +impl AsRef<[u8]> for EncodedUint { + fn as_ref(&self) -> &[u8] { + bytemuck::must_cast_slice(&self.0) + } } -/// Encode a [`Uint`] to a little endian byte array of the given size. -pub(crate) const fn uint_to_le_bytes( - uint: &Uint, -) -> [u8; BYTES] { - if BYTES != LIMBS * Limb::BYTES { - panic!("BYTES != LIMBS * Limb::BYTES"); +impl AsMut<[u8]> for EncodedUint { + fn as_mut(&mut self) -> &mut [u8] { + bytemuck::must_cast_slice_mut(&mut self.0) } +} - let mut ret = [0u8; BYTES]; - let mut i = 0; +/// Returned if an object cannot be instantiated from the given byte slice. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TryFromSliceError; - while i < LIMBS { - let limb_bytes = uint.limbs[i].0.to_le_bytes(); - let mut j = 0; +impl fmt::Display for TryFromSliceError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "TryFromSliceError") + } +} - while j < Limb::BYTES { - ret[i * Limb::BYTES + j] = limb_bytes[j]; - j += 1; +impl core::error::Error for TryFromSliceError {} + +impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint { + type Error = TryFromSliceError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Uint::::BYTES { + return Err(TryFromSliceError); } + let mut result = Self::default(); + result.as_mut().copy_from_slice(bytes); + Ok(result) + } +} + +impl Encoding for Uint { + type Repr = EncodedUint; + + #[inline] + fn from_be_bytes(bytes: Self::Repr) -> Self { + Self::from_be_slice(bytes.as_ref()) + } - i += 1; + #[inline] + fn from_le_bytes(bytes: Self::Repr) -> Self { + Self::from_le_slice(bytes.as_ref()) + } + + #[inline] + fn to_be_bytes(&self) -> Self::Repr { + self.to_be_bytes() } - ret + #[inline] + fn to_le_bytes(&self) -> Self::Repr { + self.to_le_bytes() + } } /// Decode a single nibble of upper or lower hex @@ -1057,7 +1138,7 @@ mod tests { let n = UintEx::from_be_hex("0011223344556677"); let bytes = n.to_be_bytes(); - assert_eq!(bytes, hex!("0011223344556677")); + assert_eq!(bytes.as_ref(), hex!("0011223344556677")); #[cfg(feature = "der")] assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7); @@ -1069,7 +1150,7 @@ mod tests { let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff"); let bytes = n.to_be_bytes(); - assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff")); + assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff")); #[cfg(feature = "der")] assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15); diff --git a/src/uint/macros.rs b/src/uint/macros.rs index f389a4a3f..da84fa7bb 100644 --- a/src/uint/macros.rs +++ b/src/uint/macros.rs @@ -7,42 +7,6 @@ macro_rules! impl_uint_aliases { #[doc = $doc] #[doc="unsigned big integer."] pub type $name = Uint<{ nlimbs!($bits) }>; - - impl $name { - /// Serialize as big endian bytes. - pub const fn to_be_bytes(&self) -> [u8; $bits / 8] { - encoding::uint_to_be_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self) - } - - /// Serialize as little endian bytes. - pub const fn to_le_bytes(&self) -> [u8; $bits / 8] { - encoding::uint_to_le_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self) - } - } - - impl Encoding for $name { - type Repr = [u8; $bits / 8]; - - #[inline] - fn from_be_bytes(bytes: Self::Repr) -> Self { - Self::from_be_slice(&bytes) - } - - #[inline] - fn from_le_bytes(bytes: Self::Repr) -> Self { - Self::from_le_slice(&bytes) - } - - #[inline] - fn to_be_bytes(&self) -> Self::Repr { - encoding::uint_to_be_bytes(self) - } - - #[inline] - fn to_le_bytes(&self) -> Self::Repr { - encoding::uint_to_le_bytes(self) - } - } )+ }; } diff --git a/tests/monty_form.rs b/tests/monty_form.rs index 3d8ceaddf..4244f51cd 100644 --- a/tests/monty_form.rs +++ b/tests/monty_form.rs @@ -4,8 +4,8 @@ mod common; use common::to_biguint; use crypto_bigint::{ - Bounded, Constants, Encoding, Integer, Invert, Monty, NonZero, Odd, U128, U256, U512, U1024, - U2048, U4096, Unsigned, + Bounded, Constants, EncodedUint, Encoding, Integer, Invert, Monty, NonZero, Odd, U128, U256, + U512, U1024, U2048, U4096, Unsigned, modular::{MontyForm, MontyParams}, }; use num_bigint::BigUint; @@ -99,7 +99,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U128, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) + random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -109,7 +109,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U256, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) + random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -119,7 +119,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U2048, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) + random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -129,7 +129,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U1024, ::Monty, ::Monty, BigUint),TestCaseError> { - random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) + random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) } } proptest! { @@ -166,7 +166,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -205,7 +205,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -244,7 +244,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -283,7 +283,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) diff --git a/tests/uint.rs b/tests/uint.rs index 1a71131a1..78983e498 100644 --- a/tests/uint.rs +++ b/tests/uint.rs @@ -531,11 +531,11 @@ proptest! { #[test] fn encoding_reverse(a in uint()) { let mut bytes = a.to_be_bytes(); - bytes.reverse(); + bytes.as_mut().reverse(); prop_assert_eq!(a, U256::from_le_bytes(bytes)); let mut bytes = a.to_le_bytes(); - bytes.reverse(); + bytes.as_mut().reverse(); prop_assert_eq!(a, U256::from_be_bytes(bytes)); } From d91710aa7a662cce3e42ce3878d4c532fc3fe8bb Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 23 Nov 2025 09:55:58 -0800 Subject: [PATCH 3/5] Add a `Deref` impl to `EncodedUint` --- src/uint/encoding.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/uint/encoding.rs b/src/uint/encoding.rs index cd04a6e11..79ab3ab3d 100644 --- a/src/uint/encoding.rs +++ b/src/uint/encoding.rs @@ -6,7 +6,7 @@ mod der; #[cfg(feature = "rlp")] mod rlp; -use core::fmt; +use core::{fmt, ops::Deref}; #[cfg(feature = "alloc")] use alloc::{string::String, vec::Vec}; @@ -290,6 +290,13 @@ impl AsMut<[u8]> for EncodedUint { } } +impl Deref for EncodedUint { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + /// Returned if an object cannot be instantiated from the given byte slice. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TryFromSliceError; From 01527dcafb47d5cb535ffee55f47588687389e70 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 23 Nov 2025 10:36:08 -0800 Subject: [PATCH 4/5] Convert pointers without bytemuck --- Cargo.lock | 7 ------- Cargo.toml | 1 - src/limb.rs | 8 -------- src/uint/encoding.rs | 21 ++++++++++++++++----- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5962ac7b..f376140a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,12 +82,6 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - [[package]] name = "bytes" version = "1.10.1" @@ -247,7 +241,6 @@ name = "crypto-bigint" version = "0.7.0-rc.10" dependencies = [ "bincode", - "bytemuck", "chacha20", "criterion", "der", diff --git a/Cargo.toml b/Cargo.toml index e8bb584ed..ec4ad9cd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ rust-version = "1.85" [dependencies] subtle = { version = "2.6", default-features = false } -bytemuck = { version = "1.24", default-features = false, features = ["must_cast", "must_cast_extra"] } # optional dependencies der = { version = "0.8.0-rc.9", optional = true, default-features = false } diff --git a/src/limb.rs b/src/limb.rs index 62dd364c5..b884ad858 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -231,14 +231,6 @@ impl Serialize for Limb { #[cfg(feature = "zeroize")] impl zeroize::DefaultIsZeroes for Limb {} -// SAFETY: `Limb` is a newtype of an integer POD type -#[allow(unsafe_code)] -unsafe impl bytemuck::Zeroable for Limb {} - -// SAFETY: `Limb` is a newtype of an integer POD type -#[allow(unsafe_code)] -unsafe impl bytemuck::Pod for Limb {} - #[cfg(test)] mod tests { #[cfg(feature = "alloc")] diff --git a/src/uint/encoding.rs b/src/uint/encoding.rs index 79ab3ab3d..0761ec586 100644 --- a/src/uint/encoding.rs +++ b/src/uint/encoding.rs @@ -223,6 +223,18 @@ impl Uint { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct EncodedUint([Word; LIMBS]); +#[allow(unsafe_code)] +const fn cast_slice(limbs: &[Word]) -> &[u8] { + let new_len = size_of_val(limbs); + unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) } +} + +#[allow(unsafe_code)] +const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] { + let new_len = size_of_val(limbs); + unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, new_len) } +} + impl EncodedUint { const fn new_le(value: &Uint) -> Self { let mut buffer = [0; LIMBS]; @@ -233,8 +245,7 @@ impl EncodedUint { // We could cast the whole `buffer` to bytes at once, // but IndexMut does not work in const context. - let dst_bytes: &mut [u8] = - bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[i])); + let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i])); // `copy_from_slice` can be used here when MSRV moves past 1.87 let mut j = 0; @@ -257,7 +268,7 @@ impl EncodedUint { // We could cast the whole `buffer` to bytes at once, // but IndexMut does not work in const context. let dst_bytes: &mut [u8] = - bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i])); + cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i])); // `copy_from_slice` can be used here when MSRV moves past 1.87 let mut j = 0; @@ -280,13 +291,13 @@ impl Default for EncodedUint { impl AsRef<[u8]> for EncodedUint { fn as_ref(&self) -> &[u8] { - bytemuck::must_cast_slice(&self.0) + cast_slice(&self.0) } } impl AsMut<[u8]> for EncodedUint { fn as_mut(&mut self) -> &mut [u8] { - bytemuck::must_cast_slice_mut(&mut self.0) + cast_slice_mut(&mut self.0) } } From c2c0117120dd969cad5c9d88f6f32c2b7c2a6d86 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 23 Nov 2025 11:27:56 -0800 Subject: [PATCH 5/5] Use assert! instead of panic! --- src/uint/concat.rs | 9 +++++---- src/uint/split.rs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/uint/concat.rs b/src/uint/concat.rs index 04c42e9c0..22d16558a 100644 --- a/src/uint/concat.rs +++ b/src/uint/concat.rs @@ -5,12 +5,13 @@ const fn concat( hi: &Uint, ) -> Uint { const { - if L + H != O { - panic!(concat![ + assert!( + L + H == O, + concat![ "The size of the declared type of `Uint` concatenation is not equal to ", "the sum of the sizes of argument types. ", - ]); - } + ] + ); } let mut result = Uint::::ZERO; diff --git a/src/uint/split.rs b/src/uint/split.rs index 77afb6e01..d6865a732 100644 --- a/src/uint/split.rs +++ b/src/uint/split.rs @@ -4,12 +4,13 @@ const fn split( value: &Uint, ) -> (Uint, Uint) { const { - if L + H != I { - panic!(concat![ + assert!( + L + H == I, + concat![ "The sum of the sizes of the declared types of `Uint` split is not equal to ", "the size of the input type. ", - ]); - } + ] + ); } let mut lo = Uint::::ZERO;