use super::super::error::H2ParseError;
const HUFFMAN_TABLE: [(u32, u8); 257] = [
(0x00001ff8, 13), (0x007fffd8, 23), (0x0fffffe2, 28), (0x0fffffe3, 28), (0x0fffffe4, 28), (0x0fffffe5, 28), (0x0fffffe6, 28), (0x0fffffe7, 28), (0x0fffffe8, 28), (0x00ffffea, 24), (0x3ffffffc, 30), (0x0fffffe9, 28), (0x0fffffea, 28), (0x3ffffffd, 30), (0x0fffffeb, 28), (0x0fffffec, 28), (0x0fffffed, 28), (0x0fffffee, 28), (0x0fffffef, 28), (0x0ffffff0, 28), (0x0ffffff1, 28), (0x0ffffff2, 28), (0x3ffffffe, 30), (0x0ffffff3, 28), (0x0ffffff4, 28), (0x0ffffff5, 28), (0x0ffffff6, 28), (0x0ffffff7, 28), (0x0ffffff8, 28), (0x0ffffff9, 28), (0x0ffffffa, 28), (0x0ffffffb, 28), (0x00000014, 6), (0x000003f8, 10), (0x000003f9, 10), (0x00000ffa, 12), (0x00001ff9, 13), (0x00000015, 6), (0x000000f8, 8), (0x000007fa, 11), (0x000003fa, 10), (0x000003fb, 10), (0x000000f9, 8), (0x000007fb, 11), (0x000000fa, 8), (0x00000016, 6), (0x00000017, 6), (0x00000018, 6), (0x00000000, 5), (0x00000001, 5), (0x00000002, 5), (0x00000019, 6), (0x0000001a, 6), (0x0000001b, 6), (0x0000001c, 6), (0x0000001d, 6), (0x0000001e, 6), (0x0000001f, 6), (0x0000005c, 7), (0x000000fb, 8), (0x00007ffc, 15), (0x00000020, 6), (0x00000ffb, 12), (0x000003fc, 10), (0x00001ffa, 13), (0x00000021, 6), (0x0000005d, 7), (0x0000005e, 7), (0x0000005f, 7), (0x00000060, 7), (0x00000061, 7), (0x00000062, 7), (0x00000063, 7), (0x00000064, 7), (0x00000065, 7), (0x00000066, 7), (0x00000067, 7), (0x00000068, 7), (0x00000069, 7), (0x0000006a, 7), (0x0000006b, 7), (0x0000006c, 7), (0x0000006d, 7), (0x0000006e, 7), (0x0000006f, 7), (0x00000070, 7), (0x00000071, 7), (0x00000072, 7), (0x000000fc, 8), (0x00000073, 7), (0x000000fd, 8), (0x00001ffb, 13), (0x0007fff0, 19), (0x00001ffc, 13), (0x00003ffc, 14), (0x00000022, 6), (0x00007ffd, 15), (0x00000003, 5), (0x00000023, 6), (0x00000004, 5), (0x00000024, 6), (0x00000005, 5), (0x00000025, 6), (0x00000026, 6), (0x00000027, 6), (0x00000006, 5), (0x00000074, 7), (0x00000075, 7), (0x00000028, 6), (0x00000029, 6), (0x0000002a, 6), (0x00000007, 5), (0x0000002b, 6), (0x00000076, 7), (0x0000002c, 6), (0x00000008, 5), (0x00000009, 5), (0x0000002d, 6), (0x00000077, 7), (0x00000078, 7), (0x00000079, 7), (0x0000007a, 7), (0x0000007b, 7), (0x00007ffe, 15), (0x000007fc, 11), (0x00003ffd, 14), (0x00001ffd, 13), (0x0ffffffc, 28), (0x000fffe6, 20), (0x003fffd2, 22), (0x000fffe7, 20), (0x000fffe8, 20), (0x003fffd3, 22), (0x003fffd4, 22), (0x003fffd5, 22), (0x007fffd9, 23), (0x003fffd6, 22), (0x007fffda, 23), (0x007fffdb, 23), (0x007fffdc, 23), (0x007fffdd, 23), (0x007fffde, 23), (0x00ffffeb, 24), (0x007fffdf, 23), (0x00ffffec, 24), (0x00ffffed, 24), (0x003fffd7, 22), (0x007fffe0, 23), (0x00ffffee, 24), (0x007fffe1, 23), (0x007fffe2, 23), (0x007fffe3, 23), (0x007fffe4, 23), (0x001fffdc, 21), (0x003fffd8, 22), (0x007fffe5, 23), (0x003fffd9, 22), (0x007fffe6, 23), (0x007fffe7, 23), (0x00ffffef, 24), (0x003fffda, 22), (0x001fffdd, 21), (0x000fffe9, 20), (0x003fffdb, 22), (0x003fffdc, 22), (0x007fffe8, 23), (0x007fffe9, 23), (0x001fffde, 21), (0x007fffea, 23), (0x003fffdd, 22), (0x003fffde, 22), (0x00fffff0, 24), (0x001fffdf, 21), (0x003fffdf, 22), (0x007fffeb, 23), (0x007fffec, 23), (0x001fffe0, 21), (0x001fffe1, 21), (0x003fffe0, 22), (0x001fffe2, 21), (0x007fffed, 23), (0x003fffe1, 22), (0x007fffee, 23), (0x007fffef, 23), (0x000fffea, 20), (0x003fffe2, 22), (0x003fffe3, 22), (0x003fffe4, 22), (0x007ffff0, 23), (0x003fffe5, 22), (0x003fffe6, 22), (0x007ffff1, 23), (0x03ffffe0, 26), (0x03ffffe1, 26), (0x000fffeb, 20), (0x0007fff1, 19), (0x003fffe7, 22), (0x007ffff2, 23), (0x003fffe8, 22), (0x01ffffec, 25), (0x03ffffe2, 26), (0x03ffffe3, 26), (0x03ffffe4, 26), (0x07ffffde, 27), (0x07ffffdf, 27), (0x03ffffe5, 26), (0x00fffff1, 24), (0x01ffffed, 25), (0x0007fff2, 19), (0x001fffe3, 21), (0x03ffffe6, 26), (0x07ffffe0, 27), (0x07ffffe1, 27), (0x03ffffe7, 26), (0x07ffffe2, 27), (0x00fffff2, 24), (0x001fffe4, 21), (0x001fffe5, 21), (0x03ffffe8, 26), (0x03ffffe9, 26), (0x0ffffffd, 28), (0x07ffffe3, 27), (0x07ffffe4, 27), (0x07ffffe5, 27), (0x000fffec, 20), (0x00fffff3, 24), (0x000fffed, 20), (0x001fffe6, 21), (0x003fffe9, 22), (0x001fffe7, 21), (0x001fffe8, 21), (0x007ffff3, 23), (0x003fffea, 22), (0x003fffeb, 22), (0x01ffffee, 25), (0x01ffffef, 25), (0x00fffff4, 24), (0x00fffff5, 24), (0x03ffffea, 26), (0x007ffff4, 23), (0x03ffffeb, 26), (0x07ffffe6, 27), (0x03ffffec, 26), (0x03ffffed, 26), (0x07ffffe7, 27), (0x07ffffe8, 27), (0x07ffffe9, 27), (0x07ffffea, 27), (0x07ffffeb, 27), (0x0ffffffe, 28), (0x07ffffec, 27), (0x07ffffed, 27), (0x07ffffee, 27), (0x07ffffef, 27), (0x07fffff0, 27), (0x03ffffee, 26), (0x3fffffff, 30), ];
const EOS_SYMBOL: usize = 256;
pub const MAX_DECODED_LEN: usize = 8 * 1024;
pub fn decode(input: &[u8]) -> Result<String, H2ParseError> {
let mut out: Vec<u8> = Vec::new();
let mut accum: u32 = 0;
let mut bits: u32 = 0;
for &byte in input {
if bits > 24 {
return Err(H2ParseError::HuffmanInvalid);
}
accum |= (byte as u32) << (24 - bits);
bits += 8;
loop {
if bits == 0 {
break;
}
let mut matched: Option<(usize, u8)> = None;
for len in 5..=30u8 {
if (len as u32) > bits {
break;
}
let candidate = accum >> (32 - len as u32);
for (sym, (code, code_len)) in HUFFMAN_TABLE.iter().enumerate() {
if *code_len == len && *code == candidate {
matched = Some((sym, len));
break;
}
}
if matched.is_some() {
break;
}
}
let (sym, len) = match matched {
Some(m) => m,
None => break, };
if sym == EOS_SYMBOL {
return Err(H2ParseError::HuffmanInvalid);
}
accum <<= len;
bits -= len as u32;
out.push(sym as u8);
if out.len() > MAX_DECODED_LEN {
return Err(H2ParseError::HuffmanOversized {
max: MAX_DECODED_LEN,
});
}
}
}
if bits > 7 {
return Err(H2ParseError::HuffmanInvalid);
}
if bits > 0 {
let mask = ((1u32 << bits) - 1) << (32 - bits);
if accum & mask != mask {
return Err(H2ParseError::HuffmanInvalid);
}
}
String::from_utf8(out).map_err(|_| H2ParseError::NonAsciiAuthority)
}
#[cfg(test)]
pub fn encode(s: &str) -> Vec<u8> {
let mut out: Vec<u8> = Vec::new();
let mut accum: u64 = 0;
let mut bits: u32 = 0;
for &b in s.as_bytes() {
let (code, len) = HUFFMAN_TABLE[b as usize];
accum = (accum << len) | (code as u64);
bits += len as u32;
while bits >= 8 {
bits -= 8;
let byte = ((accum >> bits) & 0xFF) as u8;
out.push(byte);
}
}
if bits > 0 {
let pad = 8 - bits;
accum = (accum << pad) | ((1u64 << pad) - 1);
out.push((accum & 0xFF) as u8);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decodes_empty_string() {
assert_eq!(decode(&[]).unwrap(), "");
}
#[test]
fn decodes_short_authority() {
let encoded = encode("a.com");
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, "a.com");
}
#[test]
fn decodes_lowercase_authority_value() {
let encoded = encode("api.example.com");
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, "api.example.com");
}
#[test]
fn decodes_authority_with_dot_and_dash() {
let encoded = encode("my-service.example.com");
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, "my-service.example.com");
}
#[test]
fn decodes_long_authority() {
let s = "very-long-subdomain-of-example.com";
let encoded = encode(s);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, s);
}
#[test]
fn decodes_authority_starting_with_digit() {
let encoded = encode("3rd-party.example.com");
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, "3rd-party.example.com");
}
#[test]
fn decodes_punycode_a_label() {
let encoded = encode("xn--nxasmq6b");
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, "xn--nxasmq6b");
}
#[test]
fn decodes_authority_with_port_then_round_trip() {
let s = "api.example.com:8443";
let encoded = encode(s);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, s);
}
#[test]
fn rejects_eos_symbol_in_stream() {
let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
let err = decode(&bytes).unwrap_err();
assert_eq!(err, H2ParseError::HuffmanInvalid);
}
#[test]
fn rejects_oversized_decoded_output() {
let s: String = std::iter::repeat_n('a', MAX_DECODED_LEN + 1).collect();
let encoded = encode(&s);
let err = decode(&encoded).unwrap_err();
assert!(matches!(err, H2ParseError::HuffmanOversized { .. }));
}
#[test]
fn rejects_too_long_padding() {
let mut bytes = encode("a"); bytes.push(0xFF);
let err = decode(&bytes).unwrap_err();
assert_eq!(err, H2ParseError::HuffmanInvalid);
}
#[test]
fn rejects_non_eos_padding() {
let mut bytes = encode("a");
let last = bytes.len() - 1;
bytes[last] &= 0xFE; let err = decode(&bytes).unwrap_err();
assert_eq!(err, H2ParseError::HuffmanInvalid);
}
}