Skip to main content

bnum/bint/
radix.rs

1use crate::doc;
2use crate::errors::ParseIntError;
3use crate::int::radix::assert_range;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::num::IntErrorKind;
7
8macro_rules! radix {
9    ($BUint: ident, $BInt: ident, $Digit: ident) => {
10        #[doc = doc::radix::impl_desc!($BInt)]
11        impl<const N: usize> $BInt<N> {
12            /// Converts a byte slice in a given base to an integer. The input slice must contain ascii/utf8 characters in [0-9a-zA-Z].
13            ///
14            /// This function is equivalent to the [`from_str_radix`](#method.from_str_radix) function for a string slice equivalent to the byte slice and the same radix.
15            ///
16            /// Returns `None` if the conversion of the byte slice to string slice fails or if a digit is larger than or equal to the given radix, otherwise the integer is wrapped in `Some`.
17            #[inline]
18            pub const fn parse_bytes(buf: &[u8], radix: u32) -> Option<Self> {
19                let s = crate::nightly::option_try!(crate::nightly::ok!(core::str::from_utf8(buf)));
20                crate::nightly::ok!(Self::from_str_radix(s, radix))
21            }
22
23            /// Converts a slice of big-endian digits in the given radix to an integer. The digits are first converted to an unsigned integer, then this is transmuted to a signed integer. Each `u8` of the slice is interpreted as one digit of base `radix` of the number, so this function will return `None` if any digit is greater than or equal to `radix`, otherwise the integer is wrapped in `Some`.
24            ///
25            /// For examples, see the
26            #[doc = concat!("[`from_radix_be`](crate::", stringify!($BUint), "::from_radix_be) method documentation for [`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
27            #[inline]
28            pub const fn from_radix_be(buf: &[u8], radix: u32) -> Option<Self> {
29                match $BUint::from_radix_be(buf, radix) { // TODO: use Option::map when stable
30                    Some(uint) => Some(Self::from_bits(uint)),
31                    None => None,
32                }
33            }
34
35            /// Converts a slice of big-endian digits in the given radix to an integer. The digits are first converted to an unsigned integer, then this is transmuted to a signed integer. Each `u8` of the slice is interpreted as one digit of base `radix` of the number, so this function will return `None` if any digit is greater than or equal to `radix`, otherwise the integer is wrapped in `Some`.
36            ///
37            /// For examples, see the
38            #[doc = concat!("[`from_radix_le`](crate::", stringify!($BUint), "::from_radix_le) method documentation for [`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
39            #[inline]
40            pub const fn from_radix_le(buf: &[u8], radix: u32) -> Option<Self> {
41                match $BUint::from_radix_le(buf, radix) { // TODO: use Option::map when stable
42                    Some(uint) => Some(Self::from_bits(uint)),
43                    None => None,
44                }
45            }
46
47            /// Converts a string slice in a given base to an integer.
48            ///
49            /// The string is expected to be an optional `+` or `-` sign followed by digits. Leading and trailing whitespace represent an error. Digits are a subset of these characters, depending on `radix`:
50            ///
51            /// - `0-9`
52            /// - `a-z`
53            /// - `A-Z`
54            ///
55            /// # Panics
56            ///
57            /// This function panics if `radix` is not in the range from 2 to 36 inclusive.
58            ///
59            /// For examples, see the
60            #[doc = concat!("[`from_str_radix`](crate::", stringify!($BUint), "::from_str_radix) method documentation for [`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
61            #[inline]
62            pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
63                assert_range!(radix, 36);
64                if src.is_empty() {
65                    return Err(ParseIntError {
66                        kind: IntErrorKind::Empty,
67                    });
68                }
69                let mut negative = false;
70                let mut leading_sign = false;
71                let buf = src.as_bytes();
72                if buf[0] == b'-' {
73                    negative = true;
74                    leading_sign = true;
75                } else if buf[0] == b'+' {
76                    leading_sign = true;
77                }
78
79                match $BUint::from_buf_radix_internal::<true, true>(buf, radix, leading_sign) {
80                    Ok(uint) => {
81                        if negative {
82                            if uint.bit(Self::BITS - 1) && uint.trailing_zeros() != Self::BITS - 1 {
83                                Err(ParseIntError {
84                                    kind: IntErrorKind::NegOverflow,
85                                })
86                            } else {
87                                Ok(Self::from_bits(uint).wrapping_neg())
88                            }
89                        } else {
90                            let out = Self::from_bits(uint);
91                            if out.is_negative() {
92                                Err(ParseIntError {
93                                    kind: IntErrorKind::PosOverflow,
94                                })
95                            } else {
96                                Ok(out)
97                            }
98                        }
99                    }
100                    Err(err) => {
101                        if let IntErrorKind::PosOverflow = err.kind() {
102                            if negative {
103                                return Err(ParseIntError {
104                                    kind: IntErrorKind::NegOverflow,
105                                });
106                            }
107                        }
108                        return Err(err)
109                    }
110                }
111            }
112
113            #[doc = doc::radix::parse_str_radix!($BUint)]
114            #[inline]
115            pub const fn parse_str_radix(src: &str, radix: u32) -> Self {
116                match Self::from_str_radix(src, radix) {
117                    Ok(n) => n,
118                    Err(e) => panic!("{}", e.description()),
119                }
120            }
121
122            /// Returns the integer as a string in the given radix.
123            ///
124            /// # Panics
125            ///
126            /// This function panics if `radix` is not in the range from 2 to 36 inclusive.
127            ///
128            /// For examples, see the
129            #[doc = concat!("[`to_str_radix`](crate::", stringify!($BUint), "::to_str_radix) method documentation for [`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
130            #[inline]
131            pub fn to_str_radix(&self, radix: u32) -> String {
132                if self.is_negative() {
133                    format!("-{}", self.unsigned_abs().to_str_radix(radix))
134                } else {
135                    self.bits.to_str_radix(radix)
136                }
137            }
138
139            /// Returns the integer's underlying representation as an unsigned integer in the given base in big-endian digit order.
140            ///
141            /// # Panics
142            ///
143            /// This function panics if `radix` is not in the range from 2 to 256 inclusive.
144            ///
145            /// For examples, see the
146            #[doc = concat!("[`to_radix_be`](crate::", stringify!($BUint), "::to_radix_be) method documentation for [`", stringify!($BUint), "`]")]
147            #[inline]
148            pub fn to_radix_be(&self, radix: u32) -> Vec<u8> {
149                self.bits.to_radix_be(radix)
150            }
151
152            /// Returns the integer's underlying representation as an unsigned integer in the given base in little-endian digit order.
153            ///
154            /// # Panics
155            ///
156            /// This function panics if `radix` is not in the range from 2 to 256 inclusive.
157            ///
158            /// For examples, see the
159            #[doc = concat!("[`to_radix_le`](crate::", stringify!($BUint), "::to_radix_le) method documentation for [`", stringify!($BUint), "`](crate::", stringify!($BUint), ").")]
160            #[inline]
161            pub fn to_radix_le(&self, radix: u32) -> Vec<u8> {
162                self.bits.to_radix_le(radix)
163            }
164        }
165    };
166}
167
168#[cfg(test)]
169crate::test::all_digit_tests! {
170    use crate::test::{quickcheck_from_to_radix, test_bignum, self};
171    use crate::test::types::itest;
172    use crate::BInt;
173
174    test_bignum! {
175        function: <itest>::from_str_radix,
176        cases: [
177            ("-14359abcasdhfkdgdfgsde", 34u32),
178            ("+23797984569ahgkhhjdskjdfiu", 32u32),
179            ("-253613132341435345", 7u32),
180            ("+23467abcad47790809ef37", 16u32),
181            ("-712930769245766867875986646", 10u32),
182            ("-😱234292", 36u32),
183            ("-+345934758", 13u32),
184            ("12💯12", 15u32),
185            ("gap gap", 36u32),
186            ("-9223372036854775809", 10u32),
187            ("-1000000000000000000001", 8u32),
188            ("+1000000000000000000001", 8u32),
189            ("-8000000000000001", 16u32),
190            ("+-23459374", 15u32),
191            ("8000000000000000", 16u32),
192            ("", 10u32)
193        ]
194    }
195
196    quickcheck_from_to_radix!(itest, radix_be, 256);
197    quickcheck_from_to_radix!(itest, radix_le, 256);
198    quickcheck_from_to_radix!(itest, str_radix, 36);
199
200    test::quickcheck_from_str_radix!(itest, "+" | "-");
201    test::quickcheck_from_str!(itest);
202
203    #[test]
204    fn from_to_radix_le() {
205        let buf = &[
206            61, 45, 48, 20, 37, 59, 53, 28, 28, 52, 54, 13, 44, 3, 46, 42, 20, 46, 37, 32,
207            13, 27, 47, 30, 33, 25, 3, 32, 4, 54, 53, 6, 44, 25, 10, 22, 33, 48, 7, 17,
208        ];
209        let u = BInt::<100>::from_radix_le(buf, 64).unwrap();
210        let v = u.to_radix_le(64);
211        assert_eq!(v, buf);
212
213        let buf = &[
214            33, 34, 61, 53, 74, 67, 54, 62, 22, 29, 4, 2, 43, 73, 74, 24, 8, 74, 65, 3, 78,
215        ];
216        let option = BInt::<100>::from_radix_le(buf, 78);
217        assert!(option.is_none());
218
219        let buf = &[
220            1, 3, 3, 0, 2, 1, 2, 3, 0, 4, 1, 2, 0, 0, 0, 0, 3, 2, 0, 1, 0, 4, 1, 3, 1, 4,
221            3, 3, 3, 4, 1, 2, 2, 1, 3, 0, 2, 1, 2, 3, 1, 1, 0, 2, 2, 1, 1, 2, 1, 0, 0, 0,
222            3, 3, 3, 0, 0, 4, 4, 2,
223        ];
224        let u = BInt::<100>::from_radix_le(buf, 5).unwrap();
225        let v = u.to_radix_le(5);
226        assert_eq!(v, buf);
227    }
228    #[test]
229    fn from_to_radix_be() {
230        let buf = &[
231            29, 89, 92, 118, 69, 140, 141, 70, 71, 76, 66, 13, 30, 28, 38, 145, 40, 7, 57,
232            18, 25, 65, 150, 119, 155, 18, 64, 76, 106, 87,
233        ];
234        let u = BInt::<100>::from_radix_be(buf, 157).unwrap();
235        let v = u.to_radix_be(157);
236        assert_eq!(v, buf);
237
238        let buf = &[
239            1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
240            1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
241            1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,
242            0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1,
243        ];
244        let u = BInt::<100>::from_radix_be(buf, 2).unwrap();
245        let v = u.to_radix_be(2);
246        assert_eq!(v, buf);
247
248        let buf = &[
249            91, 167, 5, 99, 61, 38, 158, 149, 115, 79, 13, 118, 53, 16, 144, 123, 70, 81,
250            78, 61, 39, 6, 34, 95, 98, 23, 175, 182,
251        ];
252        let option = BInt::<100>::from_radix_le(buf, 180);
253        assert!(option.is_none());
254
255        let buf = &[
256            39, 90, 119, 93, 95, 7, 70, 81, 3, 100, 39, 107, 98, 31, 61, 5, 36, 19, 18,
257            124, 4, 77, 119, 17, 121, 116, 24, 35,
258        ];
259        let u = BInt::<100>::from_radix_be(buf, 128).unwrap();
260        let v = u.to_radix_be(128);
261        assert_eq!(v, buf);
262    }
263    #[test]
264    fn from_to_str_radix() {
265        let src = "-293487598aashkhkhakb8345cbvjkus";
266        let u = BInt::<100>::from_str_radix(src, 35).unwrap();
267        let v = u.to_str_radix(35);
268        assert_eq!(v, src);
269
270        let src = "zzzzzzzzzzzzzzzzzzzzzzzzz";
271        let result = BInt::<1>::from_str_radix(src, 36);
272        assert!(result.is_err());
273
274        let invalid = "inval_id string";
275        let result = BInt::<1>::from_str_radix(invalid, 36);
276        assert!(result.is_err());
277
278        let src = "72954hslfhbui79845y6audfgiu984h5ihhhdfg";
279        let u = BInt::<100>::from_str_radix(src, 36).unwrap();
280        assert_eq!(u.to_str_radix(36), src);
281    }
282    #[test]
283    fn parse_bytes() {
284        let src = "1797972456987acbdead7889";
285        let u = BInt::<100>::parse_bytes(src.as_bytes(), 16).unwrap();
286        let v = BInt::<100>::from_str_radix(src, 16).unwrap();
287        assert_eq!(u, v);
288        assert_eq!(v.to_str_radix(16), src);
289
290        let bytes = b"279874657dgfhjh";
291        let option = BInt::<100>::parse_bytes(bytes, 11);
292        assert!(option.is_none());
293    }
294}
295
296crate::macro_impl!(radix);