dashu_int/parse/
mod.rs

1//! Integer parsing helpers.
2
3use crate::{
4    ibig::IBig,
5    radix::{self, is_radix_valid, Digit},
6    ubig::UBig,
7    Sign::*,
8};
9use core::str::FromStr;
10use dashu_base::ParseError;
11
12mod non_power_two;
13mod power_two;
14
15impl FromStr for UBig {
16    type Err = ParseError;
17    #[inline]
18    fn from_str(s: &str) -> Result<UBig, ParseError> {
19        UBig::from_str_radix(s, 10)
20    }
21}
22
23impl FromStr for IBig {
24    type Err = ParseError;
25    #[inline]
26    fn from_str(s: &str) -> Result<IBig, ParseError> {
27        IBig::from_str_radix(s, 10)
28    }
29}
30
31impl UBig {
32    /// Convert a string in a given base to [UBig].
33    ///
34    /// `src` may contain an optional `+` prefix.
35    /// Digits 10-35 are represented by `a-z` or `A-Z`.
36    ///
37    /// # Examples
38    /// ```
39    /// # use dashu_base::ParseError;
40    /// # use dashu_int::UBig;
41    /// assert_eq!(UBig::from_str_radix("+7ab", 32)?, UBig::from(7499u16));
42    /// # Ok::<(), ParseError>(())
43    /// ```
44    pub fn from_str_radix(src: &str, radix: u32) -> Result<UBig, ParseError> {
45        if !is_radix_valid(radix) {
46            return Err(ParseError::UnsupportedRadix);
47        }
48        let src = src.strip_prefix('+').unwrap_or(src);
49        UBig::from_str_radix_no_sign(src, radix)
50    }
51
52    /// Convert a string with an optional radix prefix to [UBig], returns the
53    /// parsed integer and radix.
54    ///
55    /// It's equivalent to [UBig::from_str_with_radix_default] with 10 as the default radix.
56    #[inline]
57    pub fn from_str_with_radix_prefix(src: &str) -> Result<(UBig, Digit), ParseError> {
58        UBig::from_str_with_radix_default(src, 10)
59    }
60
61    /// Convert a string with an optional radix prefix to [UBig], returns the
62    /// parsed integer and radix. If no prefix is present, then the default radix input
63    /// will be used for parsing.
64    ///
65    /// `src` may contain an optional `+` before the radix prefix.
66    ///
67    /// Allowed prefixes: `0b` for binary, `0o` for octal, `0x` for hexadecimal.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// # use dashu_base::ParseError;
73    /// # use dashu_int::UBig;
74    /// assert_eq!(UBig::from_str_with_radix_default("+0o17", 10)?, (UBig::from(0o17u8), 8));
75    /// assert_eq!(UBig::from_str_with_radix_default("0x1f", 10)?.0, UBig::from(0x1fu8));
76    /// # Ok::<(), ParseError>(())
77    /// ```
78    #[inline]
79    pub fn from_str_with_radix_default(
80        src: &str,
81        default_radix: Digit,
82    ) -> Result<(UBig, Digit), ParseError> {
83        let src = src.strip_prefix('+').unwrap_or(src);
84        UBig::from_str_with_radix_prefix_no_sign(src, default_radix)
85    }
86
87    /// Convert an unsigned string with an optional radix prefix to [UBig].
88    fn from_str_with_radix_prefix_no_sign(
89        src: &str,
90        default_radix: Digit,
91    ) -> Result<(UBig, Digit), ParseError> {
92        if let Some(bin) = src.strip_prefix("0b") {
93            UBig::from_str_radix_no_sign(bin, 2).map(|v| (v, 2))
94        } else if let Some(oct) = src.strip_prefix("0o") {
95            UBig::from_str_radix_no_sign(oct, 8).map(|v| (v, 8))
96        } else if let Some(hex) = src.strip_prefix("0x") {
97            UBig::from_str_radix_no_sign(hex, 16).map(|v| (v, 16))
98        } else {
99            UBig::from_str_radix_no_sign(src, default_radix).map(|v| (v, default_radix))
100        }
101    }
102
103    /// Convert an unsigned string to [UBig].
104    fn from_str_radix_no_sign(mut src: &str, radix: Digit) -> Result<UBig, ParseError> {
105        debug_assert!(radix::is_radix_valid(radix));
106        if src.is_empty() {
107            return Err(ParseError::NoDigits);
108        }
109
110        while let Some(src2) = src.strip_prefix('0') {
111            src = src2;
112        }
113
114        if radix.is_power_of_two() {
115            power_two::parse(src, radix)
116        } else {
117            non_power_two::parse(src, radix)
118        }
119    }
120}
121
122impl IBig {
123    /// Convert a string in a given base to [IBig].
124    ///
125    /// The string may contain a `+` or `-` prefix.
126    /// Digits 10-35 are represented by `a-z` or `A-Z`.
127    ///
128    /// # Examples
129    /// ```
130    /// # use dashu_base::ParseError;
131    /// # use dashu_int::IBig;
132    /// assert_eq!(IBig::from_str_radix("-7ab", 32)?, IBig::from(-7499));
133    /// # Ok::<(), ParseError>(())
134    /// ```
135    pub fn from_str_radix(mut src: &str, radix: u32) -> Result<IBig, ParseError> {
136        if !is_radix_valid(radix) {
137            return Err(ParseError::UnsupportedRadix);
138        }
139
140        let sign = match src.strip_prefix('-') {
141            Some(s) => {
142                src = s;
143                Negative
144            }
145            None => {
146                src = src.strip_prefix('+').unwrap_or(src);
147                Positive
148            }
149        };
150        let mag = UBig::from_str_radix_no_sign(src, radix)?;
151        Ok(IBig(mag.0.with_sign(sign)))
152    }
153
154    /// Convert a string with an optional radix prefix to [IBig], return the
155    /// parsed integer and radix.
156    ///
157    /// It's equivalent to [IBig::from_str_with_radix_default] with 10 as the default radix.
158    pub fn from_str_with_radix_prefix(src: &str) -> Result<(IBig, Digit), ParseError> {
159        IBig::from_str_with_radix_default(src, 10)
160    }
161
162    /// Convert a string with an optional radix prefix to [IBig], return the
163    /// parsed integer and radix. If no prefix is present, then the default radix input
164    /// will be used for parsing.
165    ///
166    /// `src` may contain an '+' or `-` prefix before the radix prefix.
167    ///
168    /// Allowed prefixes: `0b` for binary, `0o` for octal, `0x` for hexadecimal.
169    ///
170    /// # Examples
171    /// ```
172    /// # use dashu_base::ParseError;
173    /// # use dashu_int::IBig;
174    /// assert_eq!(IBig::from_str_with_radix_default("+0o17", 10)?, (IBig::from(0o17), 8));
175    /// assert_eq!(IBig::from_str_with_radix_default("-0x1f", 10)?.0, IBig::from(-0x1f));
176    /// # Ok::<(), ParseError>(())
177    /// ```
178    #[inline]
179    pub fn from_str_with_radix_default(
180        src: &str,
181        default_radix: Digit,
182    ) -> Result<(IBig, Digit), ParseError> {
183        let (src, sign) = match src.strip_prefix('-') {
184            Some(s) => (s, Negative),
185            None => (src.strip_prefix('+').unwrap_or(src), Positive),
186        };
187        let (mag, radix) = UBig::from_str_with_radix_prefix_no_sign(src, default_radix)?;
188        Ok((IBig(mag.0.with_sign(sign)), radix))
189    }
190}