use crate::error::{ErrorKind, Result, err};
pub fn parse_u64(digits: &[u8]) -> Result<u64> {
if digits.is_empty() {
return err(ErrorKind::Empty, 0);
}
let mut n: u64 = 0;
for (i, &b) in digits.iter().enumerate() {
if !b.is_ascii_digit() {
return err(ErrorKind::InvalidNumber, i);
}
n = match n
.checked_mul(10)
.and_then(|m| m.checked_add(u64::from(b - b'0')))
{
Some(v) => v,
None => return err(ErrorKind::NumberOverflow, i),
};
}
Ok(n)
}
pub(crate) const DELTA_SECONDS_MAX: u32 = 2_147_483_648;
pub fn parse_u32_saturating(digits: &[u8]) -> Result<u32> {
if digits.is_empty() {
return err(ErrorKind::Empty, 0);
}
if digits.len() <= 9 {
let mut n: u32 = 0;
for (i, &b) in digits.iter().enumerate() {
let d = b.wrapping_sub(b'0');
if d > 9 {
return err(ErrorKind::InvalidNumber, i);
}
n = n * 10 + u32::from(d);
}
return Ok(n.min(DELTA_SECONDS_MAX));
}
let mut n: u32 = 0;
for (i, &b) in digits.iter().enumerate() {
if !b.is_ascii_digit() {
return err(ErrorKind::InvalidNumber, i);
}
n = n
.saturating_mul(10)
.saturating_add(u32::from(b - b'0'))
.min(DELTA_SECONDS_MAX);
}
Ok(n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_u64_rejects_signs_and_overflow() {
assert_eq!(parse_u64(b"0"), Ok(0));
assert_eq!(parse_u64(b"007"), Ok(7));
assert_eq!(parse_u64(b"18446744073709551615"), Ok(u64::MAX));
let e = parse_u64(b"18446744073709551616").unwrap_err();
assert_eq!((e.kind(), e.offset()), (ErrorKind::NumberOverflow, 19));
assert_eq!(
parse_u64(b"+1").unwrap_err().kind(),
ErrorKind::InvalidNumber
);
assert_eq!(
parse_u64(b"-1").unwrap_err().kind(),
ErrorKind::InvalidNumber
);
assert_eq!(parse_u64(b"").unwrap_err().kind(), ErrorKind::Empty);
let e = parse_u64(b"1 ").unwrap_err();
assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 1));
}
#[test]
fn test_parse_u32_saturating_boundaries() {
assert_eq!(parse_u32_saturating(b"2147483647"), Ok(2_147_483_647));
assert_eq!(parse_u32_saturating(b"2147483648"), Ok(2_147_483_648));
assert_eq!(parse_u32_saturating(b"2147483649"), Ok(2_147_483_648));
assert_eq!(parse_u32_saturating(b"4294967296"), Ok(2_147_483_648));
assert_eq!(
parse_u32_saturating(b"").unwrap_err().kind(),
ErrorKind::Empty
);
assert_eq!(
parse_u32_saturating(b"6s").unwrap_err().kind(),
ErrorKind::InvalidNumber
);
}
fn reference_u32(d: &[u8]) -> Result<u32> {
if d.is_empty() {
return err(ErrorKind::Empty, 0);
}
let mut n: u128 = 0;
for (i, &b) in d.iter().enumerate() {
if !b.is_ascii_digit() {
return err(ErrorKind::InvalidNumber, i);
}
n = (n * 10 + u128::from(b - b'0')).min(u128::from(u32::MAX) + 1);
}
Ok(u32::try_from(n.min(u128::from(DELTA_SECONDS_MAX))).unwrap_or(DELTA_SECONDS_MAX))
}
proptest::proptest! {
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(8192))]
#[test]
fn test_parse_u32_saturating_agrees_with_reference(d in "[0-9]{0,22}|[0-9]{0,10}[^0-9][0-9]{0,4}") {
proptest::prop_assert_eq!(parse_u32_saturating(d.as_bytes()), reference_u32(d.as_bytes()));
}
}
#[test]
fn test_parse_u32_saturating_length_boundaries() {
assert_eq!(parse_u32_saturating(b"999999999"), Ok(999_999_999));
assert_eq!(parse_u32_saturating(b"000000000"), Ok(0));
assert_eq!(parse_u32_saturating(b"2147483647"), Ok(2_147_483_647));
assert_eq!(parse_u32_saturating(b"2147483648"), Ok(DELTA_SECONDS_MAX));
assert_eq!(parse_u32_saturating(b"2147483649"), Ok(DELTA_SECONDS_MAX));
assert_eq!(
parse_u32_saturating(b"99999999999999999999"),
Ok(DELTA_SECONDS_MAX)
);
let e = parse_u32_saturating(b"12345678/").unwrap_err();
assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 8));
let e = parse_u32_saturating(b"1234567890:").unwrap_err();
assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidNumber, 10));
}
}