use crate::error::{PluginError, Result};
use crate::plugin::{CompressionAlgorithm, PluginMetadata, COMPRESSION_ALGORITHMS};
use flate2::read::{DeflateDecoder, DeflateEncoder};
use flate2::Compression;
use linkme::distributed_slice;
use std::io::Read;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
pub struct DeflatePlugin;
impl CompressionAlgorithm for DeflatePlugin {
fn name(&self) -> &'static str {
"deflate"
}
fn metadata(&self) -> PluginMetadata {
PluginMetadata {
name: "deflate",
version: "1.0.0",
magic_number: [0x43, 0x52, 0x01, 0x00],
throughput: 200.0,
compression_ratio: 0.35,
description: "Standard DEFLATE compression (RFC 1951)",
}
}
fn compress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>> {
if cancel_flag.load(Ordering::Acquire) {
return Err(PluginError::Cancelled.into());
}
let mut encoder = DeflateEncoder::new(input, Compression::default());
let mut compressed = Vec::new();
let mut buffer = vec![0u8; 64 * 1024]; loop {
if cancel_flag.load(Ordering::Acquire) {
return Err(PluginError::Cancelled.into());
}
match encoder.read(&mut buffer) {
Ok(0) => break, Ok(n) => compressed.extend_from_slice(&buffer[..n]),
Err(e) => {
return Err(PluginError::OperationFailed(format!(
"DEFLATE compression failed: {e}"
))
.into())
}
}
}
Ok(compressed)
}
fn decompress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>> {
if cancel_flag.load(Ordering::Acquire) {
return Err(PluginError::Cancelled.into());
}
let mut decoder = DeflateDecoder::new(input);
let mut decompressed = Vec::new();
let mut buffer = vec![0u8; 64 * 1024]; loop {
if cancel_flag.load(Ordering::Acquire) {
return Err(PluginError::Cancelled.into());
}
match decoder.read(&mut buffer) {
Ok(0) => break, Ok(n) => decompressed.extend_from_slice(&buffer[..n]),
Err(e) => {
return Err(PluginError::OperationFailed(format!(
"DEFLATE decompression failed: {e}"
))
.into())
}
}
}
Ok(decompressed)
}
fn detect(&self, _file_header: &[u8]) -> bool {
true
}
}
#[distributed_slice(COMPRESSION_ALGORITHMS)]
static DEFLATE_PLUGIN: &dyn CompressionAlgorithm = &DeflatePlugin;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deflate_metadata() {
let plugin = DeflatePlugin;
let metadata = plugin.metadata();
assert_eq!(metadata.name, "deflate");
assert_eq!(metadata.magic_number, [0x43, 0x52, 0x01, 0x00]);
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_roundtrip() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(false));
let original = b"Hello, DEFLATE! This is a test of the compression algorithm.";
let compressed = plugin.compress(original, Arc::clone(&cancel_flag)).unwrap();
let decompressed = plugin.decompress(&compressed, cancel_flag).unwrap();
assert_eq!(original.as_slice(), decompressed.as_slice());
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_empty() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(false));
let original = b"";
let compressed = plugin.compress(original, Arc::clone(&cancel_flag)).unwrap();
let decompressed = plugin.decompress(&compressed, cancel_flag).unwrap();
assert_eq!(original.as_slice(), decompressed.as_slice());
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_cancellation() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(true));
let original = b"This should be cancelled";
let result = plugin.compress(original, cancel_flag);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(
err,
crate::error::CrushError::Plugin(crate::error::PluginError::Cancelled)
),
"Expected PluginError::Cancelled, got: {err:?}"
);
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_decompress_invalid_data() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(false));
let invalid_data = b"This is not compressed data!";
let result = plugin.decompress(invalid_data, cancel_flag);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("DEFLATE decompression failed"));
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_decompress_cancellation() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(true));
let data = b"Some data";
let result = plugin.decompress(data, cancel_flag);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(
err,
crate::error::CrushError::Plugin(crate::error::PluginError::Cancelled)
),
"Expected PluginError::Cancelled, got: {err:?}"
);
}
#[test]
fn test_deflate_detect() {
let plugin = DeflatePlugin;
assert!(plugin.detect(b"any data"));
assert!(plugin.detect(&[]));
assert!(plugin.detect(b"\x00\x01\x02\x03"));
}
#[test]
fn test_deflate_name() {
let plugin = DeflatePlugin;
assert_eq!(plugin.name(), "deflate");
}
#[test]
#[allow(clippy::unwrap_used)]
fn test_deflate_large_data() {
let plugin = DeflatePlugin;
let cancel_flag = Arc::new(AtomicBool::new(false));
let original = vec![0x42u8; 128 * 1024]; let compressed = plugin
.compress(&original, Arc::clone(&cancel_flag))
.unwrap();
let decompressed = plugin.decompress(&compressed, cancel_flag).unwrap();
assert_eq!(original, decompressed);
assert!(compressed.len() < original.len());
}
#[test]
fn test_deflate_metadata_values() {
let plugin = DeflatePlugin;
let metadata = plugin.metadata();
assert_eq!(metadata.version, "1.0.0");
assert!(metadata.throughput > 0.0);
assert!(metadata.compression_ratio > 0.0 && metadata.compression_ratio <= 1.0);
assert!(!metadata.description.is_empty());
}
}