mod table;
use self ::table::{DECODE_TABLE, ENCODE_TABLE};
use crate ::hpack::DecoderError;
use bytes::{BufMut, BytesMut};
// Constructed in the generated `table.rs` file
struct Decoder {
state: usize,
maybe_eos: bool,
}
// These flags must match the ones in genhuff.rs
const MAYBE_EOS: u8 = 1 ;
const DECODED: u8 = 2 ;
const ERROR: u8 = 4 ;
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
let mut decoder = Decoder::new();
// Max compression ratio is >= 0.5
buf.reserve(src.len() << 1 );
for b in src {
if let Some(b) = decoder.decode4(b >> 4 )? {
buf.put_u8(b);
}
if let Some(b) = decoder.decode4(b & 0 xf)? {
buf.put_u8(b);
}
}
if !decoder.is_final() {
return Err(DecoderError::InvalidHuffmanCode);
}
Ok(buf.split())
}
pub fn encode(src: &[u8], dst: &mut BytesMut) {
let mut bits: u64 = 0 ;
let mut bits_left = 40 ;
for &b in src {
let (nbits, code) = ENCODE_TABLE[b as usize];
bits |= code << (bits_left - nbits);
bits_left -= nbits;
while bits_left <= 32 {
dst.put_u8((bits >> 32 ) as u8);
bits <<= 8 ;
bits_left += 8 ;
}
}
if bits_left != 40 {
// This writes the EOS token
bits |= (1 << bits_left) - 1 ;
dst.put_u8((bits >> 32 ) as u8);
}
}
impl Decoder {
fn new() -> Decoder {
Decoder {
state: 0 ,
maybe_eos: false ,
}
}
// Decodes 4 bits
fn decode4(&mut self , input: u8) -> Result<Option<u8>, DecoderError> {
// (next-state, byte, flags)
let (next, byte, flags) = DECODE_TABLE[self .state][input as usize];
if flags & ERROR == ERROR {
// Data followed the EOS marker
return Err(DecoderError::InvalidHuffmanCode);
}
let mut ret = None;
if flags & DECODED == DECODED {
ret = Some(byte);
}
self .state = next;
self .maybe_eos = flags & MAYBE_EOS == MAYBE_EOS;
Ok(ret)
}
fn is_final(&self ) -> bool {
self .state == 0 || self .maybe_eos
}
}
#[ cfg(test)]
mod test {
use super ::*;
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
let mut buf = BytesMut::new();
super ::decode(src, &mut buf)
}
#[ test]
fn decode_single_byte() {
assert_eq!("o" , decode(&[0 b00111111]).unwrap());
assert_eq!("0" , decode(&[7 ]).unwrap());
assert_eq!("A" , decode(&[(0 x21 << 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(&[0 b01010011, 0 b11111000]).unwrap());
}
#[ test]
fn encode_single_byte() {
let mut dst = BytesMut::with_capacity(1 );
encode(b"o" , &mut dst);
assert_eq!(&dst[..], &[0 b00111111]);
dst.clear();
encode(b"0" , &mut dst);
assert_eq!(&dst[..], &[7 ]);
dst.clear();
encode(b"A" , &mut dst);
assert_eq!(&dst[..], &[(0 x21 << 2 ) + 3 ]);
}
#[ test]
fn encode_decode_str() {
const DATA: &[&str] = &[
"hello world" ,
":method" ,
":scheme" ,
":authority" ,
"yahoo.co.jp" ,
"GET" ,
"http" ,
":path" ,
"/images/top/sp2/cmn/logo-ns-130528.png" ,
"example.com" ,
"hpack-test" ,
"xxxxxxx1" ,
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0" ,
"accept" ,
"Accept" ,
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"cookie" ,
"B=76j09a189a6h4&b=3&s=0b" ,
"TE" ,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non bibendum libero. \
Etiam ultrices lorem ut.",
];
for s in DATA {
let mut dst = BytesMut::with_capacity(s.len());
encode(s.as_bytes(), &mut dst);
let decoded = decode(&dst).unwrap();
assert_eq!(&decoded[..], s.as_bytes());
}
}
#[ test]
fn encode_decode_u8() {
const DATA: &[&[u8]] = &[b"\0" , b"\0\0\0" , b"\0\x01\x02\x03\x04\x05" , b"\xFF\xF8" ];
for s in DATA {
let mut dst = BytesMut::with_capacity(s.len());
encode(s, &mut dst);
let decoded = decode(&dst).unwrap();
assert_eq!(&decoded[..], &s[..]);
}
}
}
Messung V0.5 in Prozent C=96 H=98 G=96
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-22)
¤
*© Formatika GbR, Deutschland