// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0 > or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT >, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate ::{
huffman_decode_helper::{huffman_decoder_root, HuffmanDecoderNode},
huffman_table::HUFFMAN_TABLE,
Error, Res,
};
struct BitReader<
'a> {
input: &
'a [u8],
offset: usize,
current_bit: u8,
}
impl <
'a> BitReader<' a> {
pub const fn new(input: &
'a [u8]) -> Self {
BitReader {
input,
offset:
0 ,
current_bit:
8 ,
}
}
pub fn read_bit(&
mut self ) -> Res<u8> {
if self .input.len() ==
self .offset {
return Err(Error::NeedMoreData);
}
if self .current_bit ==
0 {
self .offset +=
1 ;
if self .offset ==
self .input.len() {
return Err(Error::NeedMoreData);
}
self .current_bit =
8 ;
}
self .current_bit -=
1 ;
Ok((
self .input[
self .offset] >>
self .current_bit) &
0 x01)
}
pub fn verify_ending(&
mut self , i: u8) -> Res<()> {
if (i +
self .current_bit) >
7 {
return Err(Error::HuffmanDecompressionFailed);
}
if self .input.is_empty() {
Ok(())
}
else if self .offset !=
self .input.len() {
Err(Error::HuffmanDecompressionFailed)
}
else if self .input[
self .input.len() -
1 ] & ((
0 x1 << (i +
self .current_bit)) -
1 )
== ((
0 x1 << (i +
self .current_bit)) -
1 )
{
self .current_bit =
0 ;
Ok(())
}
else {
Err(Error::HuffmanDecompressionFailed)
}
}
pub const fn has_more_data(&
self ) -> bool {
!
self .input.is_empty() && (
self .offset !=
self .input.len() || (
self .current_bit !=
0 ))
}
}
/// Decodes huffman encoded input.
///
/// # Errors
///
/// This function may return `HuffmanDecompressionFailed` if `input` is not a correct
/// huffman-encoded array of bits.
///
/// # Panics
///
/// Never, but rust can't know that.
pub fn decode_huffman(input: &[u8]) -> Res<Vec<u8>> {
let mut reader = BitReader::new(input);
let mut output = Vec::new();
while reader.has_more_data() {
if let Some(c) = decode_character(&
mut reader)? {
if c ==
256 {
return Err(Error::HuffmanDecompressionFailed);
}
output.push(u8::try_from(c).unwrap());
}
}
Ok(output)
}
fn decode_character(reader: &
mut BitReader) -> Res<Option<u16>> {
let mut node: &HuffmanDecoderNode = huffman_decoder_root();
let mut i =
0 ;
while node.value.is_none() {
match reader.read_bit() {
Err(_) => {
reader.verify_ending(i)?;
return Ok(None);
}
Ok(b) => {
i +=
1 ;
if let Some(next) = &node.next[usize::from(b)] {
node = next;
}
else {
reader.verify_ending(i)?;
return Ok(None);
}
}
}
}
debug_assert!(node.value.is_some());
Ok(node.value)
}
/// # Panics
///
/// Never, but rust doesn't know that.
#[ must_use]
pub fn encode_huffman(input: &[u8]) -> Vec<u8> {
let mut output: Vec<u8> = Vec::new();
let mut left: u8 =
8 ;
let mut saved: u8 =
0 ;
for c
in input {
let mut e = HUFFMAN_TABLE[*c
as usize];
// Fill the previous byte
if e.len < left {
let b = u8::try_from(e.val &
0 xFF).unwrap();
saved |= b << (left - e.len);
left -= e.len;
e.len =
0 ;
}
else {
let v: u8 = u8::try_from(e.val >> (e.len - left)).unwrap();
saved |= v;
output.push(saved);
e.len -= left;
left =
8 ;
saved =
0 ;
}
// Write full bytes
while e.len >=
8 {
let v: u8 = u8::try_from((e.val >> (e.len -
8 )) &
0 xFF).unwrap();
output.push(v);
e.len -=
8 ;
}
// Write the rest into saved.
if e.len >
0 {
saved = u8::try_from(e.val & ((
1 << e.len) -
1 )).unwrap() << (
8 - e.len);
left =
8 - e.len;
}
}
if left <
8 {
let v: u8 = (
1 << left) -
1 ;
saved |= v;
output.push(saved);
}
output
}
#[ cfg(test)]
mod tests {
use super ::{decode_huffman, encode_huffman, Error};
struct TestElement {
pub val: &
'static [u8],
pub res: &
'static [u8],
}
const TEST_CASES: &[TestElement] = &[
TestElement {
val: b
"www.example.com" ,
res: &[
0 xf1,
0 xe3,
0 xc2,
0 xe5,
0 xf2,
0 x3a,
0 x6b,
0 xa0,
0 xab,
0 x90,
0 xf4,
0 xff,
],
},
TestElement {
val: b
"no-cache" ,
res: &[
0 xa8,
0 xeb,
0 x10,
0 x64,
0 x9c,
0 xbf],
},
TestElement {
val: b
"custom-key" ,
res: &[
0 x25,
0 xa8,
0 x49,
0 xe9,
0 x5b,
0 xa9,
0 x7d,
0 x7f],
},
TestElement {
val: b
"custom-value" ,
res: &[
0 x25,
0 xa8,
0 x49,
0 xe9,
0 x5b,
0 xb8,
0 xe8,
0 xb4,
0 xbf],
},
TestElement {
val: b
"private" ,
res: &[
0 xae,
0 xc3,
0 x77,
0 x1a,
0 x4b],
},
TestElement {
val: b
"Mon, 21 Oct 2013 20:13:21 GMT" ,
res: &[
0 xd0,
0 x7a,
0 xbe,
0 x94,
0 x10,
0 x54,
0 xd4,
0 x44,
0 xa8,
0 x20,
0 x05,
0 x95,
0 x04,
0 x0b,
0 x81,
0 x66,
0 xe0,
0 x82,
0 xa6,
0 x2d,
0 x1b,
0 xff,
],
},
TestElement {
val: b
"https://www.example.com ",
res: &[
0 x9d,
0 x29,
0 xad,
0 x17,
0 x18,
0 x63,
0 xc7,
0 x8f,
0 x0b,
0 x97,
0 xc8,
0 xe9,
0 xae,
0 x82,
0 xae,
0 x43,
0 xd3,
],
},
TestElement {
val: b
"Mon, 21 Oct 2013 20:13:22 GMT" ,
res: &[
0 xd0,
0 x7a,
0 xbe,
0 x94,
0 x10,
0 x54,
0 xd4,
0 x44,
0 xa8,
0 x20,
0 x05,
0 x95,
0 x04,
0 x0b,
0 x81,
0 x66,
0 xe0,
0 x84,
0 xa6,
0 x2d,
0 x1b,
0 xff,
],
},
TestElement {
val: b
"gzip" ,
res: &[
0 x9b,
0 xd9,
0 xab],
},
TestElement {
val: b
"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" ,
res: &[
0 x94,
0 xe7,
0 x82,
0 x1d,
0 xd7,
0 xf2,
0 xe6,
0 xc7,
0 xb3,
0 x35,
0 xdf,
0 xdf,
0 xcd,
0 x5b,
0 x39,
0 x60,
0 xd5,
0 xaf,
0 x27,
0 x08,
0 x7f,
0 x36,
0 x72,
0 xc1,
0 xab,
0 x27,
0 x0f,
0 xb5,
0 x29,
0 x1f,
0 x95,
0 x87,
0 x31,
0 x60,
0 x65,
0 xc0,
0 x03,
0 xed,
0 x4e,
0 xe5,
0 xb1,
0 x06,
0 x3d,
0 x50,
0 x07,
],
},
TestElement {
val: b
"<?\\ >" ,
res: &[
0 xff,
0 xf9,
0 xfe,
0 x7f,
0 xff,
0 x05,
0 x3f,
0 xef],
},
];
const WRONG_END: &[u8] = &[
0 xa8,
0 xeb,
0 x10,
0 x64,
0 x9c,
0 xaf];
#[ test]
fn encoder() {
for e
in TEST_CASES {
let out = encode_huffman(e.val);
assert_eq!(out[..], *e.res);
}
}
#[ test]
fn decoder() {
for e
in TEST_CASES {
let res = decode_huffman(e.res);
assert!(res.is_ok());
assert_eq!(res.unwrap()[..], *e.val);
}
}
#[ test]
fn decoder_error_wrong_ending() {
assert_eq!(
decode_huffman(WRONG_END),
Err(Error::HuffmanDecompressionFailed)
);
}
}
Messung V0.5 in Prozent C=77 H=86 G=81
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland