use flate2::Compression;
use std::io::{Read, Write};
use crate::runtime::transport::common::errors;
#[derive(Debug, Clone, Copy)]
pub struct CompressionConfig {
pub enabled: bool,
pub level: u32,
pub min_size: usize,
}
impl Default for CompressionConfig {
fn default() -> Self {
CompressionConfig {
enabled: true,
level: 6, min_size: 128, }
}
}
impl CompressionConfig {
pub fn disabled() -> Self {
CompressionConfig {
enabled: false,
level: 0,
min_size: 0,
}
}
pub fn maximum() -> Self {
CompressionConfig {
enabled: true,
level: 9,
min_size: 64,
}
}
pub fn fast() -> Self {
CompressionConfig {
enabled: true,
level: 1,
min_size: 256,
}
}
}
pub fn compress(data: &[u8], config: CompressionConfig) -> Result<Vec<u8>, String> {
if !config.enabled || data.len() < config.min_size {
return Ok(data.to_vec());
}
let compression = match config.level {
0 => Compression::none(),
1 => Compression::fast(),
9 => Compression::best(),
level => Compression::new(level),
};
let mut encoder = flate2::write::GzEncoder::new(Vec::new(), compression);
encoder
.write_all(data)
.map_err(|e| errors::compression_write_failed(&e))?;
encoder
.finish()
.map_err(|e| errors::compression_finish_failed(&e))
}
pub fn decompress(data: &[u8]) -> Result<Vec<u8>, String> {
let mut decoder = flate2::read::GzDecoder::new(data);
let mut decompressed = Vec::new();
decoder
.read_to_end(&mut decompressed)
.map_err(|e| errors::decompression_failed(&e))?;
Ok(decompressed)
}
pub fn compression_ratio(original_size: usize, compressed_size: usize) -> f64 {
if original_size == 0 {
0.0
} else {
compressed_size as f64 / original_size as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compression_config_default_is_enabled() {
let config = CompressionConfig::default();
assert!(config.enabled);
assert_eq!(config.level, 6);
assert_eq!(config.min_size, 128);
}
#[test]
fn compression_disabled_returns_original() {
let data = b"test data";
let config = CompressionConfig::disabled();
let result = compress(data, config).expect("should compress");
assert_eq!(result, data);
}
#[test]
fn compression_respects_min_size() {
let data = b"small";
let config = CompressionConfig {
enabled: true,
level: 6,
min_size: 1024, };
let result = compress(data, config).expect("should compress");
assert_eq!(result, data); }
#[test]
fn compresses_and_decompresses_correctly() {
let original = b"hello world ".repeat(100); let config = CompressionConfig::default();
let compressed = compress(&original, config).expect("should compress");
assert!(compressed.len() < original.len(), "compressed should be smaller");
let decompressed = decompress(&compressed).expect("should decompress");
assert_eq!(decompressed, original);
}
#[test]
fn compression_ratio_calculated_correctly() {
let ratio = compression_ratio(1000, 500);
assert_eq!(ratio, 0.5);
let ratio = compression_ratio(1000, 1000);
assert_eq!(ratio, 1.0);
let ratio = compression_ratio(0, 100);
assert_eq!(ratio, 0.0);
}
#[test]
fn maximum_compression_level_compresses_better() {
let data = &b"repetitive data ".repeat(100);
let config_normal = CompressionConfig::default();
let config_max = CompressionConfig::maximum();
let compressed_normal = compress(data, config_normal).expect("should compress");
let compressed_max = compress(data, config_max).expect("should compress");
assert!(compressed_max.len() <= compressed_normal.len());
}
}