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    // Nine digits cannot overflow a u32 (999,999,999 < 2^32), so the usual
69    // lengths take a loop without saturating steps.
70    if digits.len() <= 9 {
71        let mut n: u32 = 0;
72        for (i, &b) in digits.iter().enumerate() {
73            let d = b.wrapping_sub(b'0');
74            if d > 9 {
75                return err(ErrorKind::InvalidNumber, i);
76            }
77            n = n * 10 + u32::from(d);
78        }
79        return Ok(n.min(DELTA_SECONDS_MAX));
80    }
81    let mut n: u32 = 0;
82    for (i, &b) in digits.iter().enumerate() {
83        if !b.is_ascii_digit() {
84            return err(ErrorKind::InvalidNumber, i);
85        }
86        n = n
87            .saturating_mul(10)
88            .saturating_add(u32::from(b - b'0'))
89            .min(DELTA_SECONDS_MAX);
90    }
91    Ok(n)
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_parse_u64_rejects_signs_and_overflow() {
100        assert_eq!(parse_u64(b"0"), Ok(0));
101        assert_eq!(parse_u64(b"007"), Ok(7));
102        assert_eq!(parse_u64(b"18446744073709551615"), Ok(u64::MAX));
103        let e = parse_u64(b"18446744073709551616").unwrap_err();
104        assert_eq!((e.kind(), e.offset()), (ErrorKind::NumberOverflow, 19));
105        assert_eq!(
106            parse_u64(b"+1").unwrap_err().kind(),
107            ErrorKind::InvalidNumber
108        );
109        assert_eq!(
110            parse_u64(b"-1").unwrap_err().kind(),
111            ErrorKind::InvalidNumber
112        );
113        assert_eq!(parse_u64(b"").unwrap_err().kind(), ErrorKind::Empty);
114        let e = parse_u64(b"1 ").unwrap_err();
115        assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 1));
116    }
117
118    #[test]
119    fn test_parse_u32_saturating_boundaries() {
120        assert_eq!(parse_u32_saturating(b"2147483647"), Ok(2_147_483_647));
121        assert_eq!(parse_u32_saturating(b"2147483648"), Ok(2_147_483_648));
122        assert_eq!(parse_u32_saturating(b"2147483649"), Ok(2_147_483_648));
123        assert_eq!(parse_u32_saturating(b"4294967296"), Ok(2_147_483_648));
124        assert_eq!(
125            parse_u32_saturating(b"").unwrap_err().kind(),
126            ErrorKind::Empty
127        );
128        assert_eq!(
129            parse_u32_saturating(b"6s").unwrap_err().kind(),
130            ErrorKind::InvalidNumber
131        );
132    }
133
134    /// The definition the fast path must agree with: exact value, clamped.
135    fn reference_u32(d: &[u8]) -> Result<u32> {
136        if d.is_empty() {
137            return err(ErrorKind::Empty, 0);
138        }
139        let mut n: u128 = 0;
140        for (i, &b) in d.iter().enumerate() {
141            if !b.is_ascii_digit() {
142                return err(ErrorKind::InvalidNumber, i);
143            }
144            n = (n * 10 + u128::from(b - b'0')).min(u128::from(u32::MAX) + 1);
145        }
146        Ok(u32::try_from(n.min(u128::from(DELTA_SECONDS_MAX))).unwrap_or(DELTA_SECONDS_MAX))
147    }
148
149    proptest::proptest! {
150        #![proptest_config(proptest::prelude::ProptestConfig::with_cases(8192))]
151
152        #[test]
153        fn test_parse_u32_saturating_agrees_with_reference(d in "[0-9]{0,22}|[0-9]{0,10}[^0-9][0-9]{0,4}") {
154            proptest::prop_assert_eq!(parse_u32_saturating(d.as_bytes()), reference_u32(d.as_bytes()));
155        }
156    }
157
158    #[test]
159    fn test_parse_u32_saturating_length_boundaries() {
160        assert_eq!(parse_u32_saturating(b"999999999"), Ok(999_999_999));
161        assert_eq!(parse_u32_saturating(b"000000000"), Ok(0));
162        assert_eq!(parse_u32_saturating(b"2147483647"), Ok(2_147_483_647));
163        assert_eq!(parse_u32_saturating(b"2147483648"), Ok(DELTA_SECONDS_MAX));
164        assert_eq!(parse_u32_saturating(b"2147483649"), Ok(DELTA_SECONDS_MAX));
165        assert_eq!(
166            parse_u32_saturating(b"99999999999999999999"),
167            Ok(DELTA_SECONDS_MAX)
168        );
169        let e = parse_u32_saturating(b"12345678/").unwrap_err();
170        assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 8));
171        let e = parse_u32_saturating(b"1234567890:").unwrap_err();
172        assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 10));
173    }
174}