alloy_primitives/signed/
conversions.rs

1use super::{BigIntConversionError, ParseSignedError, Sign, Signed, utils::twos_complement};
2use alloc::string::String;
3use core::str::FromStr;
4use ruint::{FromUintError, ToUintError, Uint, UintTryFrom, UintTryTo};
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 from `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 to `Signed` of different `BITS` or `LIMBS` length.
52impl<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
82/// Conversion from positive `Signed` to `Uint` of different `BITS` or `LIMBS` length.
83impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
84    UintTryFrom<Signed<BITS_SRC, LIMBS_SRC>> for Uint<BITS, LIMBS>
85{
86    #[inline]
87    fn uint_try_from(value: Signed<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
88        if value.is_negative() {
89            return Err(ToUintError::ValueNegative(BITS, Self::uint_try_from(value.into_raw())?));
90        }
91        Self::uint_try_from(value.into_raw())
92    }
93}
94
95/// Conversion to `Uint` from positive `Signed` of different `BITS` or `LIMBS` length.
96impl<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
111/// Conversion from `Uint` to positive `Signed` of different `BITS` or `LIMBS` length.
112impl<const BITS: usize, const LIMBS: usize, const BITS_SRC: usize, const LIMBS_SRC: usize>
113    UintTryFrom<Uint<BITS_SRC, LIMBS_SRC>> for Signed<BITS, LIMBS>
114{
115    #[inline]
116    fn uint_try_from(value: Uint<BITS_SRC, LIMBS_SRC>) -> Result<Self, ToUintError<Self>> {
117        let resized =
118            Self::from_raw(Uint::<BITS, LIMBS>::uint_try_from(value).map_err(signed_err)?);
119        if resized.is_negative() {
120            return Err(ToUintError::ValueNegative(BITS, resized));
121        }
122        Ok(resized)
123    }
124}
125
126/// Conversion to positive `Signed` from `Uint` of different `BITS` or `LIMBS` length.
127impl<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
149fn signed_err<const BITS: usize, const LIMBS: usize>(
150    err: ToUintError<Uint<BITS, LIMBS>>,
151) -> ToUintError<Signed<BITS, LIMBS>> {
152    match err {
153        ToUintError::ValueTooLarge(b, t) => ToUintError::ValueTooLarge(b, Signed(t)),
154        ToUintError::ValueNegative(b, t) => ToUintError::ValueNegative(b, Signed(t)),
155        ToUintError::NotANumber(b) => ToUintError::NotANumber(b),
156    }
157}
158
159impl<const BITS: usize, const LIMBS: usize> TryFrom<&str> for Signed<BITS, LIMBS> {
160    type Error = ParseSignedError;
161
162    #[inline]
163    fn try_from(value: &str) -> Result<Self, Self::Error> {
164        Self::from_str(value)
165    }
166}
167
168impl<const BITS: usize, const LIMBS: usize> TryFrom<&String> for Signed<BITS, LIMBS> {
169    type Error = ParseSignedError;
170
171    #[inline]
172    fn try_from(value: &String) -> Result<Self, Self::Error> {
173        value.parse()
174    }
175}
176
177impl<const BITS: usize, const LIMBS: usize> TryFrom<String> for Signed<BITS, LIMBS> {
178    type Error = ParseSignedError;
179
180    #[inline]
181    fn try_from(value: String) -> Result<Self, Self::Error> {
182        value.parse()
183    }
184}
185
186impl<const BITS: usize, const LIMBS: usize> FromStr for Signed<BITS, LIMBS> {
187    type Err = ParseSignedError;
188
189    #[inline]
190    fn from_str(s: &str) -> Result<Self, Self::Err> {
191        let (sign, s) = match s.as_bytes().first() {
192            Some(b'+') => (Sign::Positive, &s[1..]),
193            Some(b'-') => (Sign::Negative, &s[1..]),
194            _ => (Sign::Positive, s),
195        };
196        let abs = Uint::<BITS, LIMBS>::from_str(s)?;
197        Self::checked_from_sign_and_abs(sign, abs).ok_or(ParseSignedError::IntegerOverflow)
198    }
199}
200
201impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for i128 {
202    type Error = BigIntConversionError;
203
204    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
205        if value.bits() > 128 {
206            return Err(BigIntConversionError);
207        }
208
209        if value.is_positive() {
210            Ok(u128::try_from(value.0).unwrap() as Self)
211        } else {
212            let u = twos_complement(value.0);
213            let u = u128::try_from(u).unwrap() as Self;
214            Ok((!u).wrapping_add(1))
215        }
216    }
217}
218
219impl<const BITS: usize, const LIMBS: usize> TryFrom<i128> for Signed<BITS, LIMBS> {
220    type Error = BigIntConversionError;
221
222    fn try_from(value: i128) -> Result<Self, Self::Error> {
223        let u = value as u128;
224        if value >= 0 {
225            return Self::try_from(u);
226        }
227
228        // This is a bit messy :(
229        let tc = (!u).wrapping_add(1);
230        let stc = Uint::<128, 2>::saturating_from(tc);
231        let (num, overflow) = Uint::<BITS, LIMBS>::overflowing_from_limbs_slice(stc.as_limbs());
232        if overflow {
233            return Err(BigIntConversionError);
234        }
235        Ok(Self(twos_complement(num)))
236    }
237}
238
239impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for u128 {
240    type Error = BigIntConversionError;
241
242    fn try_from(value: Signed<BITS, LIMBS>) -> Result<Self, Self::Error> {
243        if value.is_negative() {
244            return Err(BigIntConversionError);
245        }
246
247        let saturated = Uint::<BITS, LIMBS>::saturating_from(Self::MAX);
248
249        // if the value is greater than the saturated value, return an error
250        if value > Signed(saturated) {
251            return Err(BigIntConversionError);
252        }
253
254        value.into_raw().try_into().map_err(|_| BigIntConversionError)
255    }
256}
257
258impl<const BITS: usize, const LIMBS: usize> TryFrom<u128> for Signed<BITS, LIMBS> {
259    type Error = BigIntConversionError;
260
261    fn try_from(value: u128) -> Result<Self, Self::Error> {
262        let saturated = Uint::<BITS, LIMBS>::saturating_from(value);
263
264        if value != saturated.to::<u128>() {
265            return Err(BigIntConversionError);
266        }
267
268        Self::try_from(saturated)
269    }
270}
271
272// conversions
273macro_rules! impl_conversions {
274    ($(
275        $u:ty [$actual_low_u:ident -> $low_u:ident, $as_u:ident],
276        $i:ty [$actual_low_i:ident -> $low_i:ident, $as_i:ident];
277    )+) => {
278        // low_*, as_*
279        impl<const BITS: usize, const LIMBS: usize> Signed<BITS, LIMBS> {
280            $(
281                impl_conversions!(@impl_fns $u, $actual_low_u $low_u $as_u);
282                impl_conversions!(@impl_fns $i, $actual_low_i $low_i $as_i);
283            )+
284        }
285
286        // From<$>, TryFrom
287        $(
288            impl<const BITS: usize, const LIMBS: usize> TryFrom<$u> for Signed<BITS, LIMBS> {
289                type Error = BigIntConversionError;
290
291                #[inline]
292                fn try_from(value: $u) -> Result<Self, Self::Error> {
293                    let u = Uint::<BITS, LIMBS>::try_from(value).map_err(|_| BigIntConversionError)?;
294                    Signed::checked_from_sign_and_abs(Sign::Positive, u).ok_or(BigIntConversionError)
295                }
296            }
297
298            impl<const BITS: usize, const LIMBS: usize> TryFrom<$i> for Signed<BITS, LIMBS> {
299                type Error = BigIntConversionError;
300
301                #[inline]
302                fn try_from(value: $i) -> Result<Self, Self::Error> {
303                    let uint: $u = value as $u;
304
305                    if value.is_positive() {
306                        return Self::try_from(uint);
307                    }
308
309                    let abs = (!uint).wrapping_add(1);
310                    let tc = twos_complement(Uint::<BITS, LIMBS>::from(abs));
311                    Ok(Self(tc))
312                }
313            }
314
315            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $u {
316                type Error = BigIntConversionError;
317
318                #[inline]
319                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$u, Self::Error> {
320                    u128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
321                }
322            }
323
324            impl<const BITS: usize, const LIMBS: usize> TryFrom<Signed<BITS, LIMBS>> for $i {
325                type Error = BigIntConversionError;
326
327                #[inline]
328                fn try_from(value: Signed<BITS, LIMBS>) -> Result<$i, Self::Error> {
329                    i128::try_from(value)?.try_into().map_err(|_| BigIntConversionError)
330                }
331            }
332        )+
333    };
334
335    (@impl_fns $t:ty, $actual_low:ident $low:ident $as:ident) => {
336        /// Low word.
337        #[inline]
338        pub const fn $low(&self) -> $t {
339            if BITS == 0 {
340                return 0
341            }
342
343            self.0.as_limbs()[0] as $t
344        }
345
346        #[doc = concat!("Conversion to ", stringify!($t) ," with overflow checking.")]
347        ///
348        /// # Panics
349        ///
350        #[doc = concat!("Panics if the number is outside the ", stringify!($t), " valid range.")]
351        #[inline]
352        #[track_caller]
353        pub fn $as(&self) -> $t {
354            <$t as TryFrom<Self>>::try_from(*self).unwrap()
355        }
356    };
357}
358
359impl_conversions! {
360    u8   [low_u64  -> low_u8,    as_u8],    i8   [low_u64  -> low_i8,    as_i8];
361    u16  [low_u64  -> low_u16,   as_u16],   i16  [low_u64  -> low_i16,   as_i16];
362    u32  [low_u64  -> low_u32,   as_u32],   i32  [low_u64  -> low_i32,   as_i32];
363    u64  [low_u64  -> low_u64,   as_u64],   i64  [low_u64  -> low_i64,   as_i64];
364    usize[low_u64  -> low_usize, as_usize], isize[low_u64  -> low_isize, as_isize];
365}