Skip to content

Commit 906ac1e

Browse files
committed
feat(primitives): Add UintTryTo trait for Signed type
1 parent 2ce9079 commit 906ac1e

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

crates/primitives/src/signed/conversions.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{BigIntConversionError, ParseSignedError, Sign, Signed, utils::twos_complement};
22
use alloc::string::String;
33
use core::str::FromStr;
4-
use ruint::{ToUintError, Uint, UintTryFrom};
4+
use ruint::{FromUintError, ToUintError, Uint, UintTryFrom, UintTryTo};
55

66
impl<const BITS: usize, const LIMBS: usize> TryFrom<Uint<BITS, LIMBS>> for Signed<BITS, LIMBS> {
77
type Error = BigIntConversionError;
@@ -28,7 +28,7 @@ impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for Uin
2828
}
2929
}
3030

31-
/// Conversion between `Signed` of different `BITS` or `LIMBS` length.
31+
/// Conversion from `Signed` of different `BITS` or `LIMBS` length.
3232
impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
3333
UintTryFrom<Signed<BITS_SRC, LIMBS_SRC>> for Signed<BITS, LIMBS>
3434
{
@@ -48,6 +48,37 @@ impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_S
4848
}
4949
}
5050

51+
/// Conversion to `Signed` of different `BITS` or `LIMBS` length.
52+
impl<const BITS: usize, const LIMBS: usize, const BITS_TARGET: usize, const LIMBS_TARGET: usize>
53+
UintTryTo<Signed<BITS_TARGET, LIMBS_TARGET>> for Signed<BITS, LIMBS>
54+
{
55+
#[inline]
56+
fn uint_try_to(
57+
&self,
58+
) -> Result<Signed<BITS_TARGET, LIMBS_TARGET>, FromUintError<Signed<BITS_TARGET, LIMBS_TARGET>>>
59+
{
60+
let (sign, abs) = self.into_sign_and_abs();
61+
let resized = Signed::<BITS_TARGET, LIMBS_TARGET>::from_raw(
62+
Uint::uint_try_to(&abs).map_err(|e| match e {
63+
FromUintError::Overflow(b, t, v) => {
64+
FromUintError::Overflow(b, Signed(t), Signed(v))
65+
}
66+
})?,
67+
);
68+
if resized.is_negative() {
69+
return Err(FromUintError::Overflow(BITS_TARGET, resized, Signed::MAX));
70+
}
71+
Ok(match sign {
72+
Sign::Negative => resized.checked_neg().ok_or(FromUintError::Overflow(
73+
BITS_TARGET,
74+
resized,
75+
Signed::MAX,
76+
))?,
77+
Sign::Positive => resized,
78+
})
79+
}
80+
}
81+
5182
/// Conversion from positive `Signed` to `Uint` of different `BITS` or `LIMBS` length.
5283
impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
5384
UintTryFrom<Signed<BITS_SRC, LIMBS_SRC>> for Uint<BITS, LIMBS>
@@ -61,6 +92,22 @@ impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_S
6192
}
6293
}
6394

95+
/// Conversion to `Uint` from positive `Signed` of different `BITS` or `LIMBS` length.
96+
impl<const BITS: usize, const LIMBS: usize, const BITS_TARGET: usize, const LIMBS_TARGET: usize>
97+
UintTryTo<Uint<BITS_TARGET, LIMBS_TARGET>> for Signed<BITS, LIMBS>
98+
{
99+
#[inline]
100+
fn uint_try_to(
101+
&self,
102+
) -> Result<Uint<BITS_TARGET, LIMBS_TARGET>, FromUintError<Uint<BITS_TARGET, LIMBS_TARGET>>>
103+
{
104+
if self.is_negative() {
105+
return Err(FromUintError::Overflow(BITS_TARGET, Uint::ZERO, Uint::MAX));
106+
}
107+
Uint::uint_try_to(&self.into_raw())
108+
}
109+
}
110+
64111
/// Conversion from `Uint` to positive `Signed` of different `BITS` or `LIMBS` length.
65112
impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
66113
UintTryFrom<Uint<BITS_SRC, LIMBS_SRC>> for Signed<BITS, LIMBS>
@@ -76,6 +123,29 @@ impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_S
76123
}
77124
}
78125

