Skip to main content

sark_http/field/text/
mod.rs

1//! ASCII field and request-value parsing helpers.
2
3pub mod percent;
4
5pub(crate) const fn hex(byte: u8) -> Option<u8> {
6    match byte {
7        b'0'..=b'9' => Some(byte - b'0'),
8        b'a'..=b'f' => Some(byte - b'a' + 10),
9        b'A'..=b'F' => Some(byte - b'A' + 10),
10        _ => None,
11    }
12}
13
14pub struct Ascii<'a> {
15    bytes: &'a [u8],
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum IntError {
20    Empty,
21    InvalidByte,
22    Overflow,
23}
24
25impl<'a> Ascii<'a> {
26    pub const fn new(bytes: &'a [u8]) -> Self {
27        Self { bytes }
28    }
29
30    pub fn parse_usize(&self) -> Result<usize, IntError> {
31        let (value, end) = self.parse_prefix()?;
32        if end != self.bytes.len() {
33            return Err(IntError::InvalidByte);
34        }
35        Ok(value)
36    }
37
38    pub fn parse_prefix(&self) -> Result<(usize, usize), IntError> {
39        parse(self.bytes, 0usize, |value, digit| {
40            value.checked_mul(10)?.checked_add(usize::from(digit))
41        })
42    }
43
44    pub fn parse_u64(&self) -> Result<u64, IntError> {
45        let (value, end) = parse(self.bytes, 0u64, |value, digit| {
46            value.checked_mul(10)?.checked_add(u64::from(digit))
47        })?;
48        if end != self.bytes.len() {
49            return Err(IntError::InvalidByte);
50        }
51        Ok(value)
52    }
53}
54
55fn parse<T>(
56    bytes: &[u8],
57    zero: T,
58    mut push: impl FnMut(T, u8) -> Option<T>,
59) -> Result<(T, usize), IntError> {
60    if bytes.is_empty() {
61        return Err(IntError::Empty);
62    }
63
64    let mut value = zero;
65    let mut end = 0usize;
66    for &byte in bytes {
67        if !byte.is_ascii_digit() {
68            break;
69        }
70        value = push(value, byte - b'0').ok_or(IntError::Overflow)?;
71        end += 1;
72    }
73    if end == 0 {
74        return Err(IntError::InvalidByte);
75    }
76    Ok((value, end))
77}