Skip to main content

simoom_hpack/
huffman.rs

1//! HPACK Huffman coding (RFC 7541 ยง5.2 and Appendix B).
2//!
3//! Decoding walks a finite-state machine four bits at a time: the states are the 256 internal
4//! nodes of the code tree, and each (state, nibble) pair maps to the next state, at most one
5//! emitted symbol (the shortest code is 5 bits) and whether the bits since the last symbol are
6//! valid padding. The 16 KiB table is built once on first use rather than stored in the binary.
7
8use std::sync::OnceLock;
9
10use crate::Error;
11
12/// RFC 7541 Appendix B: (code, bit length) for symbols 0..=256 (256 = EOS).
13const CODES: [(u32, u8); 257] = [
14    (0x1ff8, 13), (0x7fffd8, 23), (0xfffffe2, 28), (0xfffffe3, 28),
15    (0xfffffe4, 28), (0xfffffe5, 28), (0xfffffe6, 28), (0xfffffe7, 28),
16    (0xfffffe8, 28), (0xffffea, 24), (0x3ffffffc, 30), (0xfffffe9, 28),
17    (0xfffffea, 28), (0x3ffffffd, 30), (0xfffffeb, 28), (0xfffffec, 28),
18    (0xfffffed, 28), (0xfffffee, 28), (0xfffffef, 28), (0xffffff0, 28),
19    (0xffffff1, 28), (0xffffff2, 28), (0x3ffffffe, 30), (0xffffff3, 28),
20    (0xffffff4, 28), (0xffffff5, 28), (0xffffff6, 28), (0xffffff7, 28),
21    (0xffffff8, 28), (0xffffff9, 28), (0xffffffa, 28), (0xffffffb, 28),
22    (0x14, 6), (0x3f8, 10), (0x3f9, 10), (0xffa, 12),
23    (0x1ff9, 13), (0x15, 6), (0xf8, 8), (0x7fa, 11),
24    (0x3fa, 10), (0x3fb, 10), (0xf9, 8), (0x7fb, 11),
25    (0xfa, 8), (0x16, 6), (0x17, 6), (0x18, 6),
26    (0x0, 5), (0x1, 5), (0x2, 5), (0x19, 6),
27    (0x1a, 6), (0x1b, 6), (0x1c, 6), (0x1d, 6),
28    (0x1e, 6), (0x1f, 6), (0x5c, 7), (0xfb, 8),
29    (0x7ffc, 15), (0x20, 6), (0xffb, 12), (0x3fc, 10),
30    (0x1ffa, 13), (0x21, 6), (0x5d, 7), (0x5e, 7),
31    (0x5f, 7), (0x60, 7), (0x61, 7), (0x62, 7),
32    (0x63, 7), (0x64, 7), (0x65, 7), (0x66, 7),
33    (0x67, 7), (0x68, 7), (0x69, 7), (0x6a, 7),
34    (0x6b, 7), (0x6c, 7), (0x6d, 7), (0x6e, 7),
35    (0x6f, 7), (0x70, 7), (0x71, 7), (0x72, 7),
36    (0xfc, 8), (0x73, 7), (0xfd, 8), (0x1ffb, 13),
37    (0x7fff0, 19), (0x1ffc, 13), (0x3ffc, 14), (0x22, 6),
38    (0x7ffd, 15), (0x3, 5), (0x23, 6), (0x4, 5),
39    (0x24, 6), (0x5, 5), (0x25, 6), (0x26, 6),
40    (0x27, 6), (0x6, 5), (0x74, 7), (0x75, 7),
41    (0x28, 6), (0x29, 6), (0x2a, 6), (0x7, 5),
42    (0x2b, 6), (0x76, 7), (0x2c, 6), (0x8, 5),
43    (0x9, 5), (0x2d, 6), (0x77, 7), (0x78, 7),
44    (0x79, 7), (0x7a, 7), (0x7b, 7), (0x7ffe, 15),
45    (0x7fc, 11), (0x3ffd, 14), (0x1ffd, 13), (0xffffffc, 28),
46    (0xfffe6, 20), (0x3fffd2, 22), (0xfffe7, 20), (0xfffe8, 20),
47    (0x3fffd3, 22), (0x3fffd4, 22), (0x3fffd5, 22), (0x7fffd9, 23),
48    (0x3fffd6, 22), (0x7fffda, 23), (0x7fffdb, 23), (0x7fffdc, 23),
49    (0x7fffdd, 23), (0x7fffde, 23), (0xffffeb, 24), (0x7fffdf, 23),
50    (0xffffec, 24), (0xffffed, 24), (0x3fffd7, 22), (0x7fffe0, 23),
51    (0xffffee, 24), (0x7fffe1, 23), (0x7fffe2, 23), (0x7fffe3, 23),
52    (0x7fffe4, 23), (0x1fffdc, 21), (0x3fffd8, 22), (0x7fffe5, 23),
53    (0x3fffd9, 22), (0x7fffe6, 23), (0x7fffe7, 23), (0xffffef, 24),
54    (0x3fffda, 22), (0x1fffdd, 21), (0xfffe9, 20), (0x3fffdb, 22),
55    (0x3fffdc, 22), (0x7fffe8, 23), (0x7fffe9, 23), (0x1fffde, 21),
56    (0x7fffea, 23), (0x3fffdd, 22), (0x3fffde, 22), (0xfffff0, 24),
57    (0x1fffdf, 21), (0x3fffdf, 22), (0x7fffeb, 23), (0x7fffec, 23),
58    (0x1fffe0, 21), (0x1fffe1, 21), (0x3fffe0, 22), (0x1fffe2, 21),
59    (0x7fffed, 23), (0x3fffe1, 22), (0x7fffee, 23), (0x7fffef, 23),
60    (0xfffea, 20), (0x3fffe2, 22), (0x3fffe3, 22), (0x3fffe4, 22),
61    (0x7ffff0, 23), (0x3fffe5, 22), (0x3fffe6, 22), (0x7ffff1, 23),
62    (0x3ffffe0, 26), (0x3ffffe1, 26), (0xfffeb, 20), (0x7fff1, 19),
63    (0x3fffe7, 22), (0x7ffff2, 23), (0x3fffe8, 22), (0x1ffffec, 25),
64    (0x3ffffe2, 26), (0x3ffffe3, 26), (0x3ffffe4, 26), (0x7ffffde, 27),
65    (0x7ffffdf, 27), (0x3ffffe5, 26), (0xfffff1, 24), (0x1ffffed, 25),
66    (0x7fff2, 19), (0x1fffe3, 21), (0x3ffffe6, 26), (0x7ffffe0, 27),
67    (0x7ffffe1, 27), (0x3ffffe7, 26), (0x7ffffe2, 27), (0xfffff2, 24),
68    (0x1fffe4, 21), (0x1fffe5, 21), (0x3ffffe8, 26), (0x3ffffe9, 26),
69    (0xffffffd, 28), (0x7ffffe3, 27), (0x7ffffe4, 27), (0x7ffffe5, 27),
70    (0xfffec, 20), (0xfffff3, 24), (0xfffed, 20), (0x1fffe6, 21),
71    (0x3fffe9, 22), (0x1fffe7, 21), (0x1fffe8, 21), (0x7ffff3, 23),
72    (0x3fffea, 22), (0x3fffeb, 22), (0x1ffffee, 25), (0x1ffffef, 25),
73    (0xfffff4, 24), (0xfffff5, 24), (0x3ffffea, 26), (0x7ffff4, 23),
74    (0x3ffffeb, 26), (0x7ffffe6, 27), (0x3ffffec, 26), (0x3ffffed, 26),
75    (0x7ffffe7, 27), (0x7ffffe8, 27), (0x7ffffe9, 27), (0x7ffffea, 27),
76    (0x7ffffeb, 27), (0xffffffe, 28), (0x7ffffec, 27), (0x7ffffed, 27),
77    (0x7ffffee, 27), (0x7ffffef, 27), (0x7fffff0, 27), (0x3ffffee, 26),
78    (0x3fffffff, 30),
79];
80
81/// Number of bytes `data` occupies once Huffman encoded.
82pub fn encoded_len(data: &[u8]) -> usize {
83    let bits: usize = data.iter().map(|&b| CODES[b as usize].1 as usize).sum();
84    bits.div_ceil(8)
85}
86
87/// Huffman-encodes `data`, appending to `out` (padded with the EOS prefix).
88pub fn encode(data: &[u8], out: &mut Vec<u8>) {
89    out.reserve(data.len());
90    let mut acc: u64 = 0;
91    let mut nbits: u32 = 0;
92    for &b in data {
93        let (code, len) = CODES[b as usize];
94        acc = (acc << len) | code as u64;
95        nbits += len as u32;
96        while nbits >= 8 {
97            nbits -= 8;
98            out.push((acc >> nbits) as u8);
99        }
100    }
101    if nbits > 0 {
102        let pad = 8 - nbits;
103        out.push(((acc << pad) | ((1u64 << pad) - 1)) as u8);
104    }
105}
106
107const EMIT: u32 = 1 << 16;
108const FAIL: u32 = 1 << 17;
109const ACCEPT: u32 = 1 << 18;
110
111/// Entry `state * 16 + nibble`: bits 0-7 next state, 8-15 symbol, then EMIT, FAIL, ACCEPT.
112fn fsm() -> &'static [u32; 4096] {
113    static FSM: OnceLock<Box<[u32; 4096]>> = OnceLock::new();
114    FSM.get_or_init(build_fsm)
115}
116
117fn build_fsm() -> Box<[u32; 4096]> {
118    // Code tree: children of internal node i; 0x8000 | symbol marks a leaf, 0 an empty slot.
119    let mut tree: Vec<[u16; 2]> = vec![[0, 0]];
120    // per internal node: depth, and whether the path from the root is all one bits
121    let mut depth: Vec<u8> = vec![0];
122    let mut ones: Vec<bool> = vec![true];
123    for (sym, &(code, len)) in CODES.iter().enumerate() {
124        let mut node = 0usize;
125        for i in (0..len).rev() {
126            let bit = ((code >> i) & 1) as usize;
127            if i == 0 {
128                tree[node][bit] = 0x8000 | sym as u16;
129            } else if tree[node][bit] == 0 {
130                tree.push([0, 0]);
131                depth.push(depth[node] + 1);
132                ones.push(ones[node] && bit == 1);
133                let idx = tree.len() - 1;
134                tree[node][bit] = idx as u16;
135                node = idx;
136            } else {
137                node = tree[node][bit] as usize;
138            }
139        }
140    }
141    debug_assert_eq!(tree.len(), 256);
142    let mut t = Box::new([0u32; 4096]);
143    for state in 0..tree.len() {
144        for nib in 0..16usize {
145            let mut node = state;
146            let mut entry = 0u32;
147            for i in (0..4).rev() {
148                let c = tree[node][(nib >> i) & 1];
149                if c & 0x8000 != 0 {
150                    let sym = (c & 0x1ff) as u32;
151                    if sym == 256 {
152                        entry |= FAIL;
153                        break;
154                    }
155                    entry |= EMIT | (sym << 8);
156                    node = 0;
157                } else {
158                    node = c as usize;
159                }
160            }
161            if node == 0 || (ones[node] && depth[node] <= 7) {
162                entry |= ACCEPT;
163            }
164            t[state * 16 + nib] = entry | node as u32;
165        }
166    }
167    t
168}
169
170/// Decodes a Huffman-encoded string, appending to `out`.
171pub fn decode(data: &[u8], out: &mut Vec<u8>) -> Result<(), Error> {
172    let t = fsm();
173    // at most 8/5 symbols per byte
174    out.reserve(data.len() * 8 / 5 + 1);
175    let mut e = ACCEPT; // state 0, nothing pending
176    for &byte in data {
177        // the masks keep the index in range, so there are no bounds checks
178        e = t[(((e & 0xff) << 4) | (byte >> 4) as u32) as usize & 0xfff];
179        if e & (EMIT | FAIL) != 0 {
180            if e & FAIL != 0 {
181                return Err(Error::Huffman);
182            }
183            out.push((e >> 8) as u8);
184        }
185        e = t[(((e & 0xff) << 4) | (byte & 0xf) as u32) as usize & 0xfff];
186        if e & (EMIT | FAIL) != 0 {
187            if e & FAIL != 0 {
188                return Err(Error::Huffman);
189            }
190            out.push((e >> 8) as u8);
191        }
192    }
193    if e & ACCEPT != 0 {
194        Ok(())
195    } else {
196        Err(Error::Huffman)
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    fn hex(s: &str) -> Vec<u8> {
205        let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
206        (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()).collect()
207    }
208
209    fn dec(b: &[u8]) -> Result<Vec<u8>, Error> {
210        let mut out = Vec::new();
211        decode(b, &mut out).map(|_| out)
212    }
213
214    #[test]
215    fn rfc_examples() {
216        let mut out = Vec::new();
217        encode(b"www.example.com", &mut out);
218        assert_eq!(out, hex("f1e3 c2e5 f23a 6ba0 ab90 f4ff"));
219        assert_eq!(dec(&out).unwrap(), b"www.example.com");
220        assert_eq!(encoded_len(b"www.example.com"), 12);
221        assert_eq!(dec(&hex("a8eb 1064 9cbf")).unwrap(), b"no-cache");
222        assert_eq!(dec(&hex("25a8 49e9 5ba9 7d7f")).unwrap(), b"custom-key");
223        assert_eq!(dec(&hex("25a8 49e9 5bb8 e8b4 bf")).unwrap(), b"custom-value");
224        assert_eq!(dec(&hex("6402")).unwrap(), b"302");
225        assert_eq!(dec(&hex("aec3 771a 4b")).unwrap(), b"private");
226        assert_eq!(dec(&hex("d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff")).unwrap(), b"Mon, 21 Oct 2013 20:13:21 GMT");
227        assert_eq!(dec(&hex("9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3")).unwrap(), b"https://www.example.com");
228    }
229
230    #[test]
231    fn all_bytes_round_trip() {
232        let data: Vec<u8> = (0..=255u8).chain((0..=255u8).rev()).collect();
233        let mut out = Vec::new();
234        encode(&data, &mut out);
235        assert_eq!(out.len(), encoded_len(&data));
236        assert_eq!(dec(&out).unwrap(), data);
237        for n in 0..64usize {
238            let s: Vec<u8> = (0..n).map(|i| (i * 37 + n) as u8).collect();
239            let mut o = Vec::new();
240            encode(&s, &mut o);
241            assert_eq!(dec(&o).unwrap(), s);
242        }
243        assert_eq!(dec(&[]).unwrap(), b"");
244    }
245
246    #[test]
247    fn invalid_padding_and_eos() {
248        // 'a' = 00011; padding must be ones
249        assert!(dec(&[0b0001_1000]).is_err());
250        assert_eq!(dec(&[0b0001_1111]).unwrap(), b"a");
251        // a full byte of ones is more than 7 bits of padding
252        assert!(dec(&[0b0001_1111, 0xff]).is_err());
253        assert!(dec(&[0xff]).is_err());
254        // EOS (30 ones) inside the string
255        assert!(dec(&[0xff, 0xff, 0xff, 0xff]).is_err());
256        // ends inside a code that is not an EOS prefix
257        assert!(dec(&[0xfe]).is_err());
258    }
259}