gix_features/zlib/stream/deflate/
mod.rs1use crate::zlib::Status;
2use std::ffi::c_int;
3
4const BUF_SIZE: usize = 4096 * 8;
5
6pub struct Write<W> {
10 compressor: Compress,
11 inner: W,
12 buf: [u8; BUF_SIZE],
13}
14
15impl<W> Clone for Write<W>
16where
17 W: Clone,
18{
19 fn clone(&self) -> Self {
20 Write {
21 compressor: impls::new_compress(),
22 inner: self.inner.clone(),
23 buf: self.buf,
24 }
25 }
26}
27
28pub struct Compress(libz_rs_sys::z_stream);
30
31unsafe impl Sync for Compress {}
32unsafe impl Send for Compress {}
33
34impl Default for Compress {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl Compress {
41 pub fn total_in(&self) -> u64 {
43 self.0.total_in as _
44 }
45
46 pub fn total_out(&self) -> u64 {
48 self.0.total_out as _
49 }
50
51 pub fn new() -> Self {
53 let mut this = libz_rs_sys::z_stream::default();
54
55 unsafe {
56 libz_rs_sys::deflateInit_(
57 &mut this,
58 libz_rs_sys::Z_BEST_SPEED,
59 libz_rs_sys::zlibVersion(),
60 core::mem::size_of::<libz_rs_sys::z_stream>() as core::ffi::c_int,
61 );
62 }
63
64 Self(this)
65 }
66
67 pub fn reset(&mut self) {
69 unsafe { libz_rs_sys::deflateReset(&mut self.0) };
70 }
71
72 pub fn compress(&mut self, input: &[u8], output: &mut [u8], flush: FlushCompress) -> Result<Status, CompressError> {
74 self.0.avail_in = input.len() as _;
75 self.0.avail_out = output.len() as _;
76
77 self.0.next_in = input.as_ptr();
78 self.0.next_out = output.as_mut_ptr();
79
80 match unsafe { libz_rs_sys::deflate(&mut self.0, flush as _) } {
81 libz_rs_sys::Z_OK => Ok(Status::Ok),
82 libz_rs_sys::Z_BUF_ERROR => Ok(Status::BufError),
83 libz_rs_sys::Z_STREAM_END => Ok(Status::StreamEnd),
84
85 libz_rs_sys::Z_STREAM_ERROR => Err(CompressError::StreamError),
86 libz_rs_sys::Z_MEM_ERROR => Err(CompressError::InsufficientMemory),
87 err => Err(CompressError::Unknown { err }),
88 }
89 }
90}
91
92impl Drop for Compress {
93 fn drop(&mut self) {
94 unsafe { libz_rs_sys::deflateEnd(&mut self.0) };
95 }
96}
97
98#[derive(Debug, thiserror::Error)]
100#[error("{msg}")]
101#[allow(missing_docs)]
102pub enum CompressError {
103 #[error("stream error")]
104 StreamError,
105 #[error("Not enough memory")]
106 InsufficientMemory,
107 #[error("An unknown error occurred: {err}")]
108 Unknown { err: c_int },
109}
110
111#[derive(Copy, Clone, PartialEq, Eq, Debug)]
114#[non_exhaustive]
115#[allow(clippy::unnecessary_cast)]
116pub enum FlushCompress {
117 None = libz_rs_sys::Z_NO_FLUSH as isize,
121
122 Partial = libz_rs_sys::Z_PARTIAL_FLUSH as isize,
131
132 Sync = libz_rs_sys::Z_SYNC_FLUSH as isize,
140
141 Full = libz_rs_sys::Z_FULL_FLUSH as isize,
147
148 Finish = libz_rs_sys::Z_FINISH as isize,
153}
154
155mod impls {
156 use std::io;
157
158 use crate::zlib::stream::deflate::{self, Compress, FlushCompress};
159 use crate::zlib::Status;
160
161 pub(crate) fn new_compress() -> Compress {
162 Compress::new()
163 }
164
165 impl<W> deflate::Write<W>
166 where
167 W: io::Write,
168 {
169 pub fn new(inner: W) -> deflate::Write<W> {
171 deflate::Write {
172 compressor: new_compress(),
173 inner,
174 buf: [0; deflate::BUF_SIZE],
175 }
176 }
177
178 pub fn reset(&mut self) {
182 self.compressor.reset();
183 }
184
185 pub fn into_inner(self) -> W {
187 self.inner
188 }
189
190 fn write_inner(&mut self, mut buf: &[u8], flush: FlushCompress) -> io::Result<usize> {
191 let total_in_when_start = self.compressor.total_in();
192 loop {
193 let last_total_in = self.compressor.total_in();
194 let last_total_out = self.compressor.total_out();
195
196 let status = self
197 .compressor
198 .compress(buf, &mut self.buf, flush)
199 .map_err(io::Error::other)?;
200
201 let written = self.compressor.total_out() - last_total_out;
202 if written > 0 {
203 self.inner.write_all(&self.buf[..written as usize])?;
204 }
205
206 match status {
207 Status::StreamEnd => return Ok((self.compressor.total_in() - total_in_when_start) as usize),
208 Status::Ok | Status::BufError => {
209 let consumed = self.compressor.total_in() - last_total_in;
210 buf = &buf[consumed as usize..];
211
212 if self.compressor.total_out() > last_total_out {
214 continue;
215 }
216 if self.compressor.total_in() > last_total_in {
218 continue;
219 }
220 return Ok((self.compressor.total_in() - total_in_when_start) as usize);
222 }
223 }
224 }
225 }
226 }
227
228 impl<W: io::Write> io::Write for deflate::Write<W> {
229 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
230 self.write_inner(buf, FlushCompress::None)
231 }
232
233 fn flush(&mut self) -> io::Result<()> {
234 self.write_inner(&[], FlushCompress::Finish).map(|_| ())
235 }
236 }
237}
238
239#[cfg(test)]
240mod tests;