Skip to content

Commit 59d985a

Browse files
committed
perf: batch HPACK encoder writes per u32
1 parent 1adb037 commit 59d985a

1 file changed

Lines changed: 75 additions & 12 deletions

File tree

src/hpack/huffman/mod.rs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,39 @@ pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError>
7272
}
7373

7474
pub fn encode(src: &[u8], dst: &mut BytesMut) {
75-
let mut bits: u64 = 0;
76-
let mut bits_left = 40;
75+
let mut bits = 0u64;
76+
let mut bits_len = 0;
7777

7878
for &b in src {
7979
let (nbits, code) = ENCODE_TABLE[b as usize];
8080

81-
bits |= code << (bits_left - nbits);
82-
bits_left -= nbits;
81+
bits = (bits << nbits) | code;
82+
bits_len += nbits;
8383

84-
while bits_left <= 32 {
85-
dst.put_u8((bits >> 32) as u8);
84+
if bits_len >= 32 {
85+
let remaining = bits_len - 32;
86+
dst.put_u32((bits >> remaining) as u32);
8687

87-
bits <<= 8;
88-
bits_left += 8;
88+
bits_len = remaining;
89+
if remaining == 0 {
90+
bits = 0;
91+
} else {
92+
bits &= (1 << remaining) - 1;
93+
}
8994
}
9095
}
9196

92-
if bits_left != 40 {
93-
// This writes the EOS token
94-
bits |= (1 << bits_left) - 1;
95-
dst.put_u8((bits >> 32) as u8);
97+
if bits_len != 0 {
98+
// Pad the final byte with the EOS prefix (all ones).
99+
let padding = 8 - (bits_len % 8);
100+
let padding = if padding == 8 { 0 } else { padding };
101+
bits = (bits << padding) | ((1 << padding) - 1);
102+
bits_len += padding;
103+
104+
while bits_len != 0 {
105+
bits_len -= 8;
106+
dst.put_u8((bits >> bits_len) as u8);
107+
}
96108
}
97109
}
98110

@@ -141,6 +153,18 @@ mod test {
141153
assert_eq!(&dst[..], &[(0x21 << 2) + 3]);
142154
}
143155

156+
#[test]
157+
fn encode_rfc_example() {
158+
let mut dst = BytesMut::new();
159+
160+
encode(b"www.example.com", &mut dst);
161+
162+
assert_eq!(
163+
&dst[..],
164+
&[0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff]
165+
);
166+
}
167+
144168
#[test]
145169
fn encode_decode_str() {
146170
const DATA: &[&str] = &[
@@ -233,6 +257,45 @@ mod bench {
233257
});
234258
}
235259
260+
fn encode_input(b: &mut Bencher, input: &[u8]) {
261+
let mut encoded = BytesMut::new();
262+
encode(input, &mut encoded);
263+
264+
b.bytes = input.len() as u64;
265+
b.iter(|| {
266+
encoded.clear();
267+
encode(black_box(input), &mut encoded);
268+
black_box(encoded.as_ref());
269+
});
270+
}
271+
272+
#[bench]
273+
fn encode_short_ascii(b: &mut Bencher) {
274+
encode_input(b, b"www.example.com");
275+
}
276+
277+
#[bench]
278+
fn encode_header_value(b: &mut Bencher) {
279+
encode_input(
280+
b,
281+
b"text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8",
282+
);
283+
}
284+
285+
#[bench]
286+
fn encode_long_ascii(b: &mut Bencher) {
287+
encode_input(
288+
b,
289+
b"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
290+
);
291+
}
292+
293+
#[bench]
294+
fn encode_all_octets(b: &mut Bencher) {
295+
let input: Vec<_> = (0..=u8::MAX).collect();
296+
encode_input(b, &input);
297+
}
298+
236299
#[bench]
237300
fn decode_short_ascii(b: &mut Bencher) {
238301
decode_input(b, b"www.example.com");

0 commit comments

Comments
 (0)