use crate::deflate::Deflater;
use crate::inflate::Inflater;
use crate::zlib::{Adler32, zlib_decompress};
use oxiarc_core::{BitReader, Crc32};
use std::io::{self, Cursor, Read, Write};
const DEFAULT_BLOCK_SIZE: usize = 128 * 1024;
const GZIP_ID1: u8 = 0x1f;
const GZIP_ID2: u8 = 0x8b;
const GZIP_CM_DEFLATE: u8 = 8;
const GZIP_FLG_NONE: u8 = 0;
const GZIP_OS_UNKNOWN: u8 = 255;
pub struct GzipStreamEncoder<W: Write> {
inner: Option<W>,
buffer: Vec<u8>,
deflater: Deflater,
crc: Crc32,
total_in: u64,
header_written: bool,
finished: bool,
block_size: usize,
}
impl<W: Write> GzipStreamEncoder<W> {
pub fn new(writer: W, level: u8) -> Self {
Self {
inner: Some(writer),
buffer: Vec::new(),
deflater: Deflater::new(level.min(9)),
crc: Crc32::new(),
total_in: 0,
header_written: false,
finished: false,
block_size: DEFAULT_BLOCK_SIZE,
}
}
pub fn with_block_size(mut self, block_size: usize) -> Self {
self.block_size = block_size.max(1);
self
}
fn ensure_header(&mut self) -> io::Result<()> {
if self.header_written {
return Ok(());
}
let header = [
GZIP_ID1,
GZIP_ID2,
GZIP_CM_DEFLATE,
GZIP_FLG_NONE,
0,
0,
0,
0, 0, GZIP_OS_UNKNOWN,
];
if let Some(ref mut w) = self.inner {
w.write_all(&header)?;
}
self.header_written = true;
Ok(())
}
pub fn sync_flush(&mut self) -> io::Result<()> {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.crc.update(&data);
self.total_in += data.len() as u64;
let mut compressed = Vec::new();
self.deflater
.deflate_sync(&data, &mut compressed)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
Ok(())
}
pub fn full_flush(&mut self) -> io::Result<()> {
self.sync_flush()?;
self.deflater.reset_lz77();
Ok(())
}
pub fn partial_flush(&mut self) -> io::Result<()> {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.crc.update(&data);
self.total_in += data.len() as u64;
let mut compressed = Vec::new();
self.deflater
.deflate_partial(&data, &mut compressed)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
Ok(())
}
pub fn finish(mut self) -> io::Result<W> {
if !self.finished {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.crc.update(&data);
self.total_in += data.len() as u64;
let mut compressed = Vec::new();
self.deflater
.deflate(&data, &mut compressed, true)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
let crc_val = self.crc.clone().finalize();
let isize_val = (self.total_in & 0xFFFF_FFFF) as u32;
if let Some(ref mut w) = self.inner {
w.write_all(&crc_val.to_le_bytes())?;
w.write_all(&isize_val.to_le_bytes())?;
}
self.finished = true;
}
self.inner
.take()
.ok_or_else(|| io::Error::other("inner writer already taken"))
}
fn maybe_flush_block(&mut self) -> io::Result<()> {
if self.buffer.len() >= self.block_size {
self.sync_flush()?;
}
Ok(())
}
pub fn buffered_bytes(&self) -> usize {
self.buffer.len()
}
pub fn is_finished(&self) -> bool {
self.finished
}
}
impl<W: Write> Write for GzipStreamEncoder<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.finished {
return Err(io::Error::other("encoder already finished"));
}
self.buffer.extend_from_slice(buf);
self.maybe_flush_block()?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
if !self.buffer.is_empty() {
self.sync_flush()?;
}
if let Some(ref mut w) = self.inner {
w.flush()?;
}
Ok(())
}
}
pub struct GzipStreamDecoder<R: Read> {
inner: R,
output_buffer: Vec<u8>,
output_pos: usize,
finished: bool,
}
impl<R: Read> GzipStreamDecoder<R> {
pub fn new(reader: R) -> Self {
Self {
inner: reader,
output_buffer: Vec::new(),
output_pos: 0,
finished: false,
}
}
pub fn into_inner(self) -> R {
self.inner
}
fn fill_buffer(&mut self) -> io::Result<()> {
if self.finished || self.output_pos < self.output_buffer.len() {
return Ok(());
}
let mut compressed = Vec::new();
self.inner.read_to_end(&mut compressed)?;
if compressed.is_empty() {
self.finished = true;
return Ok(());
}
let cursor = Cursor::new(compressed);
let mut bit_reader = BitReader::new(cursor);
let mut all_decompressed = Vec::new();
loop {
let mut magic = [0u8; 2];
match bit_reader.read_bytes(&mut magic) {
Ok(()) => {}
Err(oxiarc_core::error::OxiArcError::Io(ref e))
if e.kind() == io::ErrorKind::UnexpectedEof =>
{
break;
}
Err(oxiarc_core::error::OxiArcError::UnexpectedEof { .. }) => break,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())),
}
if magic[0] != GZIP_ID1 || magic[1] != GZIP_ID2 {
break;
}
let mut header_rest = [0u8; 8];
bit_reader.read_bytes(&mut header_rest).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip header truncated: {e}"),
)
})?;
let cm = header_rest[0]; let flg = header_rest[1];
if cm != GZIP_CM_DEFLATE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unsupported gzip compression method: {cm}"),
));
}
if flg & 0x04 != 0 {
let mut xlen_buf = [0u8; 2];
bit_reader.read_bytes(&mut xlen_buf).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip FEXTRA truncated: {e}"),
)
})?;
let xlen = u16::from_le_bytes(xlen_buf) as usize;
let mut extra = vec![0u8; xlen];
bit_reader.read_bytes(&mut extra).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip FEXTRA data truncated: {e}"),
)
})?;
}
if flg & 0x08 != 0 {
let mut byte = [0u8; 1];
loop {
bit_reader.read_bytes(&mut byte).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip FNAME truncated: {e}"),
)
})?;
if byte[0] == 0 {
break;
}
}
}
if flg & 0x10 != 0 {
let mut byte = [0u8; 1];
loop {
bit_reader.read_bytes(&mut byte).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip FCOMMENT truncated: {e}"),
)
})?;
if byte[0] == 0 {
break;
}
}
}
if flg & 0x02 != 0 {
let mut hcrc = [0u8; 2];
bit_reader.read_bytes(&mut hcrc).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip FHCRC truncated: {e}"),
)
})?;
}
let mut inflater = Inflater::new();
let (decompressed, _consumed) =
inflater.inflate_consumed(&mut bit_reader).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip deflate error: {e}"),
)
})?;
let mut trailer = [0u8; 8];
bit_reader.read_bytes(&mut trailer).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip trailer truncated: {e}"),
)
})?;
let stored_crc = u32::from_le_bytes([trailer[0], trailer[1], trailer[2], trailer[3]]);
let stored_isize = u32::from_le_bytes([trailer[4], trailer[5], trailer[6], trailer[7]]);
let actual_crc = Crc32::compute(&decompressed);
if actual_crc != stored_crc {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"gzip CRC-32 mismatch: stored {stored_crc:#010x}, computed {actual_crc:#010x}"
),
));
}
let actual_isize = (decompressed.len() as u64 & 0xFFFF_FFFF) as u32;
if actual_isize != stored_isize {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("gzip ISIZE mismatch: stored {stored_isize}, computed {actual_isize}"),
));
}
all_decompressed.extend_from_slice(&decompressed);
}
self.output_buffer = all_decompressed;
self.output_pos = 0;
self.finished = true;
Ok(())
}
pub fn decompressed_size(&self) -> usize {
self.output_buffer.len()
}
pub fn is_finished(&self) -> bool {
self.finished && self.output_pos >= self.output_buffer.len()
}
}
impl<R: Read> Read for GzipStreamDecoder<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.fill_buffer()?;
let available = self.output_buffer.len() - self.output_pos;
if available == 0 {
return Ok(0);
}
let to_copy = buf.len().min(available);
buf[..to_copy]
.copy_from_slice(&self.output_buffer[self.output_pos..self.output_pos + to_copy]);
self.output_pos += to_copy;
Ok(to_copy)
}
}
pub struct ZlibStreamEncoder<W: Write> {
inner: Option<W>,
buffer: Vec<u8>,
deflater: Deflater,
adler: Adler32,
header_written: bool,
finished: bool,
block_size: usize,
level: u8,
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
enum ZlibLevel {
Fastest = 0,
Fast = 1,
Default = 2,
Maximum = 3,
}
impl ZlibLevel {
fn from_level(level: u8) -> Self {
match level {
0..=2 => Self::Fastest,
3..=5 => Self::Fast,
6 => Self::Default,
7..=9 => Self::Maximum,
_ => Self::Default,
}
}
}
impl<W: Write> ZlibStreamEncoder<W> {
pub fn new(writer: W, level: u8) -> Self {
let level = level.min(9);
Self {
inner: Some(writer),
buffer: Vec::new(),
deflater: Deflater::new(level),
adler: Adler32::new(),
header_written: false,
finished: false,
block_size: DEFAULT_BLOCK_SIZE,
level,
}
}
pub fn with_block_size(mut self, block_size: usize) -> Self {
self.block_size = block_size.max(1);
self
}
fn ensure_header(&mut self) -> io::Result<()> {
if self.header_written {
return Ok(());
}
let cmf: u8 = 0x78;
let flevel = ZlibLevel::from_level(self.level) as u8;
let fdict = 0u8;
let fcheck = {
let base = (cmf as u16) * 256 + ((flevel << 6) | (fdict << 5)) as u16;
let remainder = base % 31;
if remainder == 0 {
0
} else {
(31 - remainder) as u8
}
};
let flg = (flevel << 6) | (fdict << 5) | fcheck;
if let Some(ref mut w) = self.inner {
w.write_all(&[cmf, flg])?;
}
self.header_written = true;
Ok(())
}
pub fn sync_flush(&mut self) -> io::Result<()> {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.adler.update(&data);
let mut compressed = Vec::new();
self.deflater
.deflate_sync(&data, &mut compressed)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
Ok(())
}
pub fn full_flush(&mut self) -> io::Result<()> {
self.sync_flush()?;
self.deflater.reset_lz77();
Ok(())
}
pub fn partial_flush(&mut self) -> io::Result<()> {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.adler.update(&data);
let mut compressed = Vec::new();
self.deflater
.deflate_partial(&data, &mut compressed)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
Ok(())
}
pub fn finish(mut self) -> io::Result<W> {
if !self.finished {
self.ensure_header()?;
let data = std::mem::take(&mut self.buffer);
self.adler.update(&data);
let mut compressed = Vec::new();
self.deflater
.deflate(&data, &mut compressed, true)
.map_err(|e| io::Error::other(e.to_string()))?;
if let Some(ref mut w) = self.inner {
w.write_all(&compressed)?;
}
let checksum = self.adler.finish();
if let Some(ref mut w) = self.inner {
w.write_all(&checksum.to_be_bytes())?;
}
self.finished = true;
}
self.inner
.take()
.ok_or_else(|| io::Error::other("inner writer already taken"))
}
fn maybe_flush_block(&mut self) -> io::Result<()> {
if self.buffer.len() >= self.block_size {
self.sync_flush()?;
}
Ok(())
}
pub fn buffered_bytes(&self) -> usize {
self.buffer.len()
}
pub fn is_finished(&self) -> bool {
self.finished
}
}
impl<W: Write> Write for ZlibStreamEncoder<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.finished {
return Err(io::Error::other("encoder already finished"));
}
self.buffer.extend_from_slice(buf);
self.maybe_flush_block()?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
if !self.buffer.is_empty() {
self.sync_flush()?;
}
if let Some(ref mut w) = self.inner {
w.flush()?;
}
Ok(())
}
}
pub struct ZlibStreamDecoder<R: Read> {
inner: R,
output_buffer: Vec<u8>,
output_pos: usize,
finished: bool,
}
impl<R: Read> ZlibStreamDecoder<R> {
pub fn new(reader: R) -> Self {
Self {
inner: reader,
output_buffer: Vec::new(),
output_pos: 0,
finished: false,
}
}
pub fn into_inner(self) -> R {
self.inner
}
fn fill_buffer(&mut self) -> io::Result<()> {
if self.finished || self.output_pos < self.output_buffer.len() {
return Ok(());
}
let mut compressed = Vec::new();
self.inner.read_to_end(&mut compressed)?;
if compressed.is_empty() {
self.finished = true;
return Ok(());
}
let mut all_decompressed = Vec::new();
let mut remaining = &compressed[..];
while !remaining.is_empty() {
if remaining.len() < 6 {
break;
}
let cmf = remaining[0];
let cm = cmf & 0x0F;
if cm != 8 {
break;
}
let flg = remaining[1];
let check = (cmf as u16) * 256 + (flg as u16);
if check % 31 != 0 {
break;
}
match zlib_decompress(remaining) {
Ok(decompressed) => {
all_decompressed.extend_from_slice(&decompressed);
remaining = &[];
}
Err(_) => {
let mut decoded_one = false;
for split_pos in 6..remaining.len().saturating_sub(5) {
let candidate_cmf = remaining[split_pos];
let candidate_cm = candidate_cmf & 0x0F;
if candidate_cm != 8 {
continue;
}
if split_pos + 1 >= remaining.len() {
continue;
}
let candidate_flg = remaining[split_pos + 1];
let candidate_check = (candidate_cmf as u16) * 256 + (candidate_flg as u16);
if candidate_check % 31 != 0 {
continue;
}
if let Ok(decompressed) = zlib_decompress(&remaining[..split_pos]) {
all_decompressed.extend_from_slice(&decompressed);
remaining = &remaining[split_pos..];
decoded_one = true;
break;
}
}
if !decoded_one {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"failed to decompress Zlib data",
));
}
}
}
}
self.output_buffer = all_decompressed;
self.output_pos = 0;
self.finished = true;
Ok(())
}
pub fn decompressed_size(&self) -> usize {
self.output_buffer.len()
}
pub fn is_finished(&self) -> bool {
self.finished && self.output_pos >= self.output_buffer.len()
}
}
impl<R: Read> Read for ZlibStreamDecoder<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.fill_buffer()?;
let available = self.output_buffer.len() - self.output_pos;
if available == 0 {
return Ok(0);
}
let to_copy = buf.len().min(available);
buf[..to_copy]
.copy_from_slice(&self.output_buffer[self.output_pos..self.output_pos + to_copy]);
self.output_pos += to_copy;
Ok(to_copy)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gzip_stream_encoder_basic() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder.write_all(b"Hello, GZIP!").expect("write failed");
let compressed = encoder.finish().expect("finish failed");
assert!(!compressed.is_empty());
assert_eq!(compressed[0], 0x1f);
assert_eq!(compressed[1], 0x8b);
}
#[test]
fn test_gzip_stream_encoder_empty() {
let encoder = GzipStreamEncoder::new(Vec::new(), 6);
let compressed = encoder.finish().expect("finish failed");
assert!(!compressed.is_empty());
assert_eq!(compressed[0], 0x1f);
assert_eq!(compressed[1], 0x8b);
}
#[test]
fn test_gzip_stream_roundtrip() {
let original = b"The quick brown fox jumps over the lazy dog.";
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, original.as_slice());
}
#[test]
fn test_gzip_stream_roundtrip_multiple_writes() {
let parts: &[&[u8]] = &[b"Hello, ", b"streaming ", b"GZIP!"];
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
for part in parts {
encoder.write_all(part).expect("write failed");
}
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Hello, streaming GZIP!");
}
#[test]
fn test_gzip_stream_decoder_small_reads() {
let original = b"ABCDEFGHIJ";
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
let mut buf = [0u8; 3];
loop {
let n = decoder.read(&mut buf).expect("read failed");
if n == 0 {
break;
}
output.extend_from_slice(&buf[..n]);
}
assert_eq!(output, original.as_slice());
}
#[test]
fn test_gzip_stream_decoder_empty_input() {
let mut decoder = GzipStreamDecoder::new(&[][..]);
let mut buf = [0u8; 16];
let n = decoder.read(&mut buf).expect("read failed");
assert_eq!(n, 0);
}
#[test]
fn test_gzip_stream_encoder_buffered_bytes() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
assert_eq!(encoder.buffered_bytes(), 0);
encoder.write_all(b"12345").expect("write failed");
assert_eq!(encoder.buffered_bytes(), 5);
encoder.write_all(b"67890").expect("write failed");
assert_eq!(encoder.buffered_bytes(), 10);
}
#[test]
fn test_gzip_stream_encoder_is_finished() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
assert!(!encoder.is_finished());
encoder.write_all(b"data").expect("write failed");
assert!(!encoder.is_finished());
}
#[test]
fn test_gzip_stream_decoder_is_finished() {
let original = b"short";
let mut enc = GzipStreamEncoder::new(Vec::new(), 6);
enc.write_all(original).expect("write failed");
let compressed = enc.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
assert!(!decoder.is_finished());
let mut out = Vec::new();
decoder.read_to_end(&mut out).expect("read failed");
assert!(decoder.is_finished());
}
#[test]
fn test_gzip_stream_roundtrip_large_data() {
let original: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect();
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder.write_all(&original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, original);
}
#[test]
fn test_gzip_stream_all_levels() {
let original = b"AAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC";
for level in 0u8..=9 {
let mut encoder = GzipStreamEncoder::new(Vec::new(), level);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(
output,
original.as_slice(),
"roundtrip failed at level {}",
level,
);
}
}
#[test]
fn test_gzip_stream_decoder_into_inner() {
let data = vec![1u8, 2, 3, 4, 5];
let decoder = GzipStreamDecoder::new(data.as_slice());
let inner = decoder.into_inner();
assert_eq!(inner, data.as_slice());
}
#[test]
fn test_gzip_stream_flush() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"data before flush")
.expect("write failed");
encoder.flush().expect("flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder.write_all(b" more data").expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"data before flush more data");
}
#[test]
fn test_gzip_stream_with_block_size() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6).with_block_size(10);
encoder
.write_all(b"This is more than ten bytes of data")
.expect("write failed");
assert!(encoder.buffered_bytes() < 35);
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"This is more than ten bytes of data");
}
#[test]
fn test_zlib_stream_encoder_basic() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder.write_all(b"Hello, Zlib!").expect("write failed");
let compressed = encoder.finish().expect("finish failed");
assert!(!compressed.is_empty());
assert_eq!(compressed[0], 0x78);
}
#[test]
fn test_zlib_stream_encoder_empty() {
let encoder = ZlibStreamEncoder::new(Vec::new(), 6);
let compressed = encoder.finish().expect("finish failed");
assert!(!compressed.is_empty());
assert_eq!(compressed[0], 0x78);
}
#[test]
fn test_zlib_stream_roundtrip() {
let original = b"The quick brown fox jumps over the lazy dog.";
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, original.as_slice());
}
#[test]
fn test_zlib_stream_roundtrip_multiple_writes() {
let parts: &[&[u8]] = &[b"Hello, ", b"streaming ", b"Zlib!"];
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
for part in parts {
encoder.write_all(part).expect("write failed");
}
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Hello, streaming Zlib!");
}
#[test]
fn test_zlib_stream_decoder_small_reads() {
let original = b"ABCDEFGHIJ";
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
let mut buf = [0u8; 3];
loop {
let n = decoder.read(&mut buf).expect("read failed");
if n == 0 {
break;
}
output.extend_from_slice(&buf[..n]);
}
assert_eq!(output, original.as_slice());
}
#[test]
fn test_zlib_stream_decoder_empty_input() {
let mut decoder = ZlibStreamDecoder::new(&[][..]);
let mut buf = [0u8; 16];
let n = decoder.read(&mut buf).expect("read failed");
assert_eq!(n, 0);
}
#[test]
fn test_zlib_stream_encoder_buffered_bytes() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
assert_eq!(encoder.buffered_bytes(), 0);
encoder.write_all(b"12345").expect("write failed");
assert_eq!(encoder.buffered_bytes(), 5);
}
#[test]
fn test_zlib_stream_decoder_is_finished() {
let original = b"short";
let mut enc = ZlibStreamEncoder::new(Vec::new(), 6);
enc.write_all(original).expect("write failed");
let compressed = enc.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
assert!(!decoder.is_finished());
let mut out = Vec::new();
decoder.read_to_end(&mut out).expect("read failed");
assert!(decoder.is_finished());
}
#[test]
fn test_zlib_stream_roundtrip_large_data() {
let original: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect();
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder.write_all(&original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, original);
}
#[test]
fn test_zlib_stream_all_levels() {
let original = b"AAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC";
for level in 0u8..=9 {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), level);
encoder.write_all(original).expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(
output,
original.as_slice(),
"roundtrip failed at level {}",
level,
);
}
}
#[test]
fn test_zlib_stream_decoder_into_inner() {
let data = vec![1u8, 2, 3, 4, 5];
let decoder = ZlibStreamDecoder::new(data.as_slice());
let inner = decoder.into_inner();
assert_eq!(inner, data.as_slice());
}
#[test]
fn test_zlib_stream_flush() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"data before flush")
.expect("write failed");
encoder.flush().expect("flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder.write_all(b" more data").expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"data before flush more data");
}
#[test]
fn test_zlib_stream_with_block_size() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6).with_block_size(10);
encoder
.write_all(b"This is more than ten bytes of data")
.expect("write failed");
assert!(encoder.buffered_bytes() < 35);
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"This is more than ten bytes of data");
}
#[test]
fn test_gzip_and_zlib_produce_different_output() {
let original = b"Same input for both formats";
let mut gzip_enc = GzipStreamEncoder::new(Vec::new(), 6);
gzip_enc.write_all(original).expect("write failed");
let gzip_out = gzip_enc.finish().expect("finish failed");
let mut zlib_enc = ZlibStreamEncoder::new(Vec::new(), 6);
zlib_enc.write_all(original).expect("write failed");
let zlib_out = zlib_enc.finish().expect("finish failed");
assert_ne!(gzip_out, zlib_out);
let mut gzip_dec = GzipStreamDecoder::new(&gzip_out[..]);
let mut gzip_result = Vec::new();
gzip_dec.read_to_end(&mut gzip_result).expect("read failed");
let mut zlib_dec = ZlibStreamDecoder::new(&zlib_out[..]);
let mut zlib_result = Vec::new();
zlib_dec.read_to_end(&mut zlib_result).expect("read failed");
assert_eq!(gzip_result, original.as_slice());
assert_eq!(zlib_result, original.as_slice());
}
#[test]
fn test_stream_encoder_write_after_finish_errors() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder.write_all(b"first").expect("write failed");
encoder.flush().expect("flush failed");
encoder.write_all(b"second").expect("write failed");
let _compressed = encoder.finish().expect("finish failed");
}
#[test]
fn test_gzip_sync_flush_produces_decompressible_prefix() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Hello, sync flush world!")
.expect("write failed");
encoder.sync_flush().expect("sync_flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder
.write_all(b" And more data after sync.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(
output,
b"Hello, sync flush world! And more data after sync."
);
}
#[test]
fn test_gzip_full_flush_resets_state() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Data before full flush.")
.expect("write failed");
encoder.full_flush().expect("full_flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder
.write_all(b" Data after full flush.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Data before full flush. Data after full flush.");
}
#[test]
fn test_gzip_partial_flush() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Partial flush data.")
.expect("write failed");
encoder.partial_flush().expect("partial_flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder
.write_all(b" More data after partial.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Partial flush data. More data after partial.");
}
#[test]
fn test_zlib_sync_flush_produces_decompressible_prefix() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Hello, zlib sync flush!")
.expect("write failed");
encoder.sync_flush().expect("sync_flush failed");
assert_eq!(encoder.buffered_bytes(), 0);
encoder
.write_all(b" More zlib data.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Hello, zlib sync flush! More zlib data.");
}
#[test]
fn test_zlib_full_flush_resets_state() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Zlib before full flush.")
.expect("write failed");
encoder.full_flush().expect("full_flush failed");
encoder
.write_all(b" Zlib after full flush.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Zlib before full flush. Zlib after full flush.");
}
#[test]
fn test_zlib_partial_flush() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"Zlib partial flush.")
.expect("write failed");
encoder.partial_flush().expect("partial_flush failed");
encoder
.write_all(b" More zlib data after partial.")
.expect("write failed");
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, b"Zlib partial flush. More zlib data after partial.");
}
#[test]
fn test_gzip_multiple_flush_write_cycles() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
let mut expected = Vec::new();
for i in 0..5 {
let chunk = format!("Chunk {} data. ", i);
expected.extend_from_slice(chunk.as_bytes());
encoder.write_all(chunk.as_bytes()).expect("write failed");
match i % 3 {
0 => encoder.sync_flush().expect("sync_flush failed"),
1 => encoder.full_flush().expect("full_flush failed"),
_ => encoder.partial_flush().expect("partial_flush failed"),
}
}
let compressed = encoder.finish().expect("finish failed");
let mut decoder = GzipStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, expected);
}
#[test]
fn test_zlib_multiple_flush_write_cycles() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
let mut expected = Vec::new();
for i in 0..5 {
let chunk = format!("ZChunk {} data. ", i);
expected.extend_from_slice(chunk.as_bytes());
encoder.write_all(chunk.as_bytes()).expect("write failed");
match i % 3 {
0 => encoder.sync_flush().expect("sync_flush failed"),
1 => encoder.full_flush().expect("full_flush failed"),
_ => encoder.partial_flush().expect("partial_flush failed"),
}
}
let compressed = encoder.finish().expect("finish failed");
let mut decoder = ZlibStreamDecoder::new(&compressed[..]);
let mut output = Vec::new();
decoder.read_to_end(&mut output).expect("read failed");
assert_eq!(output, expected);
}
#[test]
fn test_gzip_sync_flush_marker_present() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"test sync marker")
.expect("write failed");
encoder.sync_flush().expect("sync_flush failed");
let compressed = encoder.finish().expect("finish failed");
let payload = &compressed[10..];
let marker = [0x00, 0x00, 0xFF, 0xFF];
let found = payload.windows(4).any(|w| w == marker);
assert!(found, "Sync flush marker not found in GZIP payload");
}
#[test]
fn test_zlib_sync_flush_marker_present() {
let mut encoder = ZlibStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"test zlib sync marker")
.expect("write failed");
encoder.sync_flush().expect("sync_flush failed");
let compressed = encoder.finish().expect("finish failed");
let payload = &compressed[2..];
let marker = [0x00, 0x00, 0xFF, 0xFF];
let found = payload.windows(4).any(|w| w == marker);
assert!(found, "Sync flush marker not found in Zlib payload");
}
#[test]
fn test_gzip_partial_flush_no_sync_marker() {
let mut encoder = GzipStreamEncoder::new(Vec::new(), 6);
encoder
.write_all(b"test partial no marker")
.expect("write failed");
encoder.partial_flush().expect("partial_flush failed");
let compressed = encoder.finish().expect("finish failed");
let payload = &compressed[10..compressed.len() - 8]; let marker = [0x00, 0x00, 0xFF, 0xFF];
let found = payload.windows(4).any(|w| w == marker);
assert!(!found, "Partial flush should not produce sync marker");
}
}