http_auth/table.rs
1// Copyright (C) 2021 Scott Lamb <slamb@slamb.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Builds and offers lookup on a table of byte values to the character
5//! classes the respective bytes are part of. Most classes are referenced from
6//! [RFC 7235 Appendix B: Imported ABNF](https://datatracker.ietf.org/doc/html/rfc7235#appendix-B)
7//! or [RFC 7235 Appendix C: Collected ABNF](https://datatracker.ietf.org/doc/html/rfc7235#appendix-C).
8
9pub(crate) const C_TCHAR: u8 = 1;
10pub(crate) const C_QDTEXT: u8 = 2;
11pub(crate) const C_ESCAPABLE: u8 = 4;
12pub(crate) const C_OWS: u8 = 8;
13pub(crate) const C_ATTR: u8 = 16;
14
15static TABLE: [u8; 128] = build_table();
16
17pub(crate) fn char_classes(b: u8) -> u8 {
18 *TABLE.get(usize::from(b)).unwrap_or(&0)
19}
20
21const fn build_table() -> [u8; 128] {
22 // It'd be nice to use array::from_fn here, but it wasn't stablized until Rust 1.63.
23 let mut table = [0u8; 128];
24 let mut i = 0;
25 while i < 128 {
26 let b = i as u8;
27 let mut classes = 0;
28 if is_tchar(b) {
29 classes |= C_TCHAR;
30 }
31 if is_qdtext(b) {
32 classes |= C_QDTEXT;
33 }
34 if is_escapable(b) {
35 classes |= C_ESCAPABLE;
36 }
37 if is_ows(b) {
38 classes |= C_OWS;
39 }
40 if is_attr(b) {
41 classes |= C_ATTR;
42 }
43 table[i] = classes;
44 i += 1;
45 }
46 table
47}
48
49/// Returns if the byte is a `tchar` as defined in
50/// [RFC 7230 section 3.2.6](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
51const fn is_tchar(b: u8) -> bool {
52 // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
53 // / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
54 // / DIGIT / ALPHA
55 // ; any VCHAR, except delimiters
56 matches!(b,
57 b'!'
58 | b'#'
59 | b'$'
60 | b'%'
61 | b'&'
62 | b'\''
63 | b'*'
64 | b'+'
65 | b'-'
66 | b'.'
67 | b'^'
68 | b'_'
69 | b'`'
70 | b'|'
71 | b'~'
72 | b'0'..=b'9'
73 | b'a'..=b'z'
74 | b'A'..=b'Z')
75}
76
77/// Returns true if the byte is a valid `qdtext` (excluding `obs-text`), as defined in
78/// [RFC 7230 section 3.2.6](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
79///
80/// ```text
81/// quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
82/// qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
83/// obs-text = %x80-FF
84/// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
85/// VCHAR = %x21-7E
86/// ; visible (printing) characters
87/// ```
88const fn is_qdtext(b: u8) -> bool {
89 matches!(b, b'\t' | b' ' | 0x21 | 0x23..=0x5B | 0x5D..=0x7E)
90}
91
92/// Returns true if the byte is a valid end of a `quoted-pair`, as defined in
93/// [RFC 7230 section 3.2.6](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
94const fn is_escapable(b: u8) -> bool {
95 matches!(b, b'\t' | b' ' | 0x21..=0x7E | 0x80..=0xFF)
96}
97
98/// Returns true if the byte is a valid `attr-char` as defined in
99/// [RFC 5987 section 3.2.1](https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1).
100///
101/// ```text
102/// attr-char = ALPHA / DIGIT
103/// / "!" / "#" / "$" / "&" / "+" / "-" / "."
104/// / "^" / "_" / "`" / "|" / "~"
105/// ; token except ( "*" / "'" / "%" )
106/// ```
107const fn is_attr(b: u8) -> bool {
108 matches!(b,
109 b'a'..=b'z'
110 | b'A'..=b'Z'
111 | b'0'..=b'9'
112 | b'!'
113 | b'#'
114 | b'$'
115 | b'&'
116 | b'+'
117 | b'-'
118 | b'.'
119 | b'^'
120 | b'_'
121 | b'`'
122 | b'|'
123 | b'~')
124}
125
126/// Returns true if the byte is valid optional whitespace as in [RFC 7230 section
127/// 3.2.3](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.3).
128///
129/// ```text
130/// OWS = *( SP / HTAB )
131/// ; optional whitespace
132/// ```
133const fn is_ows(b: u8) -> bool {
134 matches!(b, b' ' | b'\t')
135}