gix_features/zlib/stream/deflate/
mod.rs

1use flate2::Compress;
2
3const BUF_SIZE: usize = 4096 * 8;
4
5/// A utility to zlib compress anything that is written via its [Write][std::io::Write] implementation.
6///
7/// Be sure to call `flush()` when done to finalize the deflate stream.
8pub struct Write<W> {
9    compressor: Compress,
10    inner: W,
11    buf: [u8; BUF_SIZE],
12}
13
14impl<W> Clone for Write<W>
15where
16    W: Clone,
17{
18    fn clone(&self) -> Self {
19        Write {
20            compressor: impls::new_compress(),
21            inner: self.inner.clone(),
22            buf: self.buf,
23        }
24    }
25}
26
27mod impls {
28    use std::io;
29
30    use flate2::{Compress, Compression, FlushCompress, Status};
31
32    use crate::zlib::stream::deflate;
33
34    pub(crate) fn new_compress() -> Compress {
35        Compress::new(Compression::fast(), true)
36    }
37
38    impl<W> deflate::Write<W>
39    where
40        W: io::Write,
41    {
42        /// Create a new instance writing compressed bytes to `inner`.
43        pub fn new(inner: W) -> deflate::Write<W> {
44            deflate::Write {
45                compressor: new_compress(),
46                inner,
47                buf: [0; deflate::BUF_SIZE],
48            }
49        }
50
51        /// Reset the compressor, starting a new compression stream.
52        ///
53        /// That way multiple streams can be written to the same inner writer.
54        pub fn reset(&mut self) {
55            self.compressor.reset();
56        }
57
58        /// Consume `self` and return the inner writer.
59        pub fn into_inner(self) -> W {
60            self.inner
61        }
62
63        fn write_inner(&mut self, mut buf: &[u8], flush: FlushCompress) -> io::Result<usize> {
64            let total_in_when_start = self.compressor.total_in();
65            loop {
66                let last_total_in = self.compressor.total_in();
67                let last_total_out = self.compressor.total_out();
68
69                let status = self
70                    .compressor
71                    .compress(buf, &mut self.buf, flush)
72                    .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
73
74                let written = self.compressor.total_out() - last_total_out;
75                if written > 0 {
76                    self.inner.write_all(&self.buf[..written as usize])?;
77                }
78
79                match status {
80                    Status::StreamEnd => return Ok((self.compressor.total_in() - total_in_when_start) as usize),
81                    Status::Ok | Status::BufError => {
82                        let consumed = self.compressor.total_in() - last_total_in;
83                        buf = &buf[consumed as usize..];
84
85                        // output buffer still makes progress
86                        if self.compressor.total_out() > last_total_out {
87                            continue;
88                        }
89                        // input still makes progress
90                        if self.compressor.total_in() > last_total_in {
91                            continue;
92                        }
93                        // input also makes no progress anymore, need more so leave with what we have
94                        return Ok((self.compressor.total_in() - total_in_when_start) as usize);
95                    }
96                }
97            }
98        }
99    }
100
101    impl<W: io::Write> io::Write for deflate::Write<W> {
102        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
103            self.write_inner(buf, FlushCompress::None)
104        }
105
106        fn flush(&mut self) -> io::Result<()> {
107            self.write_inner(&[], FlushCompress::Finish).map(|_| ())
108        }
109    }
110}
111
112#[cfg(test)]
113mod tests;