use crate::error::{CompressionError, CompressionResult};
use crate::{CompressionLevel, Compressor};
use flate2::read::{ZlibDecoder, ZlibEncoder};
use flate2::Compression;
use std::fmt;
use std::io::Read;
#[derive(Clone)]
pub struct ZlibCompressor {
level: CompressionLevel,
}
impl ZlibCompressor {
pub fn new(level: CompressionLevel) -> Self {
ZlibCompressor { level }
}
pub fn fast() -> Self {
ZlibCompressor::new(CompressionLevel::Fast)
}
pub fn default_level() -> Self {
ZlibCompressor::new(CompressionLevel::Default)
}
pub fn best() -> Self {
ZlibCompressor::new(CompressionLevel::Best)
}
fn get_compression(&self) -> Compression {
match self.level {
CompressionLevel::Fast => Compression::fast(), CompressionLevel::Default => Compression::new(6), CompressionLevel::Best => Compression::best(), }
}
}
impl fmt::Debug for ZlibCompressor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ZlibCompressor")
.field("level", &self.level)
.finish()
}
}
impl Compressor for ZlibCompressor {
fn compress(&self, data: &[u8]) -> CompressionResult<Vec<u8>> {
if data.is_empty() {
return Ok(Vec::new());
}
let mut encoder = ZlibEncoder::new(data, self.get_compression());
let mut compressed = Vec::new();
encoder
.read_to_end(&mut compressed)
.map_err(|e| CompressionError::zstd_error(format!("zlib compression failed: {}", e)))?;
Ok(compressed)
}
fn decompress(&self, data: &[u8]) -> CompressionResult<Vec<u8>> {
if data.is_empty() {
return Ok(Vec::new());
}
let is_zlib = data.len() >= 2 && data[0] == 0x78 && {
let cmf = data[0] as u16;
let flg = data[1] as u16;
(cmf * 256 + flg).is_multiple_of(31)
};
if is_zlib {
let mut decoder = ZlibDecoder::new(data);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed).map_err(|e| {
CompressionError::decompression_failed(format!("zlib decompression failed: {}", e))
})?;
Ok(decompressed)
} else {
Ok(data.to_vec())
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_zlib_compress_decompress() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let original = b"Hello, World! This is a test of zlib compression.";
let compressed = compressor.compress(original).unwrap();
let decompressed = compressor.decompress(&compressed).unwrap();
assert_eq!(original, &decompressed[..]);
}
#[test]
fn test_zlib_compress_empty() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let compressed = compressor.compress(b"").unwrap();
assert_eq!(compressed.len(), 0);
}
#[test]
fn test_zlib_decompress_empty() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let decompressed = compressor.decompress(b"").unwrap();
assert_eq!(decompressed.len(), 0);
}
#[test]
fn test_zlib_decompress_uncompressed_data() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let data = b"This is not compressed";
let result = compressor.decompress(data).unwrap();
assert_eq!(result, data);
}
#[test]
fn test_zlib_compression_levels() {
let data = b"This is test data that should compress. ".repeat(100);
let fast = ZlibCompressor::fast().compress(&data).unwrap();
let default = ZlibCompressor::default_level().compress(&data).unwrap();
let best = ZlibCompressor::best().compress(&data).unwrap();
assert!(best.len() <= default.len());
assert!(default.len() <= fast.len() + 50);
let decompressor = ZlibCompressor::default_level();
let fast_dec = decompressor.decompress(&fast).unwrap();
let default_dec = decompressor.decompress(&default).unwrap();
let best_dec = decompressor.decompress(&best).unwrap();
assert_eq!(fast_dec, data);
assert_eq!(default_dec, data);
assert_eq!(best_dec, data);
}
#[test]
fn test_zlib_large_data() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let original = vec![0x42u8; 1024 * 1024];
let compressed = compressor.compress(&original).unwrap();
let decompressed = compressor.decompress(&compressed).unwrap();
assert_eq!(original, decompressed);
assert!(compressed.len() < original.len() / 100);
}
#[test]
fn test_zlib_random_data() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let original = (0..1000).map(|i| (i ^ 0xAA) as u8).collect::<Vec<_>>();
let compressed = compressor.compress(&original).unwrap();
let decompressed = compressor.decompress(&compressed).unwrap();
assert_eq!(original, decompressed);
}
#[test]
fn test_zlib_debug_format() {
let compressor = ZlibCompressor::new(CompressionLevel::Default);
let debug_str = format!("{:?}", compressor);
assert!(debug_str.contains("ZlibCompressor"));
assert!(debug_str.contains("Default"));
}
#[test]
fn test_zlib_git_compatibility() {
let compressor = ZlibCompressor::default_level();
let git_blob = b"blob 13\0Hello, World!";
let compressed = compressor.compress(git_blob).unwrap();
assert_eq!(compressed[0], 0x78);
let decompressed = compressor.decompress(&compressed).unwrap();
assert_eq!(decompressed, git_blob);
}
}