Skip to main content

parse_net/
num.rs

1//! Decimal digits with overflow checks.
2
3use crate::error::{ErrorKind, Result, err};
4
5/// Parses `1*DIGIT` into a `u64`.
6///
7/// No sign, no whitespace, no trimming; leading zeros are allowed.
8///
9/// # Errors
10///
11/// [`ErrorKind::Empty`] for an empty input, [`ErrorKind::InvalidNumber`] at
12/// the first byte that is not a digit, [`ErrorKind::NumberOverflow`] at the
13/// digit that overflows.
14///
15/// # Examples
16///
17/// ```
18/// use parse_net::{num::parse_u64, ErrorKind};
19///
20/// assert_eq!(parse_u64(b"18446744073709551615")?, u64::MAX);
21/// assert_eq!(parse_u64(b"18446744073709551616").unwrap_err().kind(), ErrorKind::NumberOverflow);
22/// assert_eq!(parse_u64(b"+1").unwrap_err().kind(), ErrorKind::InvalidNumber);
23/// # Ok::<(), parse_net::ParseError>(())
24/// ```
25pub fn parse_u64(digits: &[u8]) -> Result<u64> {
26    if digits.is_empty() {
27        return err(ErrorKind::Empty, 0);
28    }
29    let mut n: u64 = 0;
30    for (i, &b) in digits.iter().enumerate() {
31        if !b.is_ascii_digit() {
32            return err(ErrorKind::InvalidNumber, i);
33        }
34        n = match n
35            .checked_mul(10)
36            .and_then(|m| m.checked_add(u64::from(b - b'0')))
37        {
38            Some(v) => v,
39            None => return err(ErrorKind::NumberOverflow, i),
40        };
41    }
42    Ok(n)
43}
44
45/// The value `delta-seconds` saturate at (RFC 9111 section 1.2.2).
46pub(crate) const DELTA_SECONDS_MAX: u32 = 2_147_483_648;
47
48/// Parses `1*DIGIT` by the `delta-seconds` rule of RFC 9111 section 1.2.2:
49/// any number of digits, values above 2147483648 give 2147483648.
50///
51/// # Errors
52///
53/// [`ErrorKind::Empty`], [`ErrorKind::InvalidNumber`].
54///
55/// # Examples
56///
57/// ```
58/// use parse_net::num::parse_u32_saturating;
59///
60/// assert_eq!(parse_u32_saturating(b"60")?, 60);
61/// assert_eq!(parse_u32_saturating(b"99999999999999999999")?, 2_147_483_648);
62/// # Ok::<(), parse_net::ParseError>(())
63/// ```
64pub fn parse_u32_saturating(digits: &[u8]) -> Result<u32> {
65    if digits.is_empty() {
66        return err(ErrorKind::Empty, 0);
67    }
68    let mut n: u32 = 0;
69    for (i, &b) in digits.iter().enumerate() {
70        if !b.is_ascii_digit() {
71            return err(ErrorKind::InvalidNumber, i);
72        }
73        n = n
74            .saturating_mul(10)
75            .saturating_add(u32::from(b - b'0'))
76            .min(DELTA_SECONDS_MAX);
77    }
78    Ok(n)
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_parse_u64_rejects_signs_and_overflow() {
87        assert_eq!(parse_u64(b"0"), Ok(0));
88        assert_eq!(parse_u64(b"007"), Ok(7));
89        assert_eq!(parse_u64(b"18446744073709551615"), Ok(u64::MAX));
90        let e = parse_u64(b"18446744073709551616").unwrap_err();
91        assert_eq!((e.kind(), e.offset()), (ErrorKind::NumberOverflow, 19));
92        assert_eq!(
93            parse_u64(b"+1").unwrap_err().kind(),
94            ErrorKind::InvalidNumber
95        );
96        assert_eq!(
97            parse_u64(b"-1").unwrap_err().kind(),
98            ErrorKind::InvalidNumber
99        );
100        assert_eq!(parse_u64(b"").unwrap_err().kind(), ErrorKind::Empty);
101        let e = parse_u64(b"1 ").unwrap_err();
102        assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 1));
103    }
104
105    #[test]
106    fn test_parse_u32_saturating_boundaries() {
107        assert_eq!(parse_u32_saturating(b"2147483647"), Ok(2_147_483_647));
108        assert_eq!(parse_u32_saturating(b"2147483648"), Ok(2_147_483_648));
109        assert_eq!(parse_u32_saturating(b"2147483649"), Ok(2_147_483_648));
110        assert_eq!(parse_u32_saturating(b"4294967296"), Ok(2_147_483_648));
111        assert_eq!(
112            parse_u32_saturating(b"").unwrap_err().kind(),
113            ErrorKind::Empty
114        );
115        assert_eq!(
116            parse_u32_saturating(b"6s").unwrap_err().kind(),
117            ErrorKind::InvalidNumber
118        );
119    }
120}