vector/sinks/util/
snappy.rs

1//! An encoder for [Snappy] compression.
2//! Whilst there does exist a [Writer] implementation for Snappy, this compresses
3//! using the [Snappy frame format][frame], which is not quite what we want. So
4//! instead this encoder buffers the data in a [`Vec`] until the end. The `raw`
5//! compressor is then used to compress the data and writes it to the provided
6//! writer.
7//!
8//! [Snappy]: https://github.com/google/snappy/blob/main/docs/README.md
9//! [Writer]: https://docs.rs/snap/latest/snap/write/struct.FrameEncoder.html
10//! [frame]: https://github.com/google/snappy/blob/master/framing_format.txt
11
12use std::io;
13
14use snap::raw::Encoder;
15
16pub struct SnappyEncoder<W: io::Write> {
17    writer: W,
18    buffer: Vec<u8>,
19}
20
21impl<W: io::Write> SnappyEncoder<W> {
22    pub const fn new(writer: W) -> Self {
23        Self {
24            writer,
25            buffer: Vec::new(),
26        }
27    }
28
29    pub fn finish(mut self) -> io::Result<W> {
30        let mut encoder = Encoder::new();
31        let compressed = encoder.compress_vec(&self.buffer)?;
32
33        self.writer.write_all(&compressed)?;
34
35        Ok(self.writer)
36    }
37
38    pub const fn get_ref(&self) -> &W {
39        &self.writer
40    }
41
42    pub fn is_empty(&self) -> bool {
43        self.buffer.is_empty()
44    }
45}
46
47impl<W: io::Write> io::Write for SnappyEncoder<W> {
48    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
49        self.buffer.extend_from_slice(buf);
50        Ok(buf.len())
51    }
52
53    fn flush(&mut self) -> io::Result<()> {
54        Ok(())
55    }
56}
57
58impl<W: io::Write + std::fmt::Debug> std::fmt::Debug for SnappyEncoder<W> {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("SnappyEncoder")
61            .field("inner", &self.get_ref())
62            .finish()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use std::io::Write;
69
70    use bytes::{BufMut, BytesMut};
71
72    use super::*;
73
74    #[test]
75    fn is_empty() {
76        let writer = BytesMut::with_capacity(64).writer();
77        let mut encoder = SnappyEncoder::new(writer);
78
79        encoder.write_all(b"I am a potato").unwrap();
80
81        // Because we are buffering the results until the end, the writer will be
82        // empty, but our buffer won't be. The `is_empty` function is provided to
83        // allow us to determine if data has been written to the encoder without having
84        // to check the writer.
85        assert!(encoder.get_ref().get_ref().is_empty());
86        assert!(!encoder.is_empty());
87    }
88}