/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 | | } |