swapvec/
compression.rs

1use lz4_flex::{compress_prepend_size, decompress_size_prepended};
2
3use crate::{swapvec::CompressionLevel, Compression};
4
5/// Provide your own compression algorithm by
6/// creating an empty struct implementing `compress`
7/// and `decompress`.
8///
9/// Your compression algorithm is allowed to fail,
10/// but _must_ always decompress into the same
11/// bytes. Undefined behaviour otherwise.
12///
13/// Note: You must always also implement
14/// CompressBoxedClone, to allow cloning
15/// and debugging of the configuration.
16///
17/// ```rust
18/// use swapvec::Compress;
19/// struct DummyCompression;
20/// impl Compress for DummyCompression {
21///   fn compress(&self, block: Vec<u8>) -> Vec<u8> {
22///       block
23///   }
24///   fn decompress(&self, block: Vec<u8>) -> Result<Vec<u8>, ()> {
25///       Ok(block)
26///   }
27/// }
28///
29/// let bytes = vec![1, 2, 3];
30/// let compression = DummyCompression;
31/// assert_eq!(bytes, compression.decompress(compression.compress(bytes.clone())).unwrap());
32/// ```
33pub trait Compress {
34    /// Compress bytes blockwise. The compressed block
35    /// will be put into `self.decompress()` later.
36    fn compress(&self, block: Vec<u8>) -> Vec<u8>;
37    /// Receive block which was earlier `compress()`ed.
38    /// If the result is `Ok`, the same bytes which were
39    /// `compress()`es earlier are expected.
40    fn decompress(&self, block: Vec<u8>) -> Result<Vec<u8>, ()>;
41}
42
43impl Compress for Option<Compression> {
44    fn compress(&self, block: Vec<u8>) -> Vec<u8> {
45        match self {
46            Some(Compression::Lz4) => compress_prepend_size(&block).to_vec(),
47            Some(Compression::Deflate(level)) => {
48                let compression_level = match level {
49                    CompressionLevel::Fast => 2,
50                    CompressionLevel::Default => 6,
51                    CompressionLevel::Slow => 9,
52                };
53                miniz_oxide::deflate::compress_to_vec(&block, compression_level)
54            }
55            Some(Compression::Custom(algo)) => algo.compress(block),
56            None => block,
57        }
58    }
59    fn decompress(&self, block: Vec<u8>) -> Result<Vec<u8>, ()> {
60        match self {
61            Some(Compression::Lz4) => decompress_size_prepended(&block).map_err(|_| ()),
62            Some(Compression::Deflate(_)) => {
63                miniz_oxide::inflate::decompress_to_vec(&block).map_err(|_| ())
64            }
65            Some(Compression::Custom(algo)) => algo.decompress(block),
66            None => Ok(block),
67        }
68    }
69}
70
71/// Your custom compression algorithm struct must be debugable
72/// and clonable. Implement this trait to keep the main
73/// configuration debugable and clonable.
74pub trait CompressBoxedClone: Compress + std::fmt::Debug {
75    /// Clone your empty struct and return it as a new Box.
76    fn boxed_clone(&self) -> Box<dyn CompressBoxedClone + Send>;
77}
78
79#[cfg(test)]
80mod test {
81    use super::*;
82
83    #[test]
84    fn test_lz4() {
85        let compression = Some(Compression::Lz4);
86        let data: Vec<u8> = (0..u8::MAX).collect();
87        let compressed = compression.compress(data.clone());
88        let decompressed = compression.decompress(compressed).unwrap();
89        assert_eq!(decompressed, data);
90    }
91}