Coverage Report

Created: 2025-11-24 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2/src/hpack/huffman/mod.rs
Line
Count
Source
1
mod table;
2
3
use self::table::{DECODE_TABLE, ENCODE_TABLE};
4
use crate::hpack::DecoderError;
5
6
use bytes::{BufMut, BytesMut};
7
8
// Constructed in the generated `table.rs` file
9
struct Decoder {
10
    state: usize,
11
    maybe_eos: bool,
12
}
13
14
// These flags must match the ones in genhuff.rs
15
16
const MAYBE_EOS: u8 = 1;
17
const DECODED: u8 = 2;
18
const ERROR: u8 = 4;
19
20
2.78M
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
21
2.78M
    let mut decoder = Decoder::new();
22
23
    // Max compression ratio is >= 0.5
24
2.78M
    buf.reserve(src.len() << 1);
25
26
56.3M
    for b in src {
27
53.5M
        if let Some(b) = decoder.decode4(b >> 4)? {
28
33.3M
            buf.put_u8(b);
29
33.3M
        }
30
31
53.5M
        if let Some(b) = decoder.decode4(b & 0xf)? {
32
41.8M
            buf.put_u8(b);
33
41.8M
        }
34
    }
35
36
2.78M
    if !decoder.is_final() {
37
691
        return Err(DecoderError::InvalidHuffmanCode);
38
2.78M
    }
39
40
2.78M
    Ok(buf.split())
41
2.78M
}
42
43
4.90k
pub fn encode(src: &[u8], dst: &mut BytesMut) {
44
4.90k
    let mut bits: u64 = 0;
45
4.90k
    let mut bits_left = 40;
46
47
21.8M
    for &b in src {
48
21.8M
        let (nbits, code) = ENCODE_TABLE[b as usize];
49
50
21.8M
        bits |= code << (bits_left - nbits);
51
21.8M
        bits_left -= nbits;
52
53
57.7M
        while bits_left <= 32 {
54
35.9M
            dst.put_u8((bits >> 32) as u8);
55
35.9M
56
35.9M
            bits <<= 8;
57
35.9M
            bits_left += 8;
58
35.9M
        }
59
    }
60
61
4.90k
    if bits_left != 40 {
62
4.84k
        // This writes the EOS token
63
4.84k
        bits |= (1 << bits_left) - 1;
64
4.84k
        dst.put_u8((bits >> 32) as u8);
65
4.84k
    }
66
4.90k
}
67
68
impl Decoder {
69
2.78M
    fn new() -> Decoder {
70
2.78M
        Decoder {
71
2.78M
            state: 0,
72
2.78M
            maybe_eos: false,
73
2.78M
        }
74
2.78M
    }
75
76
    // Decodes 4 bits
77
107M
    fn decode4(&mut self, input: u8) -> Result<Option<u8>, DecoderError> {
78
        // (next-state, byte, flags)
79
107M
        let (next, byte, flags) = DECODE_TABLE[self.state][input as usize];
80
81
107M
        if flags & ERROR == ERROR {
82
            // Data followed the EOS marker
83
119
            return Err(DecoderError::InvalidHuffmanCode);
84
107M
        }
85
86
107M
        let mut ret = None;
87
88
107M
        if flags & DECODED == DECODED {
89
75.1M
            ret = Some(byte);
90
75.1M
        }
91
92
107M
        self.state = next;
93
107M
        self.maybe_eos = flags & MAYBE_EOS == MAYBE_EOS;
94
95
107M
        Ok(ret)
96
107M
    }
97
98
2.78M
    fn is_final(&self) -> bool {
99
2.78M
        self.state == 0 || self.maybe_eos
100
2.78M
    }
101
}
102
103
#[cfg(test)]
104
mod test {
105
    use super::*;
106
107
    fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
108
        let mut buf = BytesMut::new();
109
        super::decode(src, &mut buf)
110
    }
111
112
    #[test]
113
    fn decode_single_byte() {
114
        assert_eq!("o", decode(&[0b00111111]).unwrap());
115
        assert_eq!("0", decode(&[7]).unwrap());
116
        assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap());
117
    }
118
119
    #[test]
120
    fn single_char_multi_byte() {
121
        assert_eq!("#", decode(&[255, 160 + 15]).unwrap());
122
        assert_eq!("$", decode(&[255, 200 + 7]).unwrap());
123
        assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap());
124
    }
125
126
    #[test]
127
    fn multi_char() {
128
        assert_eq!("!0", decode(&[254, 1]).unwrap());
129
        assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
130
    }
131
132
    #[test]
133
    fn encode_single_byte() {
134
        let mut dst = BytesMut::with_capacity(1);
135
136
        encode(b"o", &mut dst);
137
        assert_eq!(&dst[..], &[0b00111111]);
138
139
        dst.clear();
140
        encode(b"0", &mut dst);
141
        assert_eq!(&dst[..], &[7]);
142
143
        dst.clear();
144
        encode(b"A", &mut dst);
145
        assert_eq!(&dst[..], &[(0x21 << 2) + 3]);
146
    }
147
148
    #[test]
149
    fn encode_decode_str() {
150
        const DATA: &[&str] = &[
151
            "hello world",
152
            ":method",
153
            ":scheme",
154
            ":authority",
155
            "yahoo.co.jp",
156
            "GET",
157
            "http",
158
            ":path",
159
            "/images/top/sp2/cmn/logo-ns-130528.png",
160
            "example.com",
161
            "hpack-test",
162
            "xxxxxxx1",
163
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
164
            "accept",
165
            "Accept",
166
            "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
167
            "cookie",
168
            "B=76j09a189a6h4&b=3&s=0b",
169
            "TE",
170
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non bibendum libero. \
171
             Etiam ultrices lorem ut.",
172
        ];
173
174
        for s in DATA {
175
            let mut dst = BytesMut::with_capacity(s.len());
176
177
            encode(s.as_bytes(), &mut dst);
178
179
            let decoded = decode(&dst).unwrap();
180
181
            assert_eq!(&decoded[..], s.as_bytes());
182
        }
183
    }
184
185
    #[test]
186
    fn encode_decode_u8() {
187
        const DATA: &[&[u8]] = &[b"\0", b"\0\0\0", b"\0\x01\x02\x03\x04\x05", b"\xFF\xF8"];
188
189
        for s in DATA {
190
            let mut dst = BytesMut::with_capacity(s.len());
191
192
            encode(s, &mut dst);
193
194
            let decoded = decode(&dst).unwrap();
195
196
            assert_eq!(&decoded[..], &s[..]);
197
        }
198
    }
199
}