Skip to main content

rama_utils/
byte_set.rs

1//! `const` builders for `[bool; 256]` byte-set lookup tables.
2//!
3//! A lookup table turns a byte-class predicate into a single branchless
4//! load, which beats a `match` / compare-chain on hot per-byte paths (URI
5//! validation, HTML tokenizing, …) and is deterministic across compilers.
6//! Build the table once at `const` time, then index it by `byte as usize`.
7//!
8//! Reach for a table only for *irregular* byte sets on a hot path: a simple
9//! range such as `b < 0x80` is one compare and should stay a comparison.
10
11/// Mark every byte in the half-open range `[lo, hi_exclusive)` as `true`.
12#[must_use]
13pub const fn set_range(mut table: [bool; 256], lo: u8, hi_exclusive: u8) -> [bool; 256] {
14    let mut i = lo;
15    while i < hi_exclusive {
16        table[i as usize] = true;
17        i += 1;
18    }
19    table
20}
21
22/// Mark every byte present in `bytes` as `true`.
23#[must_use]
24pub const fn set_each(mut table: [bool; 256], bytes: &[u8]) -> [bool; 256] {
25    let mut i = 0;
26    while i < bytes.len() {
27        table[bytes[i] as usize] = true;
28        i += 1;
29    }
30    table
31}
32
33/// Mark the ASCII alpha bytes (`A-Z`, `a-z`) as `true`.
34#[must_use]
35pub const fn set_ascii_alpha(table: [bool; 256]) -> [bool; 256] {
36    let table = set_range(table, b'A', b'Z' + 1);
37    set_range(table, b'a', b'z' + 1)
38}
39
40/// Mark the ASCII alphanumeric bytes (`0-9`, `A-Z`, `a-z`) as `true`.
41#[must_use]
42pub const fn set_ascii_alphanum(table: [bool; 256]) -> [bool; 256] {
43    set_range(set_ascii_alpha(table), b'0', b'9' + 1)
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn ranges_and_members() {
52        const T: [bool; 256] = set_each(set_range([false; 256], b'0', b'9' + 1), b"+-");
53        assert!(T[b'0' as usize] && T[b'9' as usize]);
54        assert!(T[b'+' as usize] && T[b'-' as usize]);
55        assert!(!T[b'a' as usize] && !T[b'/' as usize]);
56    }
57
58    #[test]
59    fn ascii_classes() {
60        const ALPHA: [bool; 256] = set_ascii_alpha([false; 256]);
61        const ALNUM: [bool; 256] = set_ascii_alphanum([false; 256]);
62        assert!(ALPHA[b'A' as usize] && ALPHA[b'z' as usize] && !ALPHA[b'0' as usize]);
63        assert!(ALNUM[b'0' as usize] && ALNUM[b'Z' as usize] && !ALNUM[b'_' as usize]);
64    }
65}