Skip to main content

agentic_memory/v3/
compression.rs

1//! Compression for cold and frozen tiers.
2//! Uses lz4 when available, otherwise falls back to no compression.
3//! Note: zstd is specified in the full spec but lz4 is already a dep.
4
5/// Compression level
6#[derive(Debug, Clone, Copy)]
7pub enum CompressionLevel {
8    None,
9    Fast,
10    Default,
11    Best,
12}
13
14/// Compress data using lz4
15pub fn compress(data: &[u8], level: CompressionLevel) -> Vec<u8> {
16    if matches!(level, CompressionLevel::None) || data.is_empty() {
17        return data.to_vec();
18    }
19
20    #[cfg(feature = "format")]
21    {
22        lz4_flex::compress_prepend_size(data)
23    }
24
25    #[cfg(not(feature = "format"))]
26    {
27        data.to_vec()
28    }
29}
30
31/// Decompress data
32pub fn decompress(data: &[u8]) -> Result<Vec<u8>, std::io::Error> {
33    if data.is_empty() {
34        return Ok(Vec::new());
35    }
36
37    #[cfg(feature = "format")]
38    {
39        lz4_flex::decompress_size_prepended(data).map_err(|e| {
40            // If decompression fails, data might not be compressed
41            std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())
42        })
43    }
44
45    #[cfg(not(feature = "format"))]
46    {
47        Ok(data.to_vec())
48    }
49}
50
51/// Check if data appears to be compressed (has lz4 size prefix)
52pub fn is_compressed(data: &[u8]) -> bool {
53    // lz4_flex prepend_size format: first 4 bytes are uncompressed size (LE)
54    // We can't definitively tell, but data.len() >= 4 is a minimum
55    data.len() >= 4
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_roundtrip() {
64        let original = b"Hello, this is a test of compression!".repeat(100);
65
66        let compressed = compress(&original, CompressionLevel::Default);
67
68        #[cfg(feature = "format")]
69        {
70            assert!(compressed.len() < original.len());
71            let decompressed = decompress(&compressed).unwrap();
72            assert_eq!(decompressed, original);
73        }
74
75        #[cfg(not(feature = "format"))]
76        {
77            assert_eq!(compressed, original);
78        }
79    }
80
81    #[test]
82    fn test_no_compression() {
83        let original = b"Small data";
84        let result = compress(original, CompressionLevel::None);
85        assert_eq!(result, original);
86    }
87}