Skip to main content

bnum/buint/
convert.rs

1macro_rules! from_uint {
2    ($BUint: ident, $Digit: ident; $($uint: tt),*) => {
3        $(
4            impl<const N: usize> From<$uint> for $BUint<N> {
5                #[inline]
6                fn from(int: $uint) -> Self {
7                    const UINT_BITS: usize = $uint::BITS as usize;
8                    let mut out = Self::ZERO;
9                    let mut i = 0;
10                    while i << crate::digit::$Digit::BIT_SHIFT < UINT_BITS {
11                        let d = (int >> (i << crate::digit::$Digit::BIT_SHIFT)) as $Digit;
12                        if d != 0 {
13                            out.digits[i] = d;
14                        }
15                        i += 1;
16                    }
17                    out
18                }
19            }
20        )*
21    }
22}
23
24macro_rules! try_from_iint {
25    ($BUint: ident; $($int: tt -> $uint: tt),*) => {
26        $(
27            impl<const N: usize> TryFrom<$int> for $BUint<N> {
28                type Error = TryFromIntError;
29
30                #[inline]
31                fn try_from(int: $int) -> Result<Self, Self::Error> {
32                    if int.is_negative() {
33                        return Err(TryFromIntError(()));
34                    }
35                    let bits = int as $uint;
36                    Ok(Self::from(bits))
37                }
38            }
39        )*
40    }
41}
42
43macro_rules! try_from_buint {
44    ($BUint: ident, $Digit: ident; $($int: ty), *) => {
45        $(
46            impl<const N: usize> TryFrom<$BUint<N>> for $int {
47                type Error = TryFromIntError;
48
49                #[inline]
50                fn try_from(u: $BUint<N>) -> Result<$int, Self::Error> {
51                    let mut out = 0;
52                    let mut i = 0;
53                    if $Digit::BITS > <$int>::BITS {
54                        let small = u.digits[i] as $int;
55                        let trunc = small as $Digit;
56                        if u.digits[i] != trunc {
57                            return Err(TryFromIntError(()));
58                        }
59                        out = small;
60                        i = 1;
61                    } else {
62                        loop {
63                            let shift = i << crate::digit::$Digit::BIT_SHIFT;
64                            if i >= N || shift >= <$int>::BITS as usize {
65                                break;
66                            }
67                            out |= u.digits[i] as $int << shift;
68                            i += 1;
69                        }
70                    }
71
72                    #[allow(unused_comparisons)]
73                    if out < 0 {
74                        return Err(TryFromIntError(()));
75                    }
76
77                    while i < N {
78                        if u.digits[i] != 0 {
79                            return Err(TryFromIntError(()));
80                        }
81                        i += 1;
82                    }
83                    
84                    Ok(out)
85                }
86            }
87        )*
88    };
89}
90
91macro_rules! uint_try_from_uint {
92    ($Trait: ident; $To: ident; $($From: ident $(<$N: ident>)?), *) => {
93        $(
94            impl<$(const $N: usize,)? const M: usize> $Trait<$From $(<$N>)?> for $To<M> {
95                type Error = TryFromIntError;
96            
97                fn try_from(from: $From $(<$N>)?) -> Result<Self, Self::Error> {
98                    if $From $(::<$N>)?::BITS <= Self::BITS || $From $(::<$N>)?::BITS - from.leading_zeros() <= Self::BITS {
99                        Ok(Self::cast_from(from))
100                    } else {
101                        Err(TryFromIntError(()))
102                    }
103                }
104            }
105        )*
106    };
107}
108
109macro_rules! uint_try_from_int {
110    ($Trait: ident; $To: ident; $($From: ident $(<$N: ident>)?), *) => {
111        $(
112            impl<$(const $N: usize,)? const M: usize> $Trait<$From $(<$N>)?> for $To<M> {
113                type Error = TryFromIntError;
114            
115                fn try_from(from: $From $(<$N>)?) -> Result<Self, Self::Error> {
116                    if from.is_negative() {
117                        Err(TryFromIntError(()))
118                    } else {
119                        if $From $(::<$N>)?::BITS.saturating_sub(1) <= Self::BITS || $From $(::<$N>)?::BITS - from.leading_zeros() <= Self::BITS {
120                            Ok(Self::cast_from(from))
121                        } else {
122                            Err(TryFromIntError(()))
123                        }
124                    }
125                }
126            }
127        )*
128    };
129}
130
131macro_rules! int_try_from_uint {
132    ($Trait: ident; $To: ident; $($From: ident $(<$N: ident>)?), *) => {
133        $(
134            impl<$(const $N: usize,)? const M: usize> $Trait<$From $(<$N>)?> for $To<M> {
135                type Error = TryFromIntError;
136            
137                fn try_from(from: $From $(<$N>)?) -> Result<Self, Self::Error> {
138                    if $From $(::<$N>)?::BITS <= Self::BITS - 1 || $From $(::<$N>)?::BITS - from.leading_zeros() <= Self::BITS - 1 { // Self::BITS - 1 as otherwise return value would be negative
139                        Ok(Self::cast_from(from))
140                    } else {
141                        Err(TryFromIntError(()))
142                    }
143                }
144            }
145        )*
146    };
147}
148
149macro_rules! int_try_from_int {
150    ($Trait: ident; $To: ident; $($From: ident $(<$N: ident>)?), *) => {
151        $(
152            impl<$(const $N: usize,)? const M: usize> $Trait<$From $(<$N>)?> for $To<M> {
153                type Error = TryFromIntError;
154            
155                fn try_from(from: $From $(<$N>)?) -> Result<Self, Self::Error> {
156                    if $From $(::<$N>)?::BITS <= Self::BITS {
157                        return Ok(Self::cast_from(from));
158                    }
159                    if from.is_negative() {
160                        if $From $(::<$N>)?::BITS - from.leading_ones() <= Self::BITS - 1 {
161                            Ok(Self::cast_from(from))
162                        } else {
163                            Err(TryFromIntError(()))
164                        }
165                    } else {
166                        if $From $(::<$N>)?::BITS - from.leading_zeros() <= Self::BITS - 1 {
167                            Ok(Self::cast_from(from))
168                        } else {
169                            Err(TryFromIntError(()))
170                        }
171                    }
172                }
173            }
174        )*
175    };
176}
177
178use crate::BTryFrom;
179
180macro_rules! mixed_try_from {
181    ($BUint: ident, $BInt: ident) => {
182        uint_try_from_uint!(BTryFrom; $BUint; BUint<N>, BUintD32<N>, BUintD16<N>, BUintD8<N>/*, u8, u16, u32, u64, u128, usize*/);
183        uint_try_from_int!(BTryFrom; $BUint; BInt<N>, BIntD32<N>, BIntD16<N>, BIntD8<N>/*, i8, i16, i32, i64, i128, isize*/);
184        int_try_from_uint!(BTryFrom; $BInt; BUint<N>, BUintD32<N>, BUintD16<N>, BUintD8<N>/*, u8, u16, u32, u64, u128, usize*/);
185        int_try_from_int!(BTryFrom; $BInt; BInt<N>, BIntD32<N>, BIntD16<N>, BIntD8<N>/*, i8, i16, i32, i64, i128, isize*/);
186    };
187}
188
189use crate::cast::CastFrom;
190use crate::errors::TryFromIntError;
191
192macro_rules! convert {
193    ($BUint: ident, $BInt: ident, $Digit: ident) => {
194        impl<const N: usize> From<bool> for $BUint<N> {
195            #[inline]
196            fn from(small: bool) -> Self {
197                Self::cast_from(small)
198            }
199        }
200
201        impl<const N: usize> From<char> for $BUint<N> {
202            #[inline]
203            fn from(c: char) -> Self {
204                Self::cast_from(c)
205            }
206        }
207
208        from_uint!($BUint, $Digit; u8, u16, u32, u64, u128, usize);
209
210        try_from_iint!($BUint; i8 -> u8, i16 -> u16, i32 -> u32, isize -> usize, i64 -> u64, i128 -> u128);
211
212        try_from_buint!($BUint, $Digit; u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
213
214        mixed_try_from!($BUint, $BInt);
215
216        impl<const N: usize> From<[$Digit; N]> for $BUint<N> {
217            #[inline]
218            fn from(digits: [$Digit; N]) -> Self {
219                Self::from_digits(digits)
220            }
221        }
222
223        impl<const N: usize> From<$BUint<N>> for [$Digit; N] {
224            #[inline]
225            fn from(uint: $BUint<N>) -> Self {
226                uint.digits
227            }
228        }
229    };
230}
231
232#[cfg(test)]
233crate::test::all_digit_tests! {
234    use crate::test::{self, types::utest};
235    use crate::test::cast_types::*;
236    use super::BTryFrom;
237
238    test::test_btryfrom!(utest; TestUint1, TestUint2, TestUint3, TestUint4, TestUint5, TestUint6, TestUint7, TestUint8, TestUint9, TestUint10, TestInt1, TestInt2, TestInt3, TestInt4, TestInt5, TestInt6, TestInt7, TestInt8, TestInt9, TestInt10/*, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize*/);
239
240    #[cfg(not(any(test_int_bits = "16", test_int_bits = "32")))] // TODO: due to incorrectly implementing From instead of TryFrom for small bnum ints
241    test::test_from! {
242        function: <utest as TryFrom>::try_from,
243        from_types: (u8, u16, u32, u64, bool, char, i8, i16, i32, i64, isize, usize) // TODO: when we can use TryFrom for conversions between bnum ints, we can just add the list of test types here, same as in the casting tests
244    }
245
246    test::test_into! {
247        function: <utest as TryInto>::try_into,
248        into_types: (u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize)
249    }
250}
251
252crate::macro_impl!(convert);