Skip to main content

simoom_qpack/
huffman.rs

1//! Huffman coding (RFC 7541 section 5.2 and Appendix B), shared with HPACK: this is
2//! simoom-hpack's code table, encoder and table-driven decoder, so a build with both HTTP/2
3//! and HTTP/3 carries one copy.
4
5pub use simoom_hpack::huffman::{encode, encoded_len};
6
7/// Decodes a Huffman-encoded string, appending to `out`. Rejects EOS, padding longer
8/// than 7 bits and padding that is not all ones.
9#[allow(clippy::result_unit_err)]
10pub fn decode(data: &[u8], out: &mut Vec<u8>) -> Result<(), ()> {
11    simoom_hpack::huffman::decode(data, out).map_err(|_| ())
12}
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17
18    fn hex(s: &str) -> Vec<u8> {
19        let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
20        (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()).collect()
21    }
22
23    fn dec(d: &[u8]) -> Result<Vec<u8>, ()> {
24        let mut out = Vec::new();
25        decode(d, &mut out).map(|_| out)
26    }
27
28    #[test]
29    fn rfc7541_c4_vectors() {
30        let mut out = Vec::new();
31        encode(b"www.example.com", &mut out);
32        assert_eq!(out, hex("f1e3 c2e5 f23a 6ba0 ab90 f4ff"));
33        assert_eq!(dec(&out).unwrap(), b"www.example.com");
34        assert_eq!(encoded_len(b"www.example.com"), 12);
35        assert_eq!(dec(&hex("a8eb 1064 9cbf")).unwrap(), b"no-cache");
36        assert_eq!(dec(&hex("25a8 49e9 5ba9 7d7f")).unwrap(), b"custom-key");
37        assert_eq!(dec(&hex("25a8 49e9 5bb8 e8b4 bf")).unwrap(), b"custom-value");
38        assert_eq!(dec(&hex("6402")).unwrap(), b"302");
39        assert_eq!(dec(&hex("aec3 771a 4b")).unwrap(), b"private");
40        assert_eq!(
41            dec(&hex("d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff")).unwrap(),
42            b"Mon, 21 Oct 2013 20:13:21 GMT"
43        );
44        assert_eq!(dec(&hex("9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3")).unwrap(), b"https://www.example.com");
45    }
46
47    #[test]
48    fn all_bytes_round_trip() {
49        let data: Vec<u8> = (0..=255u8).chain((0..=255u8).rev()).collect();
50        let mut out = Vec::new();
51        encode(&data, &mut out);
52        assert_eq!(out.len(), encoded_len(&data));
53        assert_eq!(dec(&out).unwrap(), data);
54        assert_eq!(dec(&[]).unwrap(), b"");
55    }
56
57    #[test]
58    fn invalid_padding_and_eos() {
59        assert!(dec(&[0b0001_1000]).is_err());
60        assert_eq!(dec(&[0b0001_1111]).unwrap(), b"a");
61        assert!(dec(&[0b0001_1111, 0xff]).is_err());
62        // EOS (30 ones) followed by padding
63        assert!(dec(&[0xff, 0xff, 0xff, 0xff]).is_err());
64    }
65}