alloy_primitives/signed/
conversions.rs

1use super::{utils::twos_complement, BigIntConversionError, ParseSignedError, Sign, Signed};
2use alloc::string::String;
3use core::str::FromStr;
4use ruint::{ToUintError, Uint, UintTryFrom};
5
6impl<const BITS: usize, const LIMBS: usize> TryFrom<Uint<BITS, LIMBS>> for Signed<BITS, LIMBS> {
7    type Error = BigIntConversionError;
8
9    #[inline]
10    fn try_from(from: Uint<BITS, LIMBS>) -> Result<Self, Self::Error> {
11        let value = Self(from);
12        match value.sign() {
13            Sign::Positive => Ok(value),
14            Sign::Negative => Err(BigIntConversionError),
15        }
16    }
17}
18
19impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for Uint<BITS, LIMBS> {
20    type Error = BigIntConversionError;
21
22    #[inline]
23    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
24        match value.sign() {
25            Sign::Positive => Ok(value.0),
26            Sign::Negative => Err(BigIntConversionError),
27        }
28    }
29}
30
31/// Conversion between `Signed` of different `BITS` or `LIMBS` length.
32impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
33    UintTryFrom<Signed<BITS_SRC, LIMBS_SRC>> for Signed<BITS, LIMBS>
34{
35    #[inline]
36    fn uint_try_from(value: Signed<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
37        let (sign, abs) = value.into_sign_and_abs();
38        let resized = Self::from_raw(Uint::<BITS, LIMBS>::uint_try_from(abs).map_err(signed_err)?);
39        if resized.is_negative() {
40            return Err(ToUintError::ValueNegative(BITS, resized));
41        }
42        Ok(match sign {
43            Sign::Negative => {
44                resized.checked_neg().ok_or(ToUintError::ValueTooLarge(BITS, resized))?
45            }
46            Sign::Positive => resized,
47        })
48    }
49}
50
51/// Conversion from positive `Signed` to `Uint` of different `BITS` or `LIMBS` length.
52impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
53    UintTryFrom<Signed<BITS_SRC, LIMBS_SRC>> for Uint<BITS, LIMBS>
54{
55    #[inline]
56    fn uint_try_from(value: Signed<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
57        if value.is_negative() {
58            return Err(ToUintError::ValueNegative(BITS, Self::uint_try_from(value.into_raw())?));
59        }
60        Self::uint_try_from(value.into_raw())
61    }
62}
63
64/// Conversion from `Uint` to positive `Signed` of different `BITS` or `LIMBS` length.
65impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
66    UintTryFrom<Uint<BITS_SRC, LIMBS_SRC>> for Signed<BITS, LIMBS>
67{
68    #[inline]
69    fn uint_try_from(value: Uint<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
70        let resized =
71            Self::from_raw(Uint::<BITS, LIMBS>::uint_try_from(value).map_err(signed_err)?);
72        if resized.is_negative() {
73            return Err(ToUintError::ValueNegative(BITS, resized));
74        }
75        Ok(resized)
76    }
77}
78
79fn signed_err<const BITS: usize, const LIMBS: usize>(
80    err: ToUintError<Uint<BITS, LIMBS>>,
81) -> ToUintError<Signed<BITS, LIMBS>> {
82    match err {
83        ToUintError::ValueTooLarge(b, t) => ToUintError::ValueTooLarge(b, Signed(t)),
84        ToUintError::ValueNegative(b, t) => ToUintError::ValueNegative(b, Signed(t)),
85        ToUintError::NotANumber(b) => ToUintError::NotANumber(b),
86    }
87}
88
89impl<const BITS: usize, const LIMBS: usize> TryFrom<&str> for Signed<BITS, LIMBS> {
90    type Error = ParseSignedError;
91
92    #[inline]
93    fn try_from(value: &str) -> Result<Self, Self::Error> {
94        Self::from_str(value)
95    }
96}
97
98impl<const BITS: usize, const LIMBS: usize> TryFrom<&String> for Signed<BITS, LIMBS> {
99    type Error = ParseSignedError;
100
101    #[inline]
102    fn try_from(value: &String) -> Result<Self, Self::Error> {
103        value.parse()
104    }
105}
106
107impl<const BITS: usize, const LIMBS: usize> TryFrom<String> for Signed<BITS, LIMBS> {
108    type Error = ParseSignedError;
109
110    #[inline]
111    fn try_from(value: String) -> Result<Self, Self::Error> {
112        value.parse()
113    }
114}
115
116impl<const BITS: usize, const LIMBS: usize> FromStr for Signed<BITS, LIMBS> {
117    type Err = ParseSignedError;
118
119    #[inline]
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        let (sign, s) = match s.as_bytes().first() {
122            Some(b'+') => (Sign::Positive, &s[1..]),
123            Some(b'-') => (Sign::Negative, &s[1..]),
124            _ => (Sign::Positive, s),
125        };
126        let abs = Uint::<BITS, LIMBS>::from_str(s)?;
127        Self::checked_from_sign_and_abs(sign, abs).ok_or(ParseSignedError::IntegerOverflow)
128    }
129}
130
131impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for i128 {
132    type Error = BigIntConversionError;
133
134    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
135        if value.bits() > 128 {
136            return Err(BigIntConversionError);
137        }
138
139        if value.is_positive() {
140            Ok(u128::try_from(value.0).unwrap() as Self)
141        } else {
142            let u = twos_complement(value.0);
143            let u = u128::try_from(u).unwrap() as Self;
144            Ok((!u).wrapping_add(1))
145        }
146    }
147}
148
149impl<const BITS: usize, const LIMBS: usize> TryFrom<i128> for Signed<BITS, LIMBS> {
150    type Error = BigIntConversionError;
151
152    fn try_from(value: i128) -> Result<Self, Self::Error> {
153        let u = value as u128;
154        if value >= 0 {
155            return Self::try_from(u);
156        }
157
158        // This is a bit messy :(
159        let tc = (!u).wrapping_add(1);
160        let stc = Uint::<128, 2>::saturating_from(tc);
161        let (num, overflow) = Uint::<BITS, LIMBS>::overflowing_from_limbs_slice(stc.as_limbs());
162        if overflow {
163            return Err(BigIntConversionError);
164        }
165        Ok(Self(twos_complement(num)))
166    }
167}
168
169impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for u128 {
170    type Error = BigIntConversionError;
171
172    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
173        if value.is_negative() {
174            return Err(BigIntConversionError);
175        }
176
177        let saturated = Uint::<BITS, LIMBS>::saturating_from(Self::MAX);
178
179        // if the value is greater than the saturated value, return an error
180        if value > Signed(saturated) {
181            return Err(BigIntConversionError);
182        }
183
184        value.into_raw().try_into().map_err(|_| BigIntConversionError)
185    }
186}
187
188impl<const BITS: usize, const LIMBS: usize> TryFrom<u128> for Signed<BITS, LIMBS> {
189    type Error = BigIntConversionError;
190
191    fn try_from(value: u128) -> Result<Self, Self::Error> {
192        let saturated = Uint::<BITS, LIMBS>::saturating_from(value);
193
194        if value != saturated.to::<u128>() {
195            return Err(BigIntConversionError);
196        }
197
198        Self::try_from(saturated)
199    }
200}
201
202// conversions
203macro_rules! impl_conversions {
204    ($(
205        $u:ty [$actual_low_u:ident -> $low_u:ident, $as_u:ident],
206        $i:ty [$actual_low_i:ident -> $low_i:ident, $as_i:ident];
207    )+) => {
208        // low_*, as_*
209        impl<const BITS: usize, const LIMBS: usize> Signed<BITS, LIMBS> {
210            $(
211                impl_conversions!(@impl_fns $u, $actual_low_u $low_u $as_u);
212                impl_conversions!(@impl_fns $i, $actual_low_i $low_i $as_i);
213            )+
214        }
215
216        // From<$>, TryFrom
217        $(
218            impl<const BITS: usize, const LIMBS: usize> TryFrom<$u> for Signed<BITS, LIMBS> {
219                type Error = BigIntConversionError;
220
221                #[inline]
222                fn try_from(value: $u) -> Result<Self, Self::Error> {
223                    let u = Uint::<BITS, LIMBS>::try_from(value).map_err(|_| BigIntConversionError)?;
224                    Signed::checked_from_sign_and_abs(Sign::Positive, u).ok_or(BigIntConversionError)
225                }
226            }
227
228            impl<const BITS: usize, const LIMBS: usize> TryFrom<$i> for Signed<BITS, LIMBS> {
229                type Error = BigIntConversionError;
230
231                #[inline]
232                fn try_from(value: $i) -> Result<Self, Self::Error> {
233                    let uint: $u = value as $u;
234
235                    if value.is_positive() {
236                        return Self::try_from(uint);
237                    }
238
239                    let abs = (!uint).wrapping_add(1);
240                    let tc = twos_complement(Uint::<BITS, LIMBS>::from(abs));
241                    Ok(Self(tc))
242                }
243            }
244
245            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $u {
246                type Error = BigIntConversionError;
247
248                #[inline]
249                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$u, Self::Error> {
250                    u128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
251                }
252            }
253
254            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $i {
255                type Error = BigIntConversionError;
256
257                #[inline]
258                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$i, Self::Error> {
259                    i128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
260                }
261            }
262        )+
263    };
264
265    (@impl_fns $t:ty, $actual_low:ident $low:ident $as:ident) => {
266        /// Low word.
267        #[inline]
268        pub const fn $low(&self) -> $t {
269            if BITS == 0 {
270                return 0
271            }
272
273            self.0.as_limbs()[0] as $t
274        }
275
276        #[doc = concat!("Conversion to ", stringify!($t) ," with overflow checking.")]
277        ///
278        /// # Panics
279        ///
280        #[doc = concat!("Panics if the number is outside the ", stringify!($t), " valid range.")]
281        #[inline]
282        #[track_caller]
283        pub fn $as(&self) -> $t {
284            <$t as TryFrom<Self>>::try_from(*self).unwrap()
285        }
286    };
287}
288
289impl_conversions! {
290    u8   [low_u64  -> low_u8,    as_u8],    i8   [low_u64  -> low_i8,    as_i8];
291    u16  [low_u64  -> low_u16,   as_u16],   i16  [low_u64  -> low_i16,   as_i16];
292    u32  [low_u64  -> low_u32,   as_u32],   i32  [low_u64  -> low_i32,   as_i32];
293    u64  [low_u64  -> low_u64,   as_u64],   i64  [low_u64  -> low_i64,   as_i64];
294    usize[low_u64  -> low_usize, as_usize], isize[low_u64  -> low_isize, as_isize];
295}