pub mod dictionary;
pub mod zstd_compression;
pub mod codec_selector;
pub mod enhanced_dict;
pub mod delta_encoding;
pub mod variable_zstd;
pub mod cahp;
pub use dictionary::{DictionaryEncoder, DictionaryDecoder};
pub use zstd_compression::{ZstdCompressor, ZstdDecompressor};
pub use codec_selector::{CompressionCodec, CodecSelector};
pub use enhanced_dict::MultiLevelDictionary;
pub use delta_encoding::DeltaEncoder;
pub use variable_zstd::VariableZstdCompressor;
pub use cahp::CAHPCompressor;
pub use crate::decompression::CodecId;
use std::fmt;
#[derive(Debug, Clone)]
pub struct CompressionResult {
pub codec: CompressionCodec,
pub original_size: usize,
pub compressed_size: usize,
pub compression_ratio: f64,
pub data: Vec<u8>,
}
impl CompressionResult {
pub fn new(codec: CompressionCodec, original_size: usize, data: Vec<u8>) -> Self {
let compressed_size = data.len();
let compression_ratio = (compressed_size as f64) / (original_size as f64);
Self {
codec,
original_size,
compressed_size,
compression_ratio,
data,
}
}
pub fn savings_percent(&self) -> f64 {
(1.0 - self.compression_ratio) * 100.0
}
pub fn is_beneficial(&self) -> bool {
self.compression_ratio < 0.95 }
}
impl fmt::Display for CompressionResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Codec: {:?}, Original: {} bytes, Compressed: {} bytes, Ratio: {:.2}%, Savings: {:.1}%",
self.codec,
self.original_size,
self.compressed_size,
self.compression_ratio * 100.0,
self.savings_percent()
)
}
}
#[derive(Clone, Debug)]
pub struct CompressionStats {
pub original_size: usize,
pub compressed_size: usize,
pub ratio: f32,
}
impl CompressionStats {
pub fn new(original_size: usize, compressed_size: usize) -> Self {
let ratio = if original_size > 0 {
(compressed_size as f32) / (original_size as f32)
} else {
1.0
};
Self {
original_size,
compressed_size,
ratio,
}
}
}
pub struct CompressionRegistry;
impl CompressionRegistry {
fn encode_varint(mut val: u32, buf: &mut Vec<u8>) {
loop {
let mut byte = (val & 0x7F) as u8;
val >>= 7;
if val != 0 {
byte |= 0x80; }
buf.push(byte);
if val == 0 {
break;
}
}
}
fn try_rle_compress(data: &[u8]) -> Option<Vec<u8>> {
let mut result = Vec::new();
let mut i = 0;
let mut compressed_any = false;
while i < data.len() {
let byte = data[i];
let mut count = 1u32;
while (i + count as usize) < data.len()
&& data[i + count as usize] == byte
&& count < u32::MAX {
count += 1;
}
if count >= 3 {
result.push(1u8);
result.push(byte);
Self::encode_varint(count, &mut result);
compressed_any = true;
i += count as usize;
} else {
for _ in 0..count {
result.push(1u8);
result.push(byte);
Self::encode_varint(1, &mut result);
}
i += count as usize;
}
}
if result.len() < data.len() {
Some(result)
} else {
None
}
}
pub fn compress(codec: CodecId, data: &[u8]) -> Result<(Vec<u8>, CodecId, CompressionStats), Box<dyn std::error::Error>> {
let (compressed_data, actual_codec) = match codec {
CodecId::None => (data.to_vec(), CodecId::None),
CodecId::RLE => {
match Self::try_rle_compress(data) {
Some(compressed) => (compressed, CodecId::RLE),
None => (data.to_vec(), CodecId::None), }
}
CodecId::Dictionary => {
match Self::try_rle_compress(data) {
Some(compressed) => (compressed, CodecId::RLE), None => (data.to_vec(), CodecId::None),
}
}
CodecId::EnhancedDictionary => {
match Self::try_rle_compress(data) {
Some(compressed) => (compressed, CodecId::EnhancedDictionary),
None => (data.to_vec(), CodecId::None),
}
}
CodecId::FOR => {
(data.to_vec(), CodecId::None) }
CodecId::DoubleDelta => {
(data.to_vec(), CodecId::None) }
CodecId::LZSS => {
let compressor = ZstdCompressor::default_balanced();
match compressor.compress(data) {
Ok(compressed) if compressed.len() < data.len() => (compressed, CodecId::LZSS),
_ => (data.to_vec(), CodecId::None), }
}
};
let stats = CompressionStats {
original_size: data.len(),
compressed_size: compressed_data.len(),
ratio: (compressed_data.len() as f32) / (data.len().max(1) as f32),
};
Ok((compressed_data, actual_codec, stats))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compression_result_creation() {
let data = vec![1, 2, 3, 4, 5];
let result = CompressionResult::new(CompressionCodec::Dictionary, 100, data);
assert_eq!(result.original_size, 100);
assert_eq!(result.compressed_size, 5);
assert!(result.is_beneficial());
}
}