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}