Skip to main content

parse_net/
scan.rs

1//! Byte search and field-value validation, eight bytes per step.
2//!
3//! The fast paths read the input as little-endian `u64` words and use the
4//! classic "has zero byte" bit trick (SWAR: SIMD within a register), with no
5//! `unsafe`. Each one agrees with the bytewise definition for every byte at
6//! every position; the tests and property tests check exactly that.
7
8use crate::{
9    class::{self, Class},
10    error::{ErrorKind, Result, err},
11};
12
13const LO: u64 = 0x0101_0101_0101_0101;
14const HI: u64 = 0x8080_8080_8080_8080;
15
16/// The high bit of each byte of `x` that is zero is set; bits above the first
17/// zero byte may be set falsely, so only the lowest set marker is exact.
18#[inline]
19const fn zero_markers(x: u64) -> u64 {
20    x.wrapping_sub(LO) & !x & HI
21}
22
23#[inline]
24fn word(chunk: &[u8]) -> u64 {
25    let mut bytes = [0u8; 8];
26    if let Ok(exact) = <[u8; 8]>::try_from(chunk) {
27        bytes = exact;
28    }
29    u64::from_le_bytes(bytes)
30}
31
32#[inline]
33fn first_marker(markers: u64) -> usize {
34    (markers.trailing_zeros() / 8) as usize
35}
36
37/// The index of the first `needle` in `hay`.
38///
39/// Identical to `hay.iter().position(|&b| b == needle)`, eight bytes per
40/// step.
41///
42/// # Examples
43///
44/// ```
45/// use parse_net::scan::find_byte;
46///
47/// assert_eq!(find_byte(b';', b"text/html; charset=utf-8"), Some(9));
48/// assert_eq!(find_byte(b',', b"gzip"), None);
49/// ```
50#[inline]
51#[must_use]
52pub fn find_byte(needle: u8, hay: &[u8]) -> Option<usize> {
53    let pattern = u64::from_le_bytes([needle; 8]);
54    let mut chunks = hay.chunks_exact(8);
55    let mut base = 0usize;
56    for chunk in &mut chunks {
57        let found = zero_markers(word(chunk) ^ pattern);
58        if found != 0 {
59            return Some(base + first_marker(found));
60        }
61        base += 8;
62    }
63    chunks
64        .remainder()
65        .iter()
66        .position(|&b| b == needle)
67        .map(|i| base + i)
68}
69
70/// The index of the first byte in `hay` that equals `a` or `b`.
71///
72/// # Examples
73///
74/// ```
75/// use parse_net::scan::find_either;
76///
77/// assert_eq!(find_either(b',', b'"', b"a=\"x,y\", b"), Some(2));
78/// ```
79#[inline]
80#[must_use]
81pub fn find_either(a: u8, b: u8, hay: &[u8]) -> Option<usize> {
82    let pa = u64::from_le_bytes([a; 8]);
83    let pb = u64::from_le_bytes([b; 8]);
84    let mut chunks = hay.chunks_exact(8);
85    let mut base = 0usize;
86    for chunk in &mut chunks {
87        let w = word(chunk);
88        let found = zero_markers(w ^ pa) | zero_markers(w ^ pb);
89        if found != 0 {
90            // The lowest marker of the union is exact: a false marker in one
91            // half can only sit above a true one in that half.
92            return Some(base + first_marker(found));
93        }
94        base += 8;
95    }
96    chunks
97        .remainder()
98        .iter()
99        .position(|&x| x == a || x == b)
100        .map(|i| base + i)
101}
102
103/// The index of the first byte of `s` outside `class`.
104///
105/// # Examples
106///
107/// ```
108/// use parse_net::{scan::first_not_in, class::Class};
109///
110/// assert_eq!(first_not_in(b"gzip, br", Class::TCHAR), Some(4));
111/// assert_eq!(first_not_in(b"gzip", Class::TCHAR), None);
112/// ```
113#[inline]
114#[must_use]
115pub fn first_not_in(s: &[u8], class: Class) -> Option<usize> {
116    s.iter().position(|&b| !class.contains(b))
117}
118
119/// Validates a field value per RFC 9110 section 5.5: every byte is
120/// `field-vchar`, SP, HTAB, or `obs-text`.
121///
122/// This is the guard every engine runs on every field value. It does not
123/// strip OWS; an empty value is valid.
124///
125/// # Errors
126///
127/// [`ErrorKind::BareCr`] for CR, [`ErrorKind::LineFeed`] for LF,
128/// [`ErrorKind::Nul`] for NUL, and [`ErrorKind::Control`] for any other
129/// control byte or DEL, each at the offending offset.
130///
131/// # Examples
132///
133/// ```
134/// use parse_net::{scan::check_field_value, ErrorKind};
135///
136/// assert!(check_field_value(b"caf\xc3\xa9 \tok").is_ok());
137/// assert_eq!(check_field_value(b"abcdefgh\x00").unwrap_err().kind(), ErrorKind::Nul);
138/// assert_eq!(check_field_value(b"a\nb").unwrap_err().offset(), 1);
139/// ```
140#[inline]
141pub fn check_field_value(value: &[u8]) -> Result<()> {
142    let mut chunks = value.chunks_exact(8);
143    let mut base = 0usize;
144    for chunk in &mut chunks {
145        let w = word(chunk);
146        // A byte is suspicious when it is below 0x20, equal to 0x7F, or has
147        // the high bit set (obs-text is valid, but rare enough to check
148        // bytewise). Suspicious words drop to the exact per-byte check.
149        let low = w.wrapping_sub(0x2020_2020_2020_2020) & !w & HI;
150        let del = zero_markers(w ^ 0x7f7f_7f7f_7f7f_7f7f);
151        if (low | del | (w & HI)) != 0 {
152            check_field_bytes(chunk, base)?;
153        }
154        base += 8;
155    }
156    check_field_bytes(chunks.remainder(), base)
157}
158
159#[inline]
160fn check_field_bytes(bytes: &[u8], base: usize) -> Result<()> {
161    match bytes.iter().position(|&b| !class::is_field_text(b)) {
162        None => Ok(()),
163        Some(i) => err(control_kind(bytes.get(i).copied().unwrap_or(0)), base + i),
164    }
165}
166
167/// Whether `b` is a control byte that no field value may hold: C0 other than
168/// HTAB, or DEL. HTAB is field text, so a misplaced tab is a grammar error,
169/// not a control (D-037).
170#[inline]
171pub(crate) const fn is_control(b: u8) -> bool {
172    (b < 0x20 && b != 0x09) || b == 0x7f
173}
174
175/// The error kind for a byte that is not allowed in a field value.
176#[inline]
177pub(crate) const fn control_kind(b: u8) -> ErrorKind {
178    match b {
179        b'\r' => ErrorKind::BareCr,
180        b'\n' => ErrorKind::LineFeed,
181        0 => ErrorKind::Nul,
182        _ => ErrorKind::Control,
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use std::vec;
189
190    use super::*;
191
192    #[test]
193    fn test_find_byte_matches_position() {
194        let hay: std::vec::Vec<u8> = (0..64u8).collect();
195        for needle in 0..70u8 {
196            assert_eq!(
197                find_byte(needle, &hay),
198                hay.iter().position(|&b| b == needle)
199            );
200        }
201        assert_eq!(find_byte(b'\n', b"\x0b\x0a"), Some(1));
202        assert_eq!(find_byte(1, &[0x81, 0x01]), Some(1));
203        assert_eq!(find_byte(b'a', b""), None);
204    }
205
206    #[test]
207    fn test_find_byte_false_marker_above_true_one() {
208        // 0x01 after 0x00 can raise a false marker; the first match must win.
209        let hay = [2u8, 2, 1, 0, 1, 2, 2, 2, 2];
210        assert_eq!(find_byte(1, &hay), Some(2));
211        assert_eq!(find_byte(0, &hay), Some(3));
212    }
213
214    #[test]
215    fn test_find_either_every_position() {
216        for len in 0..20usize {
217            for pos_a in 0..len {
218                let mut v = vec![b'x'; len];
219                v[pos_a] = b',';
220                for pos_b in 0..len {
221                    let mut w = v.clone();
222                    w[pos_b] = b'"';
223                    let expected = w.iter().position(|&b| b == b',' || b == b'"');
224                    assert_eq!(find_either(b',', b'"', &w), expected);
225                }
226            }
227        }
228    }
229
230    #[test]
231    fn test_field_value_rejects_controls_with_kinds() {
232        assert!(check_field_value(b"").is_ok());
233        assert!(check_field_value(b"text/html; q=0.9\tx").is_ok());
234        assert!(check_field_value(b"caf\xc3\xa9 \x80\xff").is_ok());
235        let e = check_field_value(b"a\rb").unwrap_err();
236        assert_eq!((e.kind(), e.offset()), (ErrorKind::BareCr, 1));
237        let e = check_field_value(b"abcdefgh\x00ijk").unwrap_err();
238        assert_eq!((e.kind(), e.offset()), (ErrorKind::Nul, 8));
239        let e = check_field_value(b"abcdefghijklmno\x7f").unwrap_err();
240        assert_eq!((e.kind(), e.offset()), (ErrorKind::Control, 15));
241        let e = check_field_value(b"abcdefg\nhij").unwrap_err();
242        assert_eq!((e.kind(), e.offset()), (ErrorKind::LineFeed, 7));
243        assert_eq!(
244            check_field_value(b"\x0b").unwrap_err().kind(),
245            ErrorKind::Control
246        );
247        assert_eq!(
248            check_field_value(b"\x0c").unwrap_err().kind(),
249            ErrorKind::Control
250        );
251    }
252
253    #[test]
254    fn test_field_value_fast_path_agrees_with_bytewise() {
255        for b in 0u8..=255 {
256            for pos in 0..17 {
257                let mut v = vec![b'a'; 17];
258                v[pos] = b;
259                let fast = check_field_value(&v);
260                let slow = check_field_bytes(&v, 0);
261                assert_eq!(fast, slow, "byte {b:#04x} at {pos}");
262            }
263        }
264    }
265
266    #[test]
267    fn test_field_value_one_mebibyte() {
268        let v = vec![b'a'; 1 << 20];
269        assert!(check_field_value(&v).is_ok());
270    }
271
272    #[test]
273    fn test_first_not_in_positions() {
274        assert_eq!(first_not_in(b"", Class::TCHAR), None);
275        assert_eq!(first_not_in(b" a", Class::TCHAR), Some(0));
276    }
277}