diff --git a/crates/primitives/src/signed/conversions.rs b/crates/primitives/src/signed/conversions.rs index 9f0d57f92..84d56f7d7 100644 --- a/crates/primitives/src/signed/conversions.rs +++ b/crates/primitives/src/signed/conversions.rs @@ -1,7 +1,7 @@ use super::{BigIntConversionError, ParseSignedError, Sign, Signed, utils::twos_complement}; use alloc::string::String; use core::str::FromStr; -use ruint::{ToUintError, Uint, UintTryFrom}; +use ruint::{FromUintError, ToUintError, Uint, UintTryFrom, UintTryTo}; impl TryFrom> for Signed { type Error = BigIntConversionError; @@ -28,7 +28,7 @@ impl TryFrom> for Uin } } -/// Conversion between `Signed` of different `BITS` or `LIMBS` length. +/// Conversion from `Signed` of different `BITS` or `LIMBS` length. impl UintTryFrom> for Signed { @@ -48,6 +48,37 @@ impl + UintTryTo> for Signed +{ + #[inline] + fn uint_try_to( + &self, + ) -> Result, FromUintError>> + { + let (sign, abs) = self.into_sign_and_abs(); + let resized = Signed::::from_raw( + Uint::uint_try_to(&abs).map_err(|e| match e { + FromUintError::Overflow(b, t, v) => { + FromUintError::Overflow(b, Signed(t), Signed(v)) + } + })?, + ); + if resized.is_negative() { + return Err(FromUintError::Overflow(BITS_TARGET, resized, Signed::MAX)); + } + Ok(match sign { + Sign::Negative => resized.checked_neg().ok_or(FromUintError::Overflow( + BITS_TARGET, + resized, + Signed::MAX, + ))?, + Sign::Positive => resized, + }) + } +} + /// Conversion from positive `Signed` to `Uint` of different `BITS` or `LIMBS` length. impl UintTryFrom> for Uint @@ -61,6 +92,22 @@ impl + UintTryTo> for Signed +{ + #[inline] + fn uint_try_to( + &self, + ) -> Result, FromUintError>> + { + if self.is_negative() { + return Err(FromUintError::Overflow(BITS_TARGET, Uint::ZERO, Uint::MAX)); + } + Uint::uint_try_to(&self.into_raw()) + } +} + /// Conversion from `Uint` to positive `Signed` of different `BITS` or `LIMBS` length. impl UintTryFrom> for Signed @@ -76,6 +123,29 @@ impl + UintTryTo> for Uint +{ + #[inline] + fn uint_try_to( + &self, + ) -> Result, FromUintError>> + { + let resized = Signed::::from_raw( + Self::uint_try_to(self).map_err(|e| match e { + FromUintError::Overflow(b, t, v) => { + FromUintError::Overflow(b, Signed(t), Signed(v)) + } + })?, + ); + if resized.is_negative() { + return Err(FromUintError::Overflow(BITS_TARGET, resized, Signed::MAX)); + } + Ok(resized) + } +} + fn signed_err( err: ToUintError>, ) -> ToUintError> { diff --git a/crates/primitives/src/signed/int.rs b/crates/primitives/src/signed/int.rs index f1d10d77b..9dbb8394f 100644 --- a/crates/primitives/src/signed/int.rs +++ b/crates/primitives/src/signed/int.rs @@ -1692,28 +1692,39 @@ mod tests { let m_i256 = I256::unchecked_from(-4); let m_i24 = I24::from(m_i256); assert_eq!(m_i24, I24::from_dec_str("-4").unwrap()); + assert_eq!(m_i24.to::(), m_i256); let m_i56 = I56::from(m_i24); assert_eq!(m_i56, I56::from_dec_str("-4").unwrap()); + assert_eq!(m_i56.to::(), m_i24); let m_i128 = I128::from(m_i56); assert_eq!(m_i128, I128::from_dec_str("-4").unwrap()); + assert_eq!(m_i128.to::(), m_i56); let m_i96 = I96::from(m_i128); assert_eq!(m_i96, I96::from_dec_str("-4").unwrap()); + assert_eq!(m_i96.to::(), m_i128); // convert positive signed to unsigned assert_eq!(U24::from(I24::from_hex_str("0x7FFFFF").unwrap()), U24::from(0x7FFFFF)); + assert_eq!(I24::from_hex_str("0x7FFFFF").unwrap().to::(), U24::from(0x7FFFFF)); // convert unsigned to positive signed assert_eq!(I24::from(U24::from(0x7FFFFF)), I24::from_hex_str("0x7FFFFF").unwrap()); + assert_eq!(U24::from(0x7FFFFF).to::(), I24::from_hex_str("0x7FFFFF").unwrap()); assert_eq!(I24::from(U96::from(0x7FFFFF)), I24::from_hex_str("0x7FFFFF").unwrap()); + assert_eq!(U96::from(0x7FFFFF).to::(), I24::from_hex_str("0x7FFFFF").unwrap()); // can't convert negative signed to unsigned assert!(U24::uint_try_from(m_i24).is_err()); + assert!(>::uint_try_to(&m_i24).is_err()); // can't convert unsigned to positive signed if too large assert!(I24::uint_try_from(U24::from(0x800000)).is_err()); + assert!(>::uint_try_to(&U24::from(0x800000)).is_err()); // out-of-bounds conversions assert!(I24::uint_try_from(I128::MIN).is_err()); + assert!(>::uint_try_to(&I128::MIN).is_err()); assert!(I24::uint_try_from(I128::MAX).is_err()); + assert!(>::uint_try_to(&I128::MAX).is_err()); } }