zopfli/
zlib.rs

1use crate::{BlockType, DeflateEncoder, Error, Options, Write};
2
3/// A Zlib encoder powered by the Zopfli algorithm, that compresses data using
4/// a [`DeflateEncoder`]. Most users will find using [`compress`](crate::compress)
5/// easier and more performant.
6///
7/// The caveats about short writes in [`DeflateEncoder`]s carry over to `ZlibEncoder`s:
8/// for best performance and compression, it is best to avoid them. One way to ensure
9/// this is to use the [`new_buffered`](ZlibEncoder::new_buffered) method.
10pub struct ZlibEncoder<W: Write> {
11    deflate_encoder: Option<DeflateEncoder<W>>,
12    adler_hasher: simd_adler32::Adler32,
13}
14
15impl<W: Write> ZlibEncoder<W> {
16    /// Creates a new Zlib encoder that will operate according to the
17    /// specified options.
18    pub fn new(options: Options, btype: BlockType, mut sink: W) -> Result<Self, Error> {
19        let cmf = 120; // CM 8, CINFO 7. See zlib spec.
20        let flevel = 3;
21        let fdict = 0;
22        let mut cmfflg: u16 = 256 * cmf + fdict * 32 + flevel * 64;
23        let fcheck = 31 - cmfflg % 31;
24        cmfflg += fcheck;
25
26        sink.write_all(&cmfflg.to_be_bytes())?;
27
28        Ok(Self {
29            deflate_encoder: Some(DeflateEncoder::new(options, btype, sink)),
30            adler_hasher: simd_adler32::Adler32::new(),
31        })
32    }
33
34    /// Creates a new Zlib encoder that operates according to the specified
35    /// options and is wrapped with a buffer to guarantee that data is
36    /// compressed in large chunks, which is necessary for decent performance
37    /// and good compression ratio.
38    #[cfg(feature = "std")]
39    pub fn new_buffered(
40        options: Options,
41        btype: BlockType,
42        sink: W,
43    ) -> Result<std::io::BufWriter<Self>, Error> {
44        Ok(std::io::BufWriter::with_capacity(
45            crate::util::ZOPFLI_MASTER_BLOCK_SIZE,
46            Self::new(options, btype, sink)?,
47        ))
48    }
49
50    /// Encodes any pending chunks of data and writes them to the sink,
51    /// consuming the encoder and returning the wrapped sink. The sink
52    /// will have received a complete Zlib stream when this method
53    /// returns.
54    ///
55    /// The encoder is automatically [`finish`](Self::finish)ed when
56    /// dropped, but explicitly finishing it with this method allows
57    /// handling I/O errors.
58    pub fn finish(mut self) -> Result<W, Error> {
59        self.__finish().map(|sink| sink.unwrap())
60    }
61
62    fn __finish(&mut self) -> Result<Option<W>, Error> {
63        if self.deflate_encoder.is_none() {
64            return Ok(None);
65        }
66
67        let mut sink = self.deflate_encoder.take().unwrap().finish()?;
68
69        sink.write_all(&self.adler_hasher.finish().to_be_bytes())?;
70
71        Ok(Some(sink))
72    }
73
74    /// Gets a reference to the underlying writer.
75    pub fn get_ref(&self) -> &W {
76        self.deflate_encoder.as_ref().unwrap().get_ref()
77    }
78
79    /// Gets a mutable reference to the underlying writer.
80    ///
81    /// Note that mutating the output/input state of the stream may corrupt
82    /// this object, so care must be taken when using this method.
83    pub fn get_mut(&mut self) -> &mut W {
84        self.deflate_encoder.as_mut().unwrap().get_mut()
85    }
86}
87
88impl<W: Write> Write for ZlibEncoder<W> {
89    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
90        self.deflate_encoder
91            .as_mut()
92            .unwrap()
93            .write(buf)
94            .map(|bytes_written| {
95                self.adler_hasher.write(&buf[..bytes_written]);
96                bytes_written
97            })
98    }
99
100    fn flush(&mut self) -> Result<(), Error> {
101        self.deflate_encoder.as_mut().unwrap().flush()
102    }
103}
104
105impl<W: Write> Drop for ZlibEncoder<W> {
106    fn drop(&mut self) {
107        self.__finish().ok();
108    }
109}
110
111// Boilerplate to make latest Rustdoc happy: https://github.com/rust-lang/rust/issues/117796
112#[cfg(all(doc, feature = "std"))]
113impl<W: crate::io::Write> std::io::Write for ZlibEncoder<W> {
114    fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
115        unimplemented!()
116    }
117
118    fn flush(&mut self) -> std::io::Result<()> {
119        unimplemented!()
120    }
121}