use crate::status::{Result, Status};
use crate::table::Compressor;
#[derive(Debug, Default, Clone, Copy)]
pub struct SnappyCompressor;
impl SnappyCompressor {
pub fn new() -> Self {
Self
}
}
impl Compressor for SnappyCompressor {
fn kind(&self) -> u8 {
1
}
fn compress(&self, input: &[u8]) -> Option<Vec<u8>> {
let mut encoder = snap::raw::Encoder::new();
match encoder.compress_vec(input) {
Ok(compressed) if compressed.len() < input.len() - (input.len() / 8) => {
Some(compressed)
}
_ => None,
}
}
fn decompress(&self, input: &[u8]) -> Result<Vec<u8>> {
let mut decoder = snap::raw::Decoder::new();
decoder
.decompress_vec(input)
.map_err(|e| Status::corruption(format!("snappy decompress: {e}")))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_compressible_input() {
let c = SnappyCompressor;
let input = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".repeat(50);
let compressed = c
.compress(&input)
.expect("highly compressible input must compress");
assert!(
compressed.len() < input.len() - (input.len() / 8),
"compressed {} vs input {} doesn't beat 12.5% threshold",
compressed.len(),
input.len(),
);
let decoded = c.decompress(&compressed).unwrap();
assert_eq!(decoded, input);
}
#[test]
fn incompressible_input_returns_none() {
let c = SnappyCompressor;
let input: Vec<u8> = (0..1024).map(|i| (i as u8).wrapping_mul(31)).collect();
if let Some(compressed) = c.compress(&input) {
assert!(compressed.len() < input.len() - (input.len() / 8));
let decoded = c.decompress(&compressed).unwrap();
assert_eq!(decoded, input);
}
}
#[test]
fn empty_input_round_trips() {
let c = SnappyCompressor;
let compressed = c.compress(b"");
assert!(compressed.is_none());
}
#[test]
fn corrupt_payload_surfaces_corruption() {
let c = SnappyCompressor;
let result = c.decompress(&[0xff, 0xff, 0xff, 0xff]);
assert!(matches!(&result, Err(e) if e.is_corruption()));
}
#[test]
fn kind_byte_is_one() {
assert_eq!(SnappyCompressor.kind(), 1);
}
}