1use crate::error::{ErrorKind, Result, err};
4
5pub 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
45pub(crate) const DELTA_SECONDS_MAX: u32 = 2_147_483_648;
47
48pub fn parse_u32_saturating(digits: &[u8]) -> Result<u32> {
65 if digits.is_empty() {
66 return err(ErrorKind::Empty, 0);
67 }
68 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 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}