use crate::hpack::huffman::table::{DECODE_TABLE, ENCODE_CODES, ENCODE_CODE_LENGTHS};
use crate::hpack::DecoderError;
use bytes::BytesMut;
const BRANCH: u16 = 0x8000;
const TABLE_INDEX_MASK: u16 = 0x7f00;
const TABLE_WIDTH: usize = 256;
const FAST_BITS: usize = 12;
const FAST_DECODE_TABLE: [u32; 1 << FAST_BITS] = build_fast_table();
const fn build_fast_table() -> [u32; 1 << FAST_BITS] {
let mut table = [0; 1 << FAST_BITS];
let mut a = 0;
while a < 256 {
let len1 = ENCODE_CODE_LENGTHS[a] as usize;
let code1 = ENCODE_CODES[a] as u64;
if len1 <= FAST_BITS {
let rem = FAST_BITS - len1;
let base = (code1 as usize) << rem;
let single = (1 << 16) | ((len1 as u32) << 24) | a as u32;
let mut i = 0;
while i < (1 << rem) {
table[base + i] = single;
i += 1;
}
}
a += 1;
}
let mut a = 0;
while a < 256 {
let len1 = ENCODE_CODE_LENGTHS[a] as usize;
let code1 = ENCODE_CODES[a] as u64;
if len1 + 5 <= FAST_BITS {
let rem = FAST_BITS - len1;
let mut b = 0;
while b < 256 {
let len2 = ENCODE_CODE_LENGTHS[b] as usize;
let code2 = ENCODE_CODES[b] as u64;
if len2 <= rem {
let rem2 = rem - len2;
let base = ((code1 as usize) << rem) | ((code2 as usize) << rem2);
let pair =
(2 << 16) | (((len1 + len2) as u32) << 24) | ((b as u32) << 8) | a as u32;
let mut i = 0;
while i < (1 << rem2) {
table[base + i] = pair;
i += 1;
}
}
b += 1;
}
}
a += 1;
}
table
}
#[inline(always)]
fn decode_code_slow(
acc: &mut u64,
bits: &mut usize,
dst: &mut [u8],
o: &mut usize,
) -> Result<(), DecoderError> {
let mut table = 0;
loop {
let e = DECODE_TABLE[table * TABLE_WIDTH + (*acc >> 56) as usize];
if e & BRANCH == 0 {
let used = (e >> 8) as usize;
if used > *bits {
return Err(DecoderError::InvalidHuffmanCode);
}
dst[*o] = e as u8;
*o += 1;
*acc <<= used;
*bits -= used;
return Ok(());
}
if *bits < 8 {
return Err(DecoderError::InvalidHuffmanCode);
}
table = ((e & TABLE_INDEX_MASK) >> 8) as usize;
if table == 0 {
return Err(DecoderError::InvalidHuffmanCode);
}
*acc <<= 8;
*bits -= 8;
}
}
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
let len = src.len();
let base_len = buf.len();
buf.resize(base_len + len * 8 / 5 + 1, 0);
let dst = &mut buf[base_len..];
let mut o = 0;
let mut acc: u64 = 0;
let mut bits: usize = 0;
let mut pos: usize = 0;
'outer: loop {
if pos + 8 <= len {
let word = u64::from_be_bytes(src[pos..pos + 8].try_into().unwrap());
acc |= word >> bits;
pos += (63 - bits) >> 3; bits |= 56; } else {
while bits <= 56 && pos < len {
acc |= (src[pos] as u64) << (56 - bits);
pos += 1;
bits += 8;
}
}
while bits >= FAST_BITS {
let entry = FAST_DECODE_TABLE[(acc >> (64 - FAST_BITS)) as usize];
let count = (entry >> 16) & 0xff;
if count == 0 {
if bits < 30 && pos < len {
continue 'outer;
}
decode_code_slow(&mut acc, &mut bits, dst, &mut o)?;
continue;
}
let consumed = (entry >> 24) as usize;
dst[o] = entry as u8;
dst[o + 1] = (entry >> 8) as u8;
o += count as usize;
acc <<= consumed;
bits -= consumed;
}
if pos >= len {
break;
}
}
while bits > 0 {
if bits < 8 && (acc >> (64 - bits)) == (1 << bits) - 1 {
break;
}
decode_code_slow(&mut acc, &mut bits, dst, &mut o)?;
}
buf.truncate(base_len + o);
Ok(buf.split())
}
#[cfg(test)]
mod test {
use super::*;
use bytes::BufMut;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
let mut buf = BytesMut::new();
super::decode(src, &mut buf)
}
fn reference_decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
let codes: Vec<String> = ENCODE_CODES
.iter()
.zip(ENCODE_CODE_LENGTHS.iter())
.map(|(&code, &len)| format!("{code:0len$b}", len = len as usize))
.collect();
let bits: String = src.iter().map(|byte| format!("{byte:08b}")).collect();
let mut decoded = BytesMut::new();
let mut remaining_bits: &str = &bits;
while let Some(sym) = (0..256).find(|&sym| remaining_bits.starts_with(&codes[sym])) {
decoded.put_u8(sym as u8);
remaining_bits = &remaining_bits[codes[sym].len()..];
}
if remaining_bits.len() >= 8 || remaining_bits.chars().any(|bit| bit == '0') {
return Err(DecoderError::InvalidHuffmanCode);
}
Ok(decoded)
}
#[test]
fn decode_single_byte() {
assert_eq!("o", decode(&[0b00111111]).unwrap());
assert_eq!("0", decode(&[7]).unwrap());
assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap());
}
#[test]
fn single_char_multi_byte() {
assert_eq!("#", decode(&[255, 160 + 15]).unwrap());
assert_eq!("$", decode(&[255, 200 + 7]).unwrap());
assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap());
}
#[test]
fn multi_char() {
assert_eq!("!0", decode(&[254, 1]).unwrap());
assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
}
#[test]
fn matches_reference_on_arbitrary_bytes() {
let mut rng = StdRng::seed_from_u64(0xdeadbeefcafe);
for _ in 0..10_000 {
let len = rng.gen_range(0..40);
let src: Vec<u8> = (0..len).map(|_| rng.gen()).collect();
assert_eq!(reference_decode(&src), decode(&src), "src={:?}", src);
}
for _ in 0..10_000 {
let len = rng.gen_range(0..40);
let src: Vec<u8> = (0..len).map(|_| rng.gen::<u8>() | 0xe0).collect();
assert_eq!(reference_decode(&src), decode(&src), "src={:?}", src);
}
}
#[test]
fn rejects_eos_and_invalid_padding() {
assert_eq!(decode(&[0xff]), Err(DecoderError::InvalidHuffmanCode));
assert_eq!(
decode(&[0xff, 0xff, 0xff, 0xff]),
Err(DecoderError::InvalidHuffmanCode)
);
assert_eq!(decode(&[0]), Err(DecoderError::InvalidHuffmanCode));
}
}