feat: add OverflowingAdd trait

Add a OverflowingAdd trait and make UnsignedInteger
and SignedInteger depend on it

This is so that other parts of the code can
use the OverflowingAdd trait without requiring
the big Unsigned/Signed Bound
This commit is contained in:
tmontaigu
2025-03-27 13:58:18 +01:00
parent d0ce05027c
commit c768af8093
6 changed files with 64 additions and 11 deletions

View File

@@ -130,3 +130,9 @@ where
}
}
}
pub trait OverflowingAdd<Rhs> {
type Output;
fn overflowing_add(self, other: Rhs) -> (Self::Output, bool);
}

View File

@@ -1,4 +1,5 @@
use super::{CastFrom, CastInto, Numeric, SignedNumeric, UnsignedInteger};
use crate::core_crypto::prelude::OverflowingAdd;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
@@ -29,6 +30,7 @@ pub trait SignedInteger:
+ ShlAssign<usize>
+ Shr<usize, Output = Self>
+ ShrAssign<usize>
+ OverflowingAdd<Self, Output = Self>
+ CastFrom<f64>
+ CastInto<f64>
{
@@ -61,6 +63,15 @@ macro_rules! implement {
type NumericUnsignedType = $UnsignedType;
}
impl OverflowingAdd<Self> for $Type {
type Output = Self;
#[inline]
fn overflowing_add(self, rhs: Self) -> (Self, bool) {
self.overflowing_add(rhs)
}
}
impl SignedInteger for $Type {
type Unsigned = $UnsignedType;
#[inline]

View File

@@ -1,4 +1,5 @@
use super::{CastFrom, CastInto, Numeric, SignedInteger, UnsignedNumeric};
use crate::core_crypto::prelude::OverflowingAdd;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
@@ -30,6 +31,7 @@ pub trait UnsignedInteger:
+ ShlAssign<usize>
+ Shr<usize, Output = Self>
+ ShrAssign<usize>
+ OverflowingAdd<Self, Output = Self>
+ CastFrom<Self::Signed>
+ CastFrom<f64>
+ CastInto<f64>
@@ -83,8 +85,6 @@ pub trait UnsignedInteger:
#[must_use]
fn wrapping_shr(self, rhs: u32) -> Self;
#[must_use]
fn overflowing_add(self, rhs: Self) -> (Self, bool);
#[must_use]
fn overflowing_sub(self, rhs: Self) -> (Self, bool);
#[must_use]
fn is_power_of_two(self) -> bool;
@@ -121,6 +121,15 @@ macro_rules! implement {
type NumericSignedType = $SignedType;
}
impl OverflowingAdd<Self> for $Type {
type Output = Self;
#[inline]
fn overflowing_add(self, rhs: Self) -> (Self, bool) {
self.overflowing_add(rhs)
}
}
impl UnsignedInteger for $Type {
type Signed = $SignedType;
#[inline]
@@ -219,10 +228,6 @@ macro_rules! implement {
self.wrapping_pow(exp)
}
#[inline]
fn overflowing_add(self, rhs: Self) -> (Self, bool) {
self.overflowing_add(rhs)
}
#[inline]
fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
self.overflowing_sub(rhs)
}

View File

@@ -150,6 +150,13 @@ pub(crate) fn wrapping_add_with_carry<T: UnsignedInteger>(l: T, r: T, c: bool) -
}
pub(crate) fn wrapping_add_assign_words<T: UnsignedInteger>(lhs: &mut [T], rhs: &[T]) {
let _carry = unsigned_overflowing_add_assign_words(lhs, rhs);
}
pub(crate) fn unsigned_overflowing_add_assign_words<T: UnsignedInteger>(
lhs: &mut [T],
rhs: &[T],
) -> bool {
let iter = lhs
.iter_mut()
.zip(rhs.iter().copied().chain(std::iter::repeat(T::ZERO)));
@@ -160,6 +167,8 @@ pub(crate) fn wrapping_add_assign_words<T: UnsignedInteger>(lhs: &mut [T], rhs:
*lhs_block = result;
carry = out_carry;
}
carry
}
/// lhs and rhs are slice of words.

View File

@@ -1,4 +1,4 @@
use crate::core_crypto::prelude::{CastFrom, Numeric, UnsignedNumeric};
use crate::core_crypto::prelude::{CastFrom, Numeric, OverflowingAdd, UnsignedNumeric};
use std::ops::ShlAssign;
const fn one_for_unsigned_u64_based_integer<const N: usize>() -> [u64; N] {
@@ -261,6 +261,18 @@ impl<const N: usize> std::ops::ShlAssign<usize> for StaticUnsignedBigInt<N> {
}
}
impl<const N: usize> OverflowingAdd<Self> for StaticUnsignedBigInt<N> {
type Output = Self;
fn overflowing_add(self, other: Self) -> (Self::Output, bool) {
let mut result = self;
let overflowed =
super::algorithms::unsigned_overflowing_add_assign_words(&mut result.0, &other.0);
(result, overflowed)
}
}
impl<const N: usize> std::ops::Not for StaticUnsignedBigInt<N> {
type Output = Self;

View File

@@ -25,12 +25,11 @@ impl U256 {
#[cfg(test)]
mod tests {
use std::panic::catch_unwind;
use rand::Rng;
use super::super::{u64_with_even_bits_set, u64_with_odd_bits_set};
use super::*;
use crate::core_crypto::prelude::OverflowingAdd;
use rand::Rng;
use std::panic::catch_unwind;
#[test]
fn test_const() {
@@ -221,6 +220,17 @@ mod tests {
assert_eq!(U256::MAX + U256::from(1u32), U256::MIN);
}
#[test]
fn test_overflowing_add() {
let (r, o) = U256::MAX.overflowing_add(U256::from(1u32));
assert_eq!(r, U256::MIN);
assert!(o);
let (r, o) = U256::MAX.overflowing_add(U256::from(0u32));
assert_eq!(r, U256::MAX);
assert!(!o);
}
#[test]
fn test_sub_wrap_around() {
assert_eq!(U256::MIN - U256::from(1u32), U256::MAX);