#![cfg(feature = "deflate")]
use compcol::deflate::{Decoder, Encoder};
use compcol::{Decoder as _, Encoder as _, Error};
fn hex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
fn decode_chunked(encoded: &[u8], in_chunk: usize, out_chunk: usize) -> Result<Vec<u8>, Error> {
let mut dec = Decoder::new();
let mut out = Vec::new();
let mut buf = vec![0u8; out_chunk.max(1)];
let mut i = 0;
while i < encoded.len() {
let end = (i + in_chunk).min(encoded.len());
let chunk = &encoded[i..end];
let mut consumed_in_chunk = 0;
loop {
let p = dec.decode(&chunk[consumed_in_chunk..], &mut buf)?;
out.extend_from_slice(&buf[..p.written]);
consumed_in_chunk += p.consumed;
if p.consumed == 0 && p.written == 0 {
break;
}
}
i = end;
}
loop {
let p = dec.finish(&mut buf)?;
out.extend_from_slice(&buf[..p.written]);
if p.done {
break;
}
if p.written == 0 {
panic!("decoder finish stalled");
}
}
Ok(out)
}
#[test]
fn decode_handcrafted_stored_block() {
let stream = [0x01, 0x05, 0x00, 0xFA, 0xFF, b'h', b'e', b'l', b'l', b'o'];
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
assert_eq!(decoded, b"hello");
}
#[test]
fn decode_two_stored_blocks() {
let stream = [
0x00, 0x03, 0x00, 0xFC, 0xFF, b'a', b'b', b'c', 0x01, 0x02, 0x00, 0xFD, 0xFF, b'd', b'e',
];
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
assert_eq!(decoded, b"abcde");
}
#[test]
fn decode_stored_block_streaming_one_byte_at_a_time() {
let stream = [0x01, 0x05, 0x00, 0xFA, 0xFF, b'h', b'e', b'l', b'l', b'o'];
let decoded = decode_chunked(&stream, 1, 1).unwrap();
assert_eq!(decoded, b"hello");
}
#[test]
fn decode_fixed_huffman_hello() {
let stream = hex("cb48cdc9c90700");
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
assert_eq!(decoded, b"hello");
}
#[test]
fn decode_fixed_huffman_long_run() {
let stream = hex("63601805c40200");
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
assert_eq!(decoded.len(), 300);
assert!(decoded.iter().all(|&b| b == 0));
}
#[test]
fn decode_fixed_huffman_two_runs() {
let stream = hex("73741cd9c069840300");
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
let mut expected = vec![b'A'; 256];
expected.extend(vec![b'B'; 256]);
assert_eq!(decoded, expected);
}
#[test]
fn decode_lorem_fixed_huffman() {
let stream =
hex("f3c92f4acd55c82c282ecd5548c9cfc92f5228ce2c5148cc4d2dd151f019951b951b95a3a91c00");
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
let expected = b"Lorem ipsum dolor sit amet, ".repeat(32);
assert_eq!(decoded, expected);
}
#[test]
fn decode_dynamic_huffman_quick_brown_fox() {
let stream = hex(
"edca470180301045412b5f016a628092d0d910084d3d88e0f8ce33aef35a735f8faa929d8b825d1af21c37d9e193f68fa7f2b9d5585bc891c96432994c2693c96432994c2693ffc82f",
);
let decoded = decode_chunked(&stream, 1024, 1024).unwrap();
let expected = b"The quick brown fox jumps over the lazy dog. ".repeat(100);
assert_eq!(decoded, expected);
}
#[test]
fn decode_dynamic_huffman_streaming_one_byte() {
let stream = hex(
"edca470180301045412b5f016a628092d0d910084d3d88e0f8ce33aef35a735f8faa929d8b825d1af21c37d9e193f68fa7f2b9d5585bc891c96432994c2693c96432994c2693ffc82f",
);
let decoded = decode_chunked(&stream, 1, 1).unwrap();
let expected = b"The quick brown fox jumps over the lazy dog. ".repeat(100);
assert_eq!(decoded, expected);
}
fn encode_chunked(input: &[u8], in_chunk: usize, out_chunk: usize) -> Vec<u8> {
let mut enc = Encoder::new();
let mut out = Vec::new();
let mut buf = vec![0u8; out_chunk.max(1)];
let mut i = 0;
while i < input.len() {
let end = (i + in_chunk).min(input.len());
let chunk = &input[i..end];
let mut consumed = 0;
loop {
let p = enc.encode(&chunk[consumed..], &mut buf).unwrap();
out.extend_from_slice(&buf[..p.written]);
consumed += p.consumed;
if p.consumed == 0 && p.written == 0 {
break;
}
}
i = end;
}
loop {
let p = enc.finish(&mut buf).unwrap();
out.extend_from_slice(&buf[..p.written]);
if p.done {
break;
}
if p.written == 0 {
panic!("encoder finish stalled");
}
}
out
}
fn round_trip(input: &[u8]) {
let encoded = encode_chunked(input, 4096, 4096);
let decoded = decode_chunked(&encoded, 4096, 4096).unwrap();
assert_eq!(
decoded,
input,
"round-trip mismatch (input len {})",
input.len()
);
}
#[test]
fn round_trip_empty() {
round_trip(b"");
}
#[test]
fn round_trip_single_byte() {
round_trip(b"x");
}
#[test]
fn round_trip_short_text() {
round_trip(b"The quick brown fox jumps over the lazy dog");
}
#[test]
fn round_trip_repeated_string() {
let input = b"abcabcabcabcabcabcabcabcabc";
round_trip(input);
}
#[test]
fn round_trip_long_zeros() {
let input = vec![0u8; 4096];
let encoded = encode_chunked(&input, 4096, 4096);
assert!(
encoded.len() < input.len() / 10,
"zeros didn't compress: {} -> {}",
input.len(),
encoded.len()
);
let decoded = decode_chunked(&encoded, 4096, 4096).unwrap();
assert_eq!(decoded, input);
}
#[test]
fn round_trip_lorem_ipsum() {
let input = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ".repeat(20);
let encoded = encode_chunked(&input, 4096, 4096);
assert!(
encoded.len() < input.len() / 2,
"text didn't compress: {} -> {}",
input.len(),
encoded.len()
);
let decoded = decode_chunked(&encoded, 4096, 4096).unwrap();
assert_eq!(decoded, input);
}
#[test]
fn round_trip_large_input() {
let input = b"The quick brown fox jumps over the lazy dog. ".repeat(2000); round_trip(&input);
}
#[test]
fn round_trip_pseudo_random() {
let mut state: u32 = 0xDEADBEEF;
let input: Vec<u8> = (0..2048)
.map(|_| {
state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
(state >> 16) as u8
})
.collect();
round_trip(&input);
}
#[test]
fn round_trip_streaming_one_byte() {
let input = b"Hello, world! ".repeat(50);
let encoded = encode_chunked(&input, 1, 1);
let decoded = decode_chunked(&encoded, 1, 1).unwrap();
assert_eq!(decoded, input);
}
#[test]
fn encoder_output_decompresses_with_reference() {
let input = b"Hello hello hello hello world world world world!";
let encoded = encode_chunked(input, 4096, 4096);
let decoded = decode_chunked(&encoded, 4096, 4096).unwrap();
assert_eq!(decoded, input);
}