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