use super::{Error, Lz4DecompressSnafu, ZstdSnafu};
use lz4_flex::block::{compress_prepend_size, decompress_size_prepended};
use snafu::ResultExt;
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Algorithm {
Lz4,
Zstd(i32),
}
impl Default for Algorithm {
fn default() -> Self {
Algorithm::Zstd(zstd::DEFAULT_COMPRESSION_LEVEL)
}
}
pub fn compress(data: &[u8], algorithm: Algorithm) -> Result<Vec<u8>> {
match algorithm {
Algorithm::Lz4 => {
Ok(compress_prepend_size(data))
}
Algorithm::Zstd(level) => {
zstd::encode_all(data, level).context(ZstdSnafu)
}
}
}
pub fn decompress(data: &[u8], algorithm: Algorithm) -> Result<Vec<u8>> {
match algorithm {
Algorithm::Lz4 => decompress_size_prepended(data).context(Lz4DecompressSnafu),
Algorithm::Zstd(_) => {
zstd::decode_all(data).context(ZstdSnafu)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
fn round_trip(algo: Algorithm, payload: &[u8]) {
let compressed = compress(payload, algo).expect("compress should succeed");
let decompressed = decompress(&compressed, algo).expect("decompress should succeed");
assert_eq!(decompressed, payload, "round-trip 必须保留所有字节");
}
#[test]
fn lz4_round_trip() {
round_trip(Algorithm::Lz4, b"hello world");
round_trip(Algorithm::Lz4, &vec![0xAB; 1024]); round_trip(Algorithm::Lz4, &[]); }
#[test]
fn zstd_round_trip() {
let algo = Algorithm::Zstd(zstd::DEFAULT_COMPRESSION_LEVEL);
round_trip(algo, b"hello world");
round_trip(algo, &vec![0xAB; 1024]);
round_trip(algo, &[]);
}
#[test]
fn decompress_garbage_returns_error() {
let garbage = b"this is definitely not compressed data";
assert!(decompress(garbage, Algorithm::Lz4).is_err());
assert!(decompress(garbage, Algorithm::Zstd(0)).is_err());
}
}