pub fn compress(data: &[u8], level: i32, prefix: &[u8]) -> Vec<u8> {
let compressed = zstd::bulk::compress(data, level)
.expect("ZSTD compression failed");
if prefix.is_empty() {
compressed
} else {
let mut result = Vec::with_capacity(prefix.len() + compressed.len());
result.extend_from_slice(prefix);
result.extend_from_slice(&compressed);
result
}
}
pub fn decompress(data: &[u8], max_size: usize) -> Option<Vec<u8>> {
if max_size > 0 {
if let Ok(Some(size)) = zstd::zstd_safe::get_frame_content_size(data)
&& size as usize > max_size {
return None;
}
}
use std::io::Read;
let mut decoder = zstd::stream::read::Decoder::new(data).ok()?;
let mut decompressed = Vec::new();
let mut buf = [0u8; 4096];
loop {
let n = decoder.read(&mut buf).ok()?;
if n == 0 {
break;
}
if max_size > 0 && decompressed.len() + n > max_size {
return None;
}
decompressed.extend_from_slice(&buf[..n]);
}
Some(decompressed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compress_decompress_roundtrip() {
let original = b"Hello, world! This is some test data for ZSTD compression.";
let compressed = compress(original, 1, &[]);
let decompressed = decompress(&compressed, 0).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_compress_with_prefix() {
let original = b"test data";
let prefix = b"PREFIX";
let result = compress(original, 1, prefix);
assert!(result.starts_with(prefix));
let decompressed = decompress(&result[prefix.len()..], 0).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_decompress_max_size_exceeded() {
let original = vec![0u8; 10_000];
let compressed = compress(&original, 1, &[]);
let result = decompress(&compressed, 100);
assert!(result.is_none());
}
#[test]
fn test_decompress_max_size_ok() {
let original = b"small data";
let compressed = compress(original, 1, &[]);
let result = decompress(&compressed, 1_000_000);
assert_eq!(result.unwrap(), original);
}
#[test]
fn test_decompress_invalid_data() {
let garbage = b"this is not zstd data at all";
assert!(decompress(garbage, 0).is_none());
}
#[test]
fn test_compress_empty() {
let original = b"";
let compressed = compress(original, 1, &[]);
let decompressed = decompress(&compressed, 0).unwrap();
assert_eq!(decompressed, original.as_slice());
}
#[test]
fn test_compress_large_data() {
let original: Vec<u8> = (0..100_000).map(|i| (i % 256) as u8).collect();
let compressed = compress(&original, 3, &[]);
assert!(compressed.len() < original.len()); let decompressed = decompress(&compressed, 200_000).unwrap();
assert_eq!(decompressed, original);
}
}