Skip to main content

streaming_crypto/core_api/compression/codecs/
lz4.rs

1//! codecs/lz4.rs
2//! LZ4 block streaming compressor/decompressor (deterministic, dictionary optional).
3use lz4_flex::block::{compress_prepend_size, decompress_size_prepended};
4
5use crate::compression::{compute_checksum, types::{CompressionError, Compressor, Decompressor}, verify_checksum};
6
7/// LZ4 compressor using lz4 block API.
8/// Note: lz4 does not expose streaming encoder with dictionary/level,
9/// so we emulate streaming by compressing each chunk independently.
10pub struct Lz4Compressor;
11
12pub struct Lz4Decompressor;
13
14impl Lz4Compressor {
15    pub fn new(_level: i32, _dict: Option<&[u8]>) -> Result<Box<dyn Compressor + Send>, CompressionError> {
16        // lz4 does not support level/dict in block mode.
17        Ok(Box::new(Self))
18    }
19}
20
21impl Compressor for Lz4Compressor {
22    fn compress_chunk(&mut self, input: &[u8], out: &mut Vec<u8>) -> Result<(), CompressionError> {
23        let compressed = compress_prepend_size(input);
24        out.extend_from_slice(&compressed);
25
26        // Append CRC32 of original input
27        let checksum = compute_checksum(&input, None);
28        out.extend_from_slice(&checksum.to_le_bytes());
29
30        Ok(())
31    }
32
33    fn finish(&mut self, _out: &mut Vec<u8>) -> Result<(), CompressionError> {
34        Ok(())
35    }
36}
37
38
39impl Lz4Decompressor {
40    pub fn new(_dict: Option<&[u8]>) -> Result<Box<dyn Decompressor + Send>, CompressionError> {
41        Ok(Box::new(Self))
42    }
43}
44
45impl Decompressor for Lz4Decompressor {
46    fn decompress_chunk(&mut self, input: &[u8], out: &mut Vec<u8>) -> Result<(), CompressionError> {
47        if input.len() < 4 {
48            return Err(CompressionError::CodecProcessFailed {
49                codec: "lz4".into(),
50                msg: "missing checksum".into(),
51            });
52        }
53        
54        // Split compressed data and checksum
55        let (compressed, checksum_bytes) = input.split_at(input.len() - 4);
56        let expected_crc = u32::from_le_bytes(checksum_bytes.try_into().unwrap());
57
58        let decompressed = decompress_size_prepended(compressed)
59            .map_err(|e| CompressionError::CodecProcessFailed {
60                codec: "lz4".into(),
61                msg: e.to_string(),
62            })?;
63        
64        // Verify checksum
65        let actual_crc = compute_checksum(&decompressed, None);
66        verify_checksum(expected_crc, actual_crc, "lz4".into())?;
67        
68        out.extend_from_slice(&decompressed);
69        Ok(())
70    }
71}