use crate::codec::Codec;
use crate::error::CoreError;
pub struct Lz4Codec;
impl Codec for Lz4Codec {
fn id(&self) -> u8 {
super::CODEC_LZ4
}
fn name(&self) -> &'static str {
"lz4"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
let codec = omnizip_lz4::Lz4FastCodec;
omnizip_codecs::Codec::compress(
&codec,
plaintext,
omnizip_codecs::CompressionLevel::default(),
)
.map_err(lz4_fast_err)
}
fn decompress(&self, compressed: &[u8], expected_len: u32) -> Result<Vec<u8>, CoreError> {
let codec = omnizip_lz4::Lz4FastCodec;
omnizip_codecs::Codec::decompress(&codec, compressed, expected_len).map_err(lz4_fast_err)
}
}
fn lz4_fast_err(e: omnizip_codecs::OmnizipError) -> CoreError {
CoreError::Corrupt {
reason: format!("lz4: {e}"),
}
}
pub struct Lz4HcCodec;
impl Codec for Lz4HcCodec {
fn id(&self) -> u8 {
super::CODEC_LZ4_HC
}
fn name(&self) -> &'static str {
"lz4-hc"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
let codec = omnizip_lz4::Lz4HcCodec;
omnizip_codecs::Codec::compress(
&codec,
plaintext,
omnizip_codecs::CompressionLevel::default(),
)
.map_err(lz4_hc_err)
}
fn decompress(&self, compressed: &[u8], expected_len: u32) -> Result<Vec<u8>, CoreError> {
let fast = omnizip_lz4::Lz4FastCodec;
omnizip_codecs::Codec::decompress(&fast, compressed, expected_len).map_err(lz4_hc_err)
}
}
fn lz4_hc_err(e: omnizip_codecs::OmnizipError) -> CoreError {
CoreError::Corrupt {
reason: format!("lz4-hc: {e}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hc_beats_fast_on_non_rle_friendly_input() {
let mut input = Vec::new();
let paragraph = b"the quick brown fox jumps over the lazy dog. ";
let mut i = 0u32;
while input.len() < 50_000 {
input.extend_from_slice(format!("{i:04}: {paragraph:?}\n").as_bytes());
i += 1;
}
let fast = Lz4Codec.compress(&input).expect("fast");
let hc = Lz4HcCodec.compress(&input).expect("hc");
assert!(
hc.len() < fast.len(),
"LZ4 HC ({}) should beat fast ({}) on mixed text",
hc.len(),
fast.len()
);
}
#[test]
fn hc_decodes_through_fast_decoder() {
let input = b"hello world. hello world. hello world.".repeat(20);
let hc = Lz4HcCodec.compress(&input).expect("hc compress");
let recovered = Lz4Codec
.decompress(&hc, input.len() as u32)
.expect("cross-decode");
assert_eq!(recovered, input);
}
#[test]
fn hc_round_trips() {
let input = b"the quick brown fox jumps over the lazy dog. ".repeat(50);
let hc = Lz4HcCodec.compress(&input).expect("hc");
let recovered = Lz4HcCodec
.decompress(&hc, input.len() as u32)
.expect("decompress");
assert_eq!(recovered, input);
}
}