use crate::compression::CompressionAlgorithm;
use do_memory_core::Result;
#[derive(Debug, Clone)]
pub struct CompressedPayload {
pub original_size: usize,
pub compressed_size: usize,
pub compression_ratio: f64,
pub data: Vec<u8>,
pub algorithm: CompressionAlgorithm,
}
impl CompressedPayload {
pub fn compress(data: &[u8], threshold: usize) -> Result<Self> {
if data.len() < threshold {
return Ok(CompressedPayload {
original_size: data.len(),
compressed_size: data.len(),
compression_ratio: 1.0,
data: data.to_vec(),
algorithm: CompressionAlgorithm::None,
});
}
let zstd_result = Self::compress_zstd(data);
match zstd_result {
Ok(zstd) => {
if zstd.compression_ratio >= 0.5 {
return Ok(zstd);
}
Self::compress_lz4(data)
}
Err(_) => {
Self::compress_lz4(data)
}
}
}
pub fn compress_lz4(data: &[u8]) -> Result<Self> {
#[cfg(feature = "compression-lz4")]
{
let compressed = lz4_flex::compress(data);
let compressed_size = compressed.len();
let original_size = data.len();
let ratio = if original_size > 0 {
compressed_size as f64 / original_size as f64
} else {
1.0
};
Ok(CompressedPayload {
original_size,
compressed_size,
compression_ratio: ratio,
data: compressed,
algorithm: CompressionAlgorithm::Lz4,
})
}
#[cfg(not(feature = "compression-lz4"))]
{
Self::compress_gzip(data)
}
}
pub fn compress_zstd(data: &[u8]) -> Result<Self> {
#[cfg(feature = "compression-zstd")]
{
let compressed = zstd::stream::encode_all(data, 0)?;
let compressed_size = compressed.len();
let original_size = data.len();
let ratio = if original_size > 0 {
compressed_size as f64 / original_size as f64
} else {
1.0
};
Ok(CompressedPayload {
original_size,
compressed_size,
compression_ratio: ratio,
data: compressed,
algorithm: CompressionAlgorithm::Zstd,
})
}
#[cfg(not(feature = "compression-zstd"))]
{
Err(do_memory_core::Error::Storage(
"Zstd compression not available (enable compression-zstd feature)".to_string(),
))
}
}
pub fn compress_gzip(data: &[u8]) -> Result<Self> {
#[cfg(feature = "compression-gzip")]
{
use flate2::{Compression, bufread::GzEncoder};
use std::io::Read;
let compression = Compression::default();
let mut encoder = GzEncoder::new(data, compression);
let mut compressed = Vec::new();
encoder.read_to_end(&mut compressed)?;
let compressed_size = compressed.len();
let original_size = data.len();
let ratio = if original_size > 0 {
compressed_size as f64 / original_size as f64
} else {
1.0
};
Ok(CompressedPayload {
original_size,
compressed_size,
compression_ratio: ratio,
data: compressed,
algorithm: CompressionAlgorithm::Gzip,
})
}
#[cfg(not(feature = "compression-gzip"))]
{
Ok(CompressedPayload {
original_size: data.len(),
compressed_size: data.len(),
compression_ratio: 1.0,
data: data.to_vec(),
algorithm: CompressionAlgorithm::None,
})
}
}
pub fn decompress(&self) -> Result<Vec<u8>> {
match self.algorithm {
CompressionAlgorithm::None => Ok(self.data.clone()),
CompressionAlgorithm::Lz4 => self.decompress_lz4(),
CompressionAlgorithm::Zstd => self.decompress_zstd(),
CompressionAlgorithm::Gzip => self.decompress_gzip(),
}
}
fn decompress_lz4(&self) -> Result<Vec<u8>> {
#[cfg(feature = "compression-lz4")]
{
lz4_flex::decompress(&self.data, self.original_size).map_err(|e| {
do_memory_core::Error::Storage(format!("LZ4 decompression failed: {}", e))
})
}
#[cfg(not(feature = "compression-lz4"))]
{
Err(do_memory_core::Error::Storage(
"LZ4 decompression not available (enable compression-lz4 feature)".to_string(),
))
}
}
fn decompress_zstd(&self) -> Result<Vec<u8>> {
#[cfg(feature = "compression-zstd")]
{
zstd::stream::decode_all(&self.data[..]).map_err(|e| {
do_memory_core::Error::Storage(format!("Zstd decompression failed: {}", e))
})
}
#[cfg(not(feature = "compression-zstd"))]
{
Err(do_memory_core::Error::Storage(
"Zstd decompression not available (enable compression-zstd feature)".to_string(),
))
}
}
fn decompress_gzip(&self) -> Result<Vec<u8>> {
#[cfg(feature = "compression-gzip")]
{
use flate2::bufread::GzDecoder;
use std::io::Read;
let mut decoder = GzDecoder::new(&self.data[..]);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed).map_err(|e| {
do_memory_core::Error::Storage(format!("Gzip decompression failed: {}", e))
})?;
Ok(decompressed)
}
#[cfg(not(feature = "compression-gzip"))]
{
Err(do_memory_core::Error::Storage(
"Gzip decompression not available (enable compression-gzip feature)".to_string(),
))
}
}
pub fn bandwidth_savings_percent(&self) -> f64 {
(1.0 - self.compression_ratio) * 100.0
}
}