1use super::*;
2use std::{
3 fmt::{self, Display},
4 str::FromStr,
5};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ParseDecimalError {
9 kind: DecimalErrorKind,
10}
11
12impl ParseDecimalError {
13 fn empty() -> Self {
14 Self {
15 kind: DecimalErrorKind::Empty,
16 }
17 }
18 fn invalid_digit() -> Self {
19 Self {
20 kind: DecimalErrorKind::InvalidDigit,
21 }
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26enum DecimalErrorKind {
27 Empty,
28 InvalidDigit,
29}
30
31impl Display for ParseDecimalError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self.kind {
34 DecimalErrorKind::Empty => write!(f, "empty string"),
35 DecimalErrorKind::InvalidDigit => write!(f, "invalid digit"),
36 }
37 }
38}
39
40impl FromStr for Decimal {
41 type Err = ParseDecimalError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 if s.is_empty() {
45 return Err(ParseDecimalError::empty());
46 }
47
48 let (s, sign) = if let Some(s) = s.strip_prefix('+') {
49 (s, Sign::Plus)
50 } else if let Some(s) = s.strip_prefix('-') {
51 (s, Sign::Minus)
52 } else {
53 (s, Sign::Plus)
54 };
55
56 let (integer_str, decimal_str) = if let Some((integer_str, decimal_str)) = s.split_once('.')
57 {
58 (integer_str, decimal_str)
59 } else {
60 (s, "")
61 };
62
63 if !integer_str.is_ascii() || !decimal_str.is_ascii() {
64 return Err(ParseDecimalError::invalid_digit());
65 }
66
67 let integer_bytes = integer_str.trim_start_matches('0').as_bytes();
68 let decimal_bytes = decimal_str.trim_end_matches('0').as_bytes();
69
70 let mut integer = Vec::with_capacity((integer_bytes.len() + (RADIX_LEN - 1)) / RADIX_LEN);
71 for chunk in integer_bytes.rchunks(18) {
72 let chunk = unsafe { std::str::from_utf8_unchecked(chunk) };
73 match chunk.parse::<u64>() {
74 Ok(val) => integer.push(val),
75 Err(_) => return Err(ParseDecimalError::invalid_digit()),
76 }
77 }
78
79 let mut decimal = Vec::with_capacity((decimal_bytes.len() + (RADIX_LEN - 1)) / RADIX_LEN);
80 for chunk in decimal_bytes.chunks(18) {
81 let chunk = unsafe { std::str::from_utf8_unchecked(chunk) };
82 match chunk.parse::<u64>() {
83 Ok(val) => decimal.push(val * POW10[RADIX_LEN - chunk.len()]),
84 Err(_) => return Err(ParseDecimalError::invalid_digit()),
85 }
86 }
87
88 let sign = if integer.is_empty() && decimal.is_empty() {
89 Sign::Zero
90 } else {
91 sign
92 };
93
94 Ok(Decimal {
95 sign,
96 integer,
97 decimal,
98 })
99 }
100}
101
102impl Display for Decimal {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self.sign {
105 Sign::Minus => write!(f, "-")?,
106 Sign::Zero => return write!(f, "0"),
107 Sign::Plus => {}
108 }
109
110 if let Some(last) = self.integer.last() {
111 write!(f, "{}", last)?;
112 for &val in self.integer.iter().rev().skip(1) {
113 write!(f, "{:018}", val)?;
114 }
115 } else {
116 write!(f, "0")?;
117 }
118
119 if let Some(last) = self.decimal.last() {
120 write!(f, ".")?;
121 for &val in self.decimal.iter().take(self.decimal.len() - 1) {
122 write!(f, "{:018}", val)?;
123 }
124 let mut l = 0;
125 let mut r = RADIX_LEN;
126 while r - l > 1 {
127 let m = (l + r) / 2;
128 if last % POW10[m] == 0 {
129 l = m;
130 } else {
131 r = m;
132 }
133 }
134 debug_assert!(last % POW10[l] == 0);
135 debug_assert!(r == RADIX_LEN || last % POW10[r] != 0);
136 write!(f, "{:0width$}", last / POW10[l], width = RADIX_LEN - l)?;
137 }
138
139 Ok(())
140 }
141}
142
143impl IterScan for Decimal {
144 type Output = Self;
145 fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
146 iter.next()?.parse().ok()
147 }
148}
149
150macro_rules! impl_from_unsigned {
151 ($base:ty; $($t:ty)*) => {
152 $(
153 impl From<$t> for Decimal {
154 fn from(val: $t) -> Self {
155 if val == 0 {
156 return Decimal::zero();
157 }
158 let mut val = val as $base;
159 let mut integer = Vec::new();
160 while val > 0 {
161 integer.push((val % RADIX as $base) as u64);
162 val /= RADIX as $base;
163 }
164 Decimal {
165 sign: Sign::Plus,
166 integer,
167 decimal: Vec::new(),
168 }
169 }
170 }
171 )*
172 };
173}
174impl_from_unsigned!(u64; u8 u16 u32 u64 usize);
175impl_from_unsigned!(u128; u128);
176
177macro_rules! impl_from_signed {
178 ($base:ty; $($t:ty)*) => {
179 $(
180 impl From<$t> for Decimal {
181 fn from(val: $t) -> Self {
182 let d = Decimal::from(val.unsigned_abs() as $base);
183 if val.is_negative() {
184 -d
185 } else {
186 d
187 }
188 }
189 }
190 )*
191 };
192}
193impl_from_signed!(u64; i8 i16 i32 i64 isize);
194impl_from_signed!(u128; i128);
195
196macro_rules! impl_from_through_string {
197 ($($t:ty)*) => {
198 $(
199 impl From<$t> for Decimal {
200 fn from(val: $t) -> Self {
201 val.to_string().parse().unwrap()
202 }
203 }
204 )*
205 };
206}
207impl_from_through_string!(f32 f64);
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use test_case::test_case;
213
214 #[test_case(
215 "0",
216 Ok(Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] });
217 "zero"
218 )]
219 #[test_case(
220 "1",
221 Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
222 "plus integer"
223 )]
224 #[test_case(
225 "+1",
226 Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
227 "plus integer with plus"
228 )]
229 #[test_case(
230 "-1",
231 Ok(Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![] });
232 "minus integer"
233 )]
234 #[test_case(
235 "1.2",
236 Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![200000000000000000] });
237 "plus decimal"
238 )]
239 #[test_case(
240 "-1.2",
241 Ok(Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![200000000000000000] });
242 "minus decimal"
243 )]
244 #[test_case(
245 "000000000000000000001.00000000000000000000",
246 Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
247 "zero padding"
248 )]
249 #[test_case(
250 ".1",
251 Ok(Decimal { sign: Sign::Plus, integer: vec![], decimal: vec![100000000000000000] });
252 "without integer"
253 )]
254 #[test_case(
255 "12345678901234567890.12345678901234567890",
256 Ok(Decimal { sign: Sign::Plus, integer: vec![345678901234567890, 12], decimal: vec![123456789012345678, 900000000000000000] });
257 "long"
258 )]
259 #[test_case(
260 "",
261 Err(ParseDecimalError { kind: DecimalErrorKind::Empty });
262 "empty"
263 )]
264 #[test_case(
265 "a.012",
266 Err(ParseDecimalError { kind: DecimalErrorKind::InvalidDigit });
267 "invalid digit in integer"
268 )]
269 #[test_case(
270 "012.a",
271 Err(ParseDecimalError { kind: DecimalErrorKind::InvalidDigit });
272 "invalid digit in decimal"
273 )]
274 fn test_from_str(s: &str, expected: Result<Decimal, ParseDecimalError>) {
275 assert_eq!(expected, s.parse());
276 }
277
278 #[test_case(
279 Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] },
280 "0";
281 "zero"
282 )]
283 #[test_case(
284 Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] },
285 "1";
286 "plus integer"
287 )]
288 #[test_case(
289 Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![] },
290 "-1";
291 "minus integer"
292 )]
293 #[test_case(
294 Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![200000000000000000] },
295 "1.2";
296 "plus decimal"
297 )]
298 #[test_case(
299 Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![200000000000000000] },
300 "-1.2";
301 "minus decimal"
302 )]
303 #[test_case(
304 Decimal { sign: Sign::Plus, integer: vec![], decimal: vec![100000000000000000] },
305 "0.1";
306 "without integer"
307 )]
308 #[test_case(
309 Decimal { sign: Sign::Plus, integer: vec![345678901234567890, 12], decimal: vec![123456789012345678, 900000000000000000] },
310 "12345678901234567890.1234567890123456789";
311 "long"
312 )]
313 #[test_case(
314 Decimal { sign: Sign::Plus, integer: vec![0], decimal: vec![1] },
315 "0.000000000000000001";
316 "small decimal"
317 )]
318 fn test_display(decimal: Decimal, expected: &str) {
319 assert_eq!(expected, decimal.to_string());
320 }
321
322 #[test_case(u8::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u8 zero")]
323 #[test_case(u8::MAX, Decimal { sign: Sign::Plus, integer: vec![255], decimal: vec![] }; "u8 max")]
324 #[test_case(u16::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u16 zero")]
325 #[test_case(u16::MAX, Decimal { sign: Sign::Plus, integer: vec![65535], decimal: vec![] }; "u16 max")]
326 #[test_case(u32::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u32 zero")]
327 #[test_case(u32::MAX, Decimal { sign: Sign::Plus, integer: vec![4294967295], decimal: vec![] }; "u32 max")]
328 #[test_case(u64::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u64 zero")]
329 #[test_case(u64::MAX, Decimal { sign: Sign::Plus, integer: vec![446744073709551615, 18], decimal: vec![] }; "u64 max")]
330 #[test_case(u128::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u128 zero")]
331 #[test_case(u128::MAX, Decimal { sign: Sign::Plus, integer: vec![374607431768211455, 282366920938463463, 340], decimal: vec![] }; "u128 max")]
332 #[test_case(i8::MIN, Decimal { sign: Sign::Minus, integer: vec![128], decimal: vec![] }; "i8 min")]
333 #[test_case(0i8, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i8 zero")]
334 #[test_case(i8::MAX, Decimal { sign: Sign::Plus, integer: vec![127], decimal: vec![] }; "i8 max")]
335 #[test_case(i16::MIN, Decimal { sign: Sign::Minus, integer: vec![32768], decimal: vec![] }; "i16 min")]
336 #[test_case(0i16, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i16 zero")]
337 #[test_case(i16::MAX, Decimal { sign: Sign::Plus, integer: vec![32767], decimal: vec![] }; "i16 max")]
338 #[test_case(i32::MIN, Decimal { sign: Sign::Minus, integer: vec![2147483648], decimal: vec![] }; "i32 min")]
339 #[test_case(0i32, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i32 zero")]
340 #[test_case(i32::MAX, Decimal { sign: Sign::Plus, integer: vec![2147483647], decimal: vec![] }; "i32 max")]
341 #[test_case(i64::MIN, Decimal { sign: Sign::Minus, integer: vec![223372036854775808, 9], decimal: vec![] }; "i64 min")]
342 #[test_case(0i64, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i64 zero")]
343 #[test_case(i64::MAX, Decimal { sign: Sign::Plus, integer: vec![223372036854775807, 9], decimal: vec![] }; "i64 max")]
344 #[test_case(i128::MIN, Decimal { sign: Sign::Minus, integer: vec![687303715884105728, 141183460469231731, 170], decimal: vec![] }; "i128 min")]
345 #[test_case(0i128, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i128 zero")]
346 #[test_case(i128::MAX, Decimal { sign: Sign::Plus, integer: vec![687303715884105727, 141183460469231731, 170], decimal: vec![] }; "i128 max")]
347 #[test_case(0f32, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "f32 zero")]
348 #[test_case(1.1f32, Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![100000000000000000] }; "f32 plus")]
349 #[test_case(-1.1f32, Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![100000000000000000] }; "f32 minus")]
350 #[test_case(0f64, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "f64 zero")]
351 #[test_case(1.1f64, Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![100000000000000000] }; "f64 plus")]
352 #[test_case(-1.1f64, Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![100000000000000000] }; "f64 minus")]
353 fn test_from(val: impl Into<Decimal>, expected: Decimal) {
354 assert_eq!(expected, val.into());
355 }
356}