Skip to main content

decimal_rs/
parse.rs

1// Copyright 2021 CoD Technologies Corp.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Decimal parsing utilities.
16
17use crate::Decimal;
18use crate::convert::MAX_I128_REPR;
19use crate::decimal::{MAX_PRECISION, MAX_SCALE, MIN_SCALE};
20use crate::error::DecimalParseError;
21use std::convert::{TryFrom, TryInto};
22use std::str::FromStr;
23
24#[derive(Debug, PartialEq)]
25enum Sign {
26    Positive,
27    Negative,
28}
29
30/// The interesting parts of a decimal string.
31#[derive(Debug)]
32struct Parts<'a> {
33    pub sign: Sign,
34    pub integral: &'a [u8],
35    pub fractional: &'a [u8],
36    pub exp: i16,
37}
38
39/// Splits a decimal string bytes into sign and the rest, without inspecting or validating the rest.
40#[inline]
41fn extract_sign(s: &[u8]) -> (Sign, &[u8]) {
42    match s.first() {
43        Some(b'+') => (Sign::Positive, &s[1..]),
44        Some(b'-') => (Sign::Negative, &s[1..]),
45        _ => (Sign::Positive, s),
46    }
47}
48
49/// Carves off decimal digits up to the first non-digit character.
50#[inline]
51fn eat_digits(s: &[u8]) -> (&[u8], &[u8]) {
52    let i = s.iter().take_while(|&i| i.is_ascii_digit()).count();
53    (&s[..i], &s[i..])
54}
55
56/// Extracts exponent, if any.
57fn extract_exponent(s: &[u8], decimal_is_zero: bool) -> Result<(i16, &[u8]), DecimalParseError> {
58    let (sign, s) = extract_sign(s);
59    let (mut number, s) = eat_digits(s);
60
61    if number.is_empty() {
62        return Err(DecimalParseError::Invalid);
63    }
64
65    if decimal_is_zero {
66        return Ok((0, s));
67    }
68
69    while number.first() == Some(&b'0') {
70        number = &number[1..];
71    }
72
73    if number.len() > 3 {
74        return match sign {
75            Sign::Positive => Err(DecimalParseError::Overflow),
76            Sign::Negative => Err(DecimalParseError::Underflow),
77        };
78    }
79
80    let exp = {
81        let mut result: i16 = 0;
82        for &n in number {
83            result = result * 10 + (n - b'0') as i16;
84        }
85        match sign {
86            Sign::Positive => result,
87            Sign::Negative => -result,
88        }
89    };
90
91    Ok((exp, s))
92}
93
94/// Checks if the input string is a valid decimal and if so, locate the integral
95/// part, the fractional part, and the exponent in it.
96fn parse_decimal(s: &[u8]) -> Result<(Parts<'_>, &[u8]), DecimalParseError> {
97    let (sign, s) = extract_sign(s);
98
99    if s.is_empty() {
100        return Err(DecimalParseError::Invalid);
101    }
102
103    let (mut integral, s) = eat_digits(s);
104
105    while integral.first() == Some(&b'0') && integral.len() > 1 {
106        integral = &integral[1..];
107    }
108
109    let (fractional, exp, s) = match s.first() {
110        Some(&b'e') | Some(&b'E') => {
111            if integral.is_empty() {
112                return Err(DecimalParseError::Invalid);
113            }
114
115            let decimal_is_zero = integral[0] == b'0';
116            let (exp, s) = extract_exponent(&s[1..], decimal_is_zero)?;
117            (&b""[..], exp, s)
118        }
119        Some(&b'.') => {
120            let (mut fractional, s) = eat_digits(&s[1..]);
121            if integral.is_empty() && fractional.is_empty() {
122                return Err(DecimalParseError::Invalid);
123            }
124
125            while fractional.last() == Some(&b'0') {
126                fractional = &fractional[0..fractional.len() - 1];
127            }
128
129            match s.first() {
130                Some(&b'e') | Some(&b'E') => {
131                    let decimal_is_zero = (integral.is_empty() || integral[0] == b'0') && fractional.is_empty();
132                    let (exp, s) = extract_exponent(&s[1..], decimal_is_zero)?;
133                    (fractional, exp, s)
134                }
135                _ => (fractional, 0, s),
136            }
137        }
138        _ => {
139            if integral.is_empty() {
140                return Err(DecimalParseError::Invalid);
141            }
142
143            (&b""[..], 0, s)
144        }
145    };
146
147    Ok((
148        Parts {
149            sign,
150            integral,
151            fractional,
152            exp,
153        },
154        s,
155    ))
156}
157
158/// Carves off whitespaces up to the first non-whitespace character.
159#[inline]
160fn eat_whitespaces(s: &[u8]) -> &[u8] {
161    let i = s.iter().take_while(|&i| i.is_ascii_whitespace()).count();
162    &s[i..]
163}
164
165/// Extracts `NaN` value.
166#[inline]
167fn extract_nan(s: &[u8]) -> (bool, &[u8]) {
168    if s.len() < 3 {
169        (false, s)
170    } else {
171        let mut buf: [u8; 3] = s[0..3].try_into().unwrap();
172        buf.make_ascii_lowercase();
173        if &buf == b"nan" { (true, &s[3..]) } else { (false, s) }
174    }
175}
176
177/// Parses a string bytes and put the number into this variable.
178///
179/// This function does not handle leading or trailing spaces, and it doesn't
180/// accept `NaN` either. It returns the remaining string bytes so that caller can
181/// check for trailing spaces/garbage if deemed necessary.
182#[inline]
183fn parse_str(s: &[u8]) -> Result<(Decimal, &[u8]), DecimalParseError> {
184    let (
185        Parts {
186            sign,
187            integral,
188            fractional,
189            exp,
190        },
191        s,
192    ) = parse_decimal(s)?;
193
194    let mut integral = integral;
195    let mut fractional = fractional;
196    let mut scale = -exp;
197
198    let mut carry = false;
199    const MAX_PRECISION_USIZE: usize = MAX_PRECISION as usize;
200
201    // normalized_exp is the exponent of a number with the format `0.{fractional}E{exponent}`, and the first digit of `fractional` is not 0.
202    // Suppose `a = 123.456e12`, convert `a` to the format above and get `0.123456e15`, then the normalized_exp of a is 15.
203    let mut normalized_exp = exp;
204
205    if integral == b"0" {
206        // fractional only
207        let zero_count = fractional.iter().take_while(|i| **i == b'0').count();
208        normalized_exp -= zero_count as i16;
209
210        let max_fractional_precision = MAX_PRECISION_USIZE + zero_count;
211        if fractional.len() > max_fractional_precision {
212            carry = fractional[max_fractional_precision] > b'4';
213            fractional = &fractional[0..max_fractional_precision];
214        }
215
216        debug_assert!(fractional.len() <= max_fractional_precision);
217    } else {
218        let int_len = integral.len() as i16;
219        normalized_exp += int_len;
220
221        if int_len > MAX_PRECISION_USIZE as i16 {
222            carry = integral[MAX_PRECISION_USIZE] > b'4';
223            scale -= int_len - MAX_PRECISION_USIZE as i16;
224
225            integral = &integral[0..MAX_PRECISION_USIZE];
226            fractional = &[];
227        } else {
228            let max_fractional_precision = MAX_PRECISION_USIZE - int_len as usize;
229            if fractional.len() > max_fractional_precision {
230                carry = fractional[max_fractional_precision] > b'4';
231                fractional = &fractional[0..max_fractional_precision];
232            }
233
234            debug_assert!(fractional.len() <= max_fractional_precision);
235        }
236    };
237
238    let mut int = 0u128;
239    for &i in integral {
240        int = int * 10 + (i - b'0') as u128;
241    }
242    for &i in fractional {
243        int = int * 10 + (i - b'0') as u128;
244    }
245    // So far, `int` precision does not exceed MAX_PRECISION.
246
247    int += carry as u128;
248    if int > MAX_I128_REPR as u128 {
249        normalized_exp += 1;
250        int /= 10;
251        scale -= 1;
252    }
253
254    if normalized_exp <= -MAX_SCALE {
255        return Err(DecimalParseError::Underflow);
256    }
257    if normalized_exp > -MIN_SCALE {
258        return Err(DecimalParseError::Overflow);
259    }
260
261    let negative = if int != 0 { sign == Sign::Negative } else { false };
262
263    scale += fractional.len() as i16;
264    Ok((unsafe { Decimal::from_parts_unchecked(int, scale, negative) }, s))
265}
266
267/// Parses a string slice and creates a decimal.
268///
269/// This function handles leading or trailing spaces, and it
270/// accepts `NaN` either.
271#[inline]
272fn from_bytes(s: &[u8]) -> Result<Decimal, DecimalParseError> {
273    let s = eat_whitespaces(s);
274    if s.is_empty() {
275        return Err(DecimalParseError::Empty);
276    }
277
278    let (is_nan, s) = extract_nan(s);
279
280    if is_nan {
281        Err(DecimalParseError::Invalid)
282    } else {
283        let (n, s) = parse_str(s)?;
284
285        if s.iter().any(|n| !n.is_ascii_whitespace()) {
286            return Err(DecimalParseError::Invalid);
287        }
288
289        Ok(n)
290    }
291}
292
293impl FromStr for Decimal {
294    type Err = DecimalParseError;
295
296    #[inline]
297    fn from_str(s: &str) -> Result<Self, Self::Err> {
298        from_bytes(s.as_bytes())
299    }
300}
301
302impl TryFrom<&[u8]> for Decimal {
303    type Error = DecimalParseError;
304
305    #[inline]
306    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
307        from_bytes(value)
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    fn assert_parse_empty<S: AsRef<str>>(s: S) {
316        let result = s.as_ref().parse::<Decimal>();
317        assert_eq!(result.unwrap_err(), DecimalParseError::Empty);
318    }
319
320    fn assert_parse_invalid<S: AsRef<str>>(s: S) {
321        let result = s.as_ref().parse::<Decimal>();
322        assert_eq!(result.unwrap_err(), DecimalParseError::Invalid);
323    }
324
325    fn assert_parse_overflow<S: AsRef<str>>(s: S) {
326        let result = s.as_ref().parse::<Decimal>();
327        assert_eq!(result.unwrap_err(), DecimalParseError::Overflow);
328    }
329
330    fn assert_parse_underflow<S: AsRef<str>>(s: S) {
331        let result = s.as_ref().parse::<Decimal>();
332        assert_eq!(result.unwrap_err(), DecimalParseError::Underflow);
333    }
334
335    #[test]
336    fn test_parse_error() {
337        assert_parse_empty("");
338        assert_parse_empty("   ");
339        assert_parse_invalid("-");
340        assert_parse_invalid("   -   ");
341        assert_parse_invalid("-.");
342        assert_parse_invalid("- 1");
343        assert_parse_invalid("-NaN");
344        assert_parse_invalid("NaN.");
345        assert_parse_invalid("NaN1");
346        assert_parse_invalid("   NaN   .   ");
347        assert_parse_invalid("   NaN   1   ");
348        assert_parse_invalid(".");
349        assert_parse_invalid("   .   ");
350        assert_parse_invalid("e");
351        assert_parse_invalid("   e   ");
352        assert_parse_invalid("-e");
353        assert_parse_invalid("-1e");
354        assert_parse_invalid("1e1.1");
355        assert_parse_invalid("-1 e1");
356        assert_parse_invalid("   x   ");
357        assert_parse_overflow("1e1000");
358        assert_parse_overflow("1e100000");
359        assert_parse_overflow("1e127");
360        assert_parse_underflow("1e-131");
361        assert_parse_underflow("1e-1000");
362        assert_parse_underflow("1e-100000");
363    }
364
365    fn assert_parse<S: AsRef<str>, V: AsRef<str>>(s: S, expected: V) {
366        let decimal = s.as_ref().parse::<Decimal>().unwrap();
367        assert_eq!(decimal.to_string(), expected.as_ref());
368    }
369
370    #[test]
371    fn test_parse_valid() {
372        // Integer
373        assert_parse("0", "0");
374        assert_parse("-0", "0");
375        assert_parse("   -0   ", "0");
376        assert_parse("00000.", "0");
377        assert_parse("-00000.", "0");
378        assert_parse("128", "128");
379        assert_parse("-128", "-128");
380        assert_parse("65536", "65536");
381        assert_parse("-65536", "-65536");
382        assert_parse("4294967296", "4294967296");
383        assert_parse("-4294967296", "-4294967296");
384        assert_parse("18446744073709551616", "18446744073709551616");
385        assert_parse("-18446744073709551616", "-18446744073709551616");
386        assert_parse(
387            "99999999999999999999999999999999999999",
388            "99999999999999999999999999999999999999",
389        );
390        assert_parse(
391            "0099999999999999999999999999999999999999",
392            "99999999999999999999999999999999999999",
393        );
394        assert_parse(
395            "-99999999999999999999999999999999999999",
396            "-99999999999999999999999999999999999999",
397        );
398        assert_parse("000000000123", "123");
399        assert_parse("-000000000123", "-123");
400        assert_parse(
401            "170141183460469231713240559642175554110",
402            "170141183460469231713240559642175554110",
403        );
404        assert_parse(
405            "999999999999999999999999999999999999990000000000",
406            "999999999999999999999999999999999999990000000000",
407        );
408
409        // Floating-point number
410        assert_parse("0.0", "0");
411        assert_parse("-0.0", "0");
412        assert_parse("   -0.0   ", "0");
413        assert_parse(".0", "0");
414        assert_parse(".00000", "0");
415        assert_parse("-.0", "0");
416        assert_parse("-.00000", "0");
417        assert_parse("128.128", "128.128");
418        assert_parse("-128.128", "-128.128");
419        assert_parse("65536.65536", "65536.65536");
420        assert_parse("-65536.65536", "-65536.65536");
421        assert_parse("4294967296.4294967296", "4294967296.4294967296");
422        assert_parse("-4294967296.4294967296", "-4294967296.4294967296");
423        assert_parse(
424            "9999999999999999999.9999999999999999999",
425            "9999999999999999999.9999999999999999999",
426        );
427        assert_parse(
428            "-9999999999999999999.9999999999999999999",
429            "-9999999999999999999.9999999999999999999",
430        );
431        assert_parse("000000000123.000000000123", "123.000000000123");
432        assert_parse("-000000000123.000000000123", "-123.000000000123");
433        assert_parse(
434            "00.000000000000000000000000000000000000123",
435            "0.000000000000000000000000000000000000123",
436        );
437        assert_parse(
438            "00.000000000000000000000000000000000000123e-87",
439            "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123",
440        );
441        assert_parse(
442            "99999999999999999999999999999999999999500000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
443            "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
444        );
445
446        // Scientific notation
447        assert_parse("0e0", "0");
448        assert_parse("-0E-0", "0");
449        assert_parse("0000000000E0000000000", "0");
450        assert_parse("-0000000000E-0000000000", "0");
451        assert_parse("00000000001e0000000000", "1");
452        assert_parse("-00000000001e-0000000000", "-1");
453        assert_parse("00000000001e00000000001", "10");
454        assert_parse("-00000000001e-00000000001", "-0.1");
455        assert_parse("1e10", "10000000000");
456        assert_parse("-1e-10", "-0.0000000001");
457        assert_parse("0000001.23456000e3", "1234.56");
458        assert_parse("-0000001.23456000E-3", "-0.00123456");
459        assert_parse("0e999", "0");
460        assert_parse("0e+99999", "0");
461        assert_parse("0e9999999", "0");
462        assert_parse("0.e999", "0");
463        assert_parse("0.e+99999", "0");
464        assert_parse("0.e9999999", "0");
465        assert_parse("0.0e999", "0");
466        assert_parse("0.0e+99999", "0");
467        assert_parse("0.0e9999999", "0");
468        assert_parse("0.0000e999", "0");
469        assert_parse("0.0000e+99999", "0");
470        assert_parse("0.0000e9999999", "0");
471        assert_parse(".000e999", "0");
472        assert_parse(".000e+99999", "0");
473        assert_parse(".000e9999999", "0");
474    }
475
476    #[test]
477    fn test_parse_boundary() {
478        assert_parse(
479            "100E-131",
480            "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100",
481        );
482        assert_parse(
483            "0.000012345E130",
484            "123450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
485        );
486        assert_parse(
487            "4.94065645841247E-126",
488            "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000494065645841247",
489        );
490        assert_parse(
491            "1234.94065645841247E-126",
492            "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123494065645841247",
493        );
494        assert_parse(
495            "12345678987654321999999E-132",
496            "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012345678987654321999999",
497        );
498        assert_parse(
499            "10000000000000000000000000000000000000e88",
500            "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
501        );
502        assert_parse(
503            "0.999999999999999999999999999999999999995e-130",
504            "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000",
505        );
506        assert_parse_underflow("0.999999999999999999999999999999999999995e-131");
507        assert_parse_overflow(
508            "999999999999999999999999999999999999995000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
509        );
510    }
511
512    #[test]
513    fn test_parse_over_precision_but_valid() {
514        // integer only
515        assert_parse(
516            "999999999999999999999999999999999999999",
517            "1000000000000000000000000000000000000000",
518        );
519        assert_parse(
520            "900719925474099290071992547409929007112123123123123",
521            "900719925474099290071992547409929007110000000000000",
522        );
523
524        // fractional only
525        assert_parse(
526            "0.123123123123123135555555555555555555555555555555",
527            "0.12312312312312313555555555555555555556",
528        );
529        assert_parse(
530            "0.0000000123123123123123135555555555555555555555555555555",
531            "0.000000012312312312312313555555555555555555556",
532        );
533        assert_parse(
534            "0.0000000123123123123123135555555555555515555555555555555",
535            "0.000000012312312312312313555555555555551555556",
536        );
537        assert_parse(
538            "0.0000000123123123123123135555555555555565555551555555555",
539            "0.000000012312312312312313555555555555556555555",
540        );
541
542        // integer over precision
543        assert_parse(
544            "1231231231231231231231231255555555555555555555.123",
545            "1231231231231231231231231255555555555600000000",
546        );
547
548        // integer + fractional over precision
549        assert_parse(
550            "123123.5555555555555555555555555555555555555555",
551            "123123.55555555555555555555555555555556",
552        );
553
554        assert_parse_overflow(
555            "90071992547409929007199254740992900711212312312312312312312312312311111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
556        );
557    }
558}