use omnizip_filters::Filter;
use crate::codec::{compress, decompress, CoreError};
pub fn filter_then_compress<F: Filter>(
plaintext: &[u8],
filter: &F,
inner_codec: u8,
) -> Result<Vec<u8>, CoreError> {
let filtered = filter.encode(plaintext);
let inner = compress(inner_codec, &filtered)?;
let filtered_len = u32::try_from(filtered.len()).unwrap_or(u32::MAX);
let mut out = Vec::with_capacity(4 + inner.len());
out.extend_from_slice(&filtered_len.to_le_bytes());
out.extend_from_slice(&inner);
Ok(out)
}
pub fn decompress_then_filter<F: Filter>(
compressed: &[u8],
filter: &F,
inner_codec: u8,
error_label: &str,
) -> Result<Vec<u8>, CoreError> {
if compressed.len() < 4 {
return Err(CoreError::Corrupt {
reason: format!("{error_label}: input too short for length prefix"),
});
}
let filtered_len =
u32::from_le_bytes([compressed[0], compressed[1], compressed[2], compressed[3]]);
let inner_bytes = &compressed[4..];
let filtered = decompress(inner_codec, inner_bytes, filtered_len)?;
Ok(filter.decode(&filtered))
}
#[cfg(test)]
mod tests {
use super::*;
use omnizip_filters::shuffle::ByteShuffle;
#[test]
fn round_trips_through_any_filter() {
let plaintext = b"hello world. hello world. hello world.".repeat(10);
let filter = ByteShuffle::new(4);
let compressed =
filter_then_compress(&plaintext, &filter, crate::codec::CODEC_LZ4).expect("compress");
let recovered =
decompress_then_filter(&compressed, &filter, crate::codec::CODEC_LZ4, "test")
.expect("decompress");
assert_eq!(recovered, plaintext);
}
}