Skip to main content

sark_core/http/head/
input.rs

1use super::byte::is_ascii_ws;
2use super::error::ERR_INVALID_HEADER_NAME;
3
4pub struct HeaderLineScan {
5    pub end: usize,
6    pub colon: Option<usize>,
7    pub value_start: usize,
8    pub value_end: usize,
9}
10
11pub trait HeadInput {
12    fn len(&self) -> usize;
13    fn is_empty(&self) -> bool {
14        self.len() == 0
15    }
16    fn find_crlf_from(&self, start: usize) -> Option<usize>;
17    fn slice_range(&self, range: std::ops::Range<usize>) -> Option<&[u8]>;
18    fn copy_range_local(
19        &self,
20        range: std::ops::Range<usize>,
21    ) -> Option<crate::http::LocalFrameBytes>;
22    fn copy_range_into(&self, range: std::ops::Range<usize>, out: &mut [u8]);
23    fn for_each_slice<F>(&self, range: std::ops::Range<usize>, f: F)
24    where
25        F: FnMut(&[u8]);
26}
27
28impl HeadInput for [u8] {
29    fn len(&self) -> usize {
30        <[u8]>::len(self)
31    }
32
33    fn find_crlf_from(&self, start: usize) -> Option<usize> {
34        BytesScan::find_crlf_from(self, start)
35    }
36
37    fn slice_range(&self, range: std::ops::Range<usize>) -> Option<&[u8]> {
38        self.get(range)
39    }
40
41    fn copy_range_into(&self, range: std::ops::Range<usize>, out: &mut [u8]) {
42        assert!(
43            range.start <= range.end && range.end <= self.len(),
44            "head input copy range invariant: range out of bounds",
45        );
46        let need = range.end - range.start;
47        assert!(
48            out.len() >= need,
49            "head input copy range invariant: output too small",
50        );
51        out[..need].copy_from_slice(&self[range]);
52    }
53
54    fn copy_range_local(
55        &self,
56        range: std::ops::Range<usize>,
57    ) -> Option<crate::http::LocalFrameBytes> {
58        self.get(range)
59            .map(o3::buffer::Shared::copy_from_slice)
60            .map(crate::http::LocalFrameBytes::from_shared)
61    }
62
63    fn for_each_slice<F>(&self, range: std::ops::Range<usize>, mut f: F)
64    where
65        F: FnMut(&[u8]),
66    {
67        if let Some(slice) = self.get(range) {
68            f(slice);
69        }
70    }
71}
72
73pub struct BytesScan;
74
75impl BytesScan {
76    pub fn find_byte_from(bytes: &[u8], start: usize, needle: u8) -> Option<usize> {
77        if start >= bytes.len() {
78            return None;
79        }
80        memchr::memchr(needle, &bytes[start..]).map(|i| start + i)
81    }
82
83    pub fn find_crlf_from(bytes: &[u8], start: usize) -> Option<usize> {
84        if bytes.len() < 2 || start >= bytes.len().saturating_sub(1) {
85            return None;
86        }
87        let mut seek = start;
88        while let Some(cr_idx) = Self::find_byte_from(bytes, seek, b'\r') {
89            if cr_idx + 1 >= bytes.len() {
90                return None;
91            }
92            if bytes[cr_idx + 1] == b'\n' {
93                return Some(cr_idx);
94            }
95            seek = cr_idx + 1;
96        }
97        None
98    }
99
100    pub fn find_name_end_valid(
101        bytes: &[u8],
102        start: usize,
103    ) -> Result<Option<(usize, u8)>, crate::error::Error> {
104        match sark_simd::scan_header_name(bytes, start) {
105            sark_simd::HeaderNameOutcome::Found { pos, byte } => Ok(Some((pos, byte))),
106            sark_simd::HeaderNameOutcome::Invalid => Err(crate::error::Error::BadRequest(
107                ERR_INVALID_HEADER_NAME.into(),
108            )),
109            sark_simd::HeaderNameOutcome::None => Ok(None),
110        }
111    }
112
113    pub fn find_header_line_from(bytes: &[u8], start: usize) -> Option<HeaderLineScan> {
114        if start >= bytes.len() {
115            return None;
116        }
117
118        let mut search = start;
119        let (colon_pos, value_segment_start) = loop {
120            let rel = memchr::memchr2(b':', b'\r', &bytes[search..])?;
121            let pos = search + rel;
122            if bytes[pos] == b':' {
123                break (pos, pos + 1);
124            }
125            if pos + 1 >= bytes.len() {
126                return None;
127            }
128            if bytes[pos + 1] == b'\n' {
129                return Some(HeaderLineScan {
130                    end: pos,
131                    colon: None,
132                    value_start: 0,
133                    value_end: 0,
134                });
135            }
136            search = pos + 1;
137        };
138
139        let mut search = value_segment_start;
140        let cr = loop {
141            let rel = memchr::memchr(b'\r', &bytes[search..])?;
142            let pos = search + rel;
143            if pos + 1 >= bytes.len() {
144                return None;
145            }
146            if bytes[pos + 1] == b'\n' {
147                break pos;
148            }
149            search = pos + 1;
150        };
151
152        let segment = &bytes[value_segment_start..cr];
153        let leading = segment.iter().take_while(|&&b| is_ascii_ws(b)).count();
154        let trailing = segment[leading..]
155            .iter()
156            .rev()
157            .take_while(|&&b| is_ascii_ws(b))
158            .count();
159        Some(HeaderLineScan {
160            end: cr,
161            colon: Some(colon_pos),
162            value_start: value_segment_start + leading,
163            value_end: cr - trailing,
164        })
165    }
166}