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}