126+
/// Conversion to positive `Signed` from `Uint` of different `BITS` or `LIMBS` length.
127+
impl<const BITS: usize, const LIMBS: usize, const BITS_TARGET: usize, const LIMBS_TARGET: usize>
128+
UintTryTo<Signed<BITS_TARGET, LIMBS_TARGET>> for Uint<BITS, LIMBS>
129+
{
130+
#[inline]
131+
fn uint_try_to(
132+
&self,
133+
) -> Result<Signed<BITS_TARGET, LIMBS_TARGET>, FromUintError<Signed<BITS_TARGET, LIMBS_TARGET>>>
134+
{
135+
let resized = Signed::<BITS_TARGET, LIMBS_TARGET>::from_raw(
136+
Self::uint_try_to(self).map_err(|e| match e {
137+
FromUintError::Overflow(b, t, v) => {
138+
FromUintError::Overflow(b, Signed(t), Signed(v))
139+
}
140+
})?,
141+
);
142+
if resized.is_negative() {
143+
return Err(FromUintError::Overflow(BITS_TARGET, resized, Signed::MAX));
144+
}
145+
Ok(resized)
146+
}
147+
}
148+
79149
fn signed_err<const BITS: usize, const LIMBS: usize>(
80150
err: ToUintError<Uint<BITS, LIMBS>>,
81151
) -> ToUintError<Signed<BITS, LIMBS>> {

crates/primitives/src/signed/int.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,28 +1692,39 @@ mod tests {
16921692
let m_i256 = I256::unchecked_from(-4);
16931693
let m_i24 = I24::from(m_i256);
16941694
assert_eq!(m_i24, I24::from_dec_str("-4").unwrap());
1695+
assert_eq!(m_i24.to::<I256>(), m_i256);
16951696
let m_i56 = I56::from(m_i24);
16961697
assert_eq!(m_i56, I56::from_dec_str("-4").unwrap());
1698+
assert_eq!(m_i56.to::<I24>(), m_i24);
16971699
let m_i128 = I128::from(m_i56);
16981700
assert_eq!(m_i128, I128::from_dec_str("-4").unwrap());
1701+
assert_eq!(m_i128.to::<I56>(), m_i56);
16991702
let m_i96 = I96::from(m_i128);
17001703
assert_eq!(m_i96, I96::from_dec_str("-4").unwrap());
1704+
assert_eq!(m_i96.to::<I128>(), m_i128);
17011705

17021706
// convert positive signed to unsigned
17031707
assert_eq!(U24::from(I24::from_hex_str("0x7FFFFF").unwrap()), U24::from(0x7FFFFF));
1708+
assert_eq!(I24::from_hex_str("0x7FFFFF").unwrap().to::<U24>(), U24::from(0x7FFFFF));
17041709

17051710
// convert unsigned to positive signed
17061711
assert_eq!(I24::from(U24::from(0x7FFFFF)), I24::from_hex_str("0x7FFFFF").unwrap());
1712+
assert_eq!(U24::from(0x7FFFFF).to::<I24>(), I24::from_hex_str("0x7FFFFF").unwrap());
17071713
assert_eq!(I24::from(U96::from(0x7FFFFF)), I24::from_hex_str("0x7FFFFF").unwrap());
1714+
assert_eq!(U96::from(0x7FFFFF).to::<I24>(), I24::from_hex_str("0x7FFFFF").unwrap());
17081715

17091716
// can't convert negative signed to unsigned
17101717
assert!(U24::uint_try_from(m_i24).is_err());
1718+
assert!(<I24 as UintTryTo<U24>>::uint_try_to(&m_i24).is_err());
17111719

17121720
// can't convert unsigned to positive signed if too large
17131721
assert!(I24::uint_try_from(U24::from(0x800000)).is_err());
1722+
assert!(<U24 as UintTryTo<I24>>::uint_try_to(&U24::from(0x800000)).is_err());
17141723

17151724
// out-of-bounds conversions
17161725
assert!(I24::uint_try_from(I128::MIN).is_err());
1726+
assert!(<I128 as UintTryTo<I24>>::uint_try_to(&I128::MIN).is_err());
17171727
assert!(I24::uint_try_from(I128::MAX).is_err());
1728+
assert!(<I128 as UintTryTo<I24>>::uint_try_to(&I128::MAX).is_err());
17181729
}
17191730
}

0 commit comments

Comments
 (0)