use crate::{StorageError, StorageResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionLevel {
None,
Fast,
Balanced,
Best,
}
impl Default for CompressionLevel {
fn default() -> Self {
Self::Balanced
}
}
pub fn compress(data: &[u8], level: CompressionLevel) -> StorageResult<Vec<u8>> {
match level {
CompressionLevel::None => Ok(data.to_vec()),
CompressionLevel::Fast => {
Ok(lz4_flex::compress_prepend_size(data))
}
CompressionLevel::Balanced => {
zstd::encode_all(data, 3).map_err(|e| StorageError::Compression(e.to_string()))
}
CompressionLevel::Best => {
zstd::encode_all(data, 9).map_err(|e| StorageError::Compression(e.to_string()))
}
}
}
pub fn decompress(data: &[u8], was_lz4: bool) -> StorageResult<Vec<u8>> {
if was_lz4 {
lz4_flex::decompress_size_prepended(data)
.map_err(|e| StorageError::Decompression(e.to_string()))
} else {
zstd::decode_all(data).map_err(|e| StorageError::Decompression(e.to_string()))
}
}
#[allow(dead_code)]
pub fn should_compress(mime_type: Option<&str>, data: &[u8]) -> bool {
if let Some(mime) = mime_type {
let skip_types = [
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"video/",
"audio/",
"application/zip",
"application/gzip",
"application/x-7z-compressed",
"application/x-rar-compressed",
];
for skip in &skip_types {
if mime.starts_with(skip) {
return false;
}
}
}
if data.len() >= 4 {
if data.starts_with(&[0xFF, 0xD8, 0xFF]) {
return false;
}
if data.starts_with(&[0x89, 0x50, 0x4E, 0x47]) {
return false;
}
if data.starts_with(b"GIF8") {
return false;
}
if data.starts_with(&[0x50, 0x4B, 0x03, 0x04]) {
return false;
}
if data.starts_with(&[0x1F, 0x8B]) {
return false;
}
if data.len() >= 8 && &data[4..8] == b"ftyp" {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zstd_roundtrip() {
let original = b"Hello, FireCloud! This is test data for compression.";
let compressed = compress(original, CompressionLevel::Balanced).unwrap();
let decompressed = decompress(&compressed, false).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_lz4_roundtrip() {
let original = b"Hello, FireCloud! This is test data for LZ4 compression.";
let compressed = compress(original, CompressionLevel::Fast).unwrap();
let decompressed = decompress(&compressed, true).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_no_compression() {
let original = b"Raw data";
let result = compress(original, CompressionLevel::None).unwrap();
assert_eq!(result, original);
}
#[test]
fn test_should_compress() {
let jpeg = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10];
assert!(!should_compress(None, &jpeg));
let text = b"This is plain text that should be compressed";
assert!(should_compress(None, text));
assert!(!should_compress(Some("image/jpeg"), &[]));
assert!(should_compress(Some("text/plain"), &[]));
}
}