Skip to main content

sark_http/field/text/percent/
decoder.rs

1use core::{mem, ops, ptr};
2
3use crate::field::text::percent::{octet, scan, utf8};
4
5#[derive(Debug)]
6pub enum Error {
7    Bounds {
8        capacity: usize,
9        source: ops::Range<usize>,
10        destination: usize,
11    },
12}
13
14pub struct Decoder<'a> {
15    bytes: &'a mut [u8],
16    read: usize,
17    start: usize,
18    written: usize,
19}
20
21impl<'a> Decoder<'a> {
22    pub fn new(bytes: &'a mut [u8], src: ops::Range<usize>, dst: usize) -> Result<Self, Error> {
23        let capacity = bytes.len();
24        let Some(read) = src.start.checked_sub(dst).filter(|_| src.start <= src.end) else {
25            return Err(Error::Bounds {
26                capacity,
27                source: src,
28                destination: dst,
29            });
30        };
31        let Some(bytes) = bytes.get_mut(dst..src.end) else {
32            return Err(Error::Bounds {
33                capacity,
34                source: src,
35                destination: dst,
36            });
37        };
38        Ok(Self {
39            bytes,
40            read,
41            start: dst,
42            written: 0,
43        })
44    }
45
46    /// Decodes within the admitted window without expanding the input.
47    /// Each token consumes at least as many bytes as UTF-8 repair emits.
48    pub fn decode(mut self) -> Written {
49        let mut utf8 = utf8::Utf8::new();
50        while self.read < self.bytes.len() {
51            let byte = self.bytes[self.read];
52            let rest = &self.bytes[self.read + 1..];
53            let literal = if byte == b'%' || !byte.is_ascii() {
54                0
55            } else {
56                1 + scan::Scan::new(rest).literal()
57            };
58            if literal != 0 && utf8.is_ready() {
59                self.copy(literal);
60                continue;
61            }
62            let (byte, consumed) = if literal != 0 || byte != b'%' {
63                (byte, 1)
64            } else {
65                rest.first()
66                    .copied()
67                    .zip(rest.get(1).copied())
68                    .and_then(|(high, low)| octet::decode(high, low))
69                    .map_or((byte, 1), |byte| (byte, 3))
70            };
71            utf8.push(self.bytes, &mut self.written, byte);
72            self.read += consumed;
73        }
74        utf8.finish(self.bytes, &mut self.written);
75        Written::new(self.start..self.start + self.written)
76    }
77
78    fn copy(&mut self, len: usize) {
79        // SAFETY: the literal scan bounds len by this owner's unread suffix.
80        // Emission never overtakes read, and this run starts with no pending
81        // UTF-8 bytes. Both ranges stay in the admitted window; copy permits overlap.
82        unsafe {
83            let bytes = self.bytes.as_mut_ptr();
84            ptr::copy(bytes.add(self.read), bytes.add(self.written), len);
85        }
86        self.read += len;
87        self.written += len;
88    }
89}
90
91const _: () = assert!(
92    mem::size_of::<Decoder<'_>>() == mem::size_of::<&mut [u8]>() + 3 * mem::size_of::<usize>()
93);
94
95/// An exact range of UTF-8 emitted by the repairing decoder.
96/// Only admitted decoding constructs this proof; the owner must preserve its bytes.
97pub struct Written {
98    range: ops::Range<usize>,
99}
100
101impl Written {
102    const fn new(range: ops::Range<usize>) -> Self {
103        Self { range }
104    }
105
106    pub fn range(self) -> ops::Range<usize> {
107        self.range
108    }
109}
110
111const _: () = assert!(size_of::<Written>() == 2 * size_of::<usize>());