Skip to main content

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