puzzlefs_lib/
compression.rs

1use std::io;
2use std::io::Seek;
3
4mod noop;
5pub use noop::Noop;
6
7mod zstd_seekable_wrapper;
8pub use zstd_seekable_wrapper::*;
9
10pub trait Compressor: io::Write {
11    // https://users.rust-lang.org/t/how-to-move-self-when-using-dyn-trait/50123
12    fn end(self: Box<Self>) -> io::Result<()>;
13}
14
15pub trait Decompressor: io::Read + io::Seek {
16    fn get_uncompressed_length(&mut self) -> io::Result<u64>;
17}
18
19pub trait Compression {
20    fn compress<'a, W: std::io::Write + 'a>(dest: W) -> io::Result<Box<dyn Compressor + 'a>>;
21    fn decompress<'a, R: std::io::Read + Seek + 'a>(
22        source: R,
23    ) -> io::Result<Box<dyn Decompressor + 'a>>;
24    fn append_extension(media_type: &str) -> String;
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use tempfile::NamedTempFile;
31
32    pub const TRUTH: &str = "meshuggah rocks";
33
34    pub fn compress_decompress<C: Compression>() -> anyhow::Result<()> {
35        let f = NamedTempFile::new()?;
36        let mut compressed = C::compress(f.reopen()?)?;
37        compressed.write_all(TRUTH.as_bytes())?;
38        compressed.end()?;
39
40        let mut buf = vec![0_u8; TRUTH.len()];
41        let n = C::decompress(f.reopen()?)?.read(&mut buf)?;
42        assert_eq!(n, TRUTH.len());
43
44        assert_eq!(TRUTH.as_bytes(), buf);
45        Ok(())
46    }
47
48    pub fn compression_is_seekable<C: Compression>() -> anyhow::Result<()> {
49        let f = NamedTempFile::new()?;
50        let mut compressed = C::compress(f.reopen()?)?;
51        compressed.write_all(TRUTH.as_bytes())?;
52        compressed.end()?;
53
54        let mut buf = vec![0_u8; 1024];
55        let mut decompressor = C::decompress(f.reopen()?)?;
56        decompressor.seek(io::SeekFrom::Start("meshuggah ".len() as u64))?;
57        let n = decompressor.read(&mut buf)?;
58        assert_eq!(n, 5);
59
60        assert_eq!("rocks".as_bytes(), &buf[0..5]);
61        Ok(())
62    }
63}