Skip to main content

zlib_rs/
stable.rs

1use core::ffi::c_uint;
2
3use crate::deflate::DeflateConfig;
4use crate::inflate::InflateConfig;
5use crate::ReturnCode;
6pub use crate::{DeflateFlush, InflateFlush};
7
8/// Possible status results of compressing some data or successfully
9/// decompressing a block of data.
10#[derive(Copy, Clone, PartialEq, Eq, Debug)]
11pub enum Status {
12    /// Indicates success.
13    ///
14    /// Means that more input may be needed but isn't available
15    /// and/or there's more output to be written but the output buffer is full.
16    Ok,
17
18    /// Indicates that forward progress is not possible due to input or output
19    /// buffers being empty.
20    ///
21    /// For compression it means the input buffer needs some more data or the
22    /// output buffer needs to be freed up before trying again.
23    ///
24    /// For decompression this means that more input is needed to continue or
25    /// the output buffer isn't large enough to contain the result. The function
26    /// can be called again after fixing both.
27    BufError,
28
29    /// Indicates that all input has been consumed and all output bytes have
30    /// been written. Decompression/compression should not be called again.
31    ///
32    /// For decompression with zlib streams the adler-32 of the decompressed
33    /// data has also been verified.
34    StreamEnd,
35}
36
37/// Errors that can occur when decompressing.
38#[derive(Copy, Clone, PartialEq, Eq, Debug)]
39#[repr(i32)]
40pub enum InflateError {
41    /// Decompressing this input requires a dictionary.
42    NeedDict { dict_id: u32 } = 2,
43    /// The [`Inflate`] is in an inconsistent state, most likely
44    /// due to an invalid configuration parameter.
45    StreamError = -2,
46    /// The input is not a valid deflate stream.
47    DataError = -3,
48    /// A memory allocation failed.
49    MemError = -4,
50}
51
52impl From<InflateError> for ReturnCode {
53    fn from(value: InflateError) -> Self {
54        match value {
55            InflateError::NeedDict { .. } => ReturnCode::NeedDict,
56            InflateError::StreamError => ReturnCode::StreamError,
57            InflateError::DataError => ReturnCode::DataError,
58            InflateError::MemError => ReturnCode::MemError,
59        }
60    }
61}
62
63impl InflateError {
64    pub fn as_str(self) -> &'static str {
65        ReturnCode::from(self).error_message_str()
66    }
67}
68
69/// The state that is used to decompress an input.
70pub struct Inflate {
71    inner: crate::inflate::InflateStream<'static>,
72    total_in: u64,
73    total_out: u64,
74}
75
76impl Inflate {
77    /// The amount of bytes consumed from the input so far.
78    pub fn total_in(&self) -> u64 {
79        self.total_in
80    }
81
82    /// The amount of decompressed bytes that have been written to the output thus far.
83    pub fn total_out(&self) -> u64 {
84        self.total_out
85    }
86
87    /// The error message if the previous operation failed.
88    pub fn error_message(&self) -> Option<&'static str> {
89        if self.inner.msg.is_null() {
90            None
91        } else {
92            unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
93        }
94    }
95
96    /// Create a new instance. Note that it allocates in various ways and thus should be re-used.
97    ///
98    /// The `window_bits` must be in the range `8..=15`, with `15` being most common.
99    pub fn new(zlib_header: bool, window_bits: u8) -> Self {
100        let config = InflateConfig {
101            window_bits: if zlib_header {
102                i32::from(window_bits)
103            } else {
104                -i32::from(window_bits)
105            },
106        };
107
108        Self {
109            inner: crate::inflate::InflateStream::new(config),
110            total_in: 0,
111            total_out: 0,
112        }
113    }
114
115    /// Reset the state to allow handling a new stream.
116    pub fn reset(&mut self, zlib_header: bool) {
117        let mut config = InflateConfig::default();
118
119        if !zlib_header {
120            config.window_bits = -config.window_bits;
121        }
122
123        self.total_in = 0;
124        self.total_out = 0;
125
126        crate::inflate::reset_with_config(&mut self.inner, config);
127    }
128
129    /// Decompress `input` and write all decompressed bytes into `output`, with `flush` defining some details about this.
130    pub fn decompress(
131        &mut self,
132        input: &[u8],
133        output: &mut [u8],
134        flush: InflateFlush,
135    ) -> Result<Status, InflateError> {
136        // Limit the length of the input and output to the maximum value of a c_uint. For larger
137        // inputs, this will either complete or signal that more input and output is needed. The
138        // caller should be able to handle this regardless.
139        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
140        self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
141
142        // This cast_mut is unfortunate, that is just how the types are.
143        self.inner.next_in = input.as_ptr().cast_mut();
144        self.inner.next_out = output.as_mut_ptr();
145
146        let start_in = self.inner.next_in;
147        let start_out = self.inner.next_out;
148
149        // SAFETY: the inflate state was properly initialized.
150        let ret = unsafe { crate::inflate::inflate(&mut self.inner, flush) };
151
152        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
153        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
154
155        match ret {
156            ReturnCode::Ok => Ok(Status::Ok),
157            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
158            ReturnCode::NeedDict => Err(InflateError::NeedDict {
159                dict_id: self.inner.adler as u32,
160            }),
161            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
162            ReturnCode::StreamError => Err(InflateError::StreamError),
163            ReturnCode::DataError => Err(InflateError::DataError),
164            ReturnCode::MemError => Err(InflateError::MemError),
165            ReturnCode::BufError => Ok(Status::BufError),
166            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
167        }
168    }
169
170    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, InflateError> {
171        match crate::inflate::set_dictionary(&mut self.inner, dictionary) {
172            ReturnCode::Ok => Ok(self.inner.adler as u32),
173            ReturnCode::StreamError => Err(InflateError::StreamError),
174            ReturnCode::DataError => Err(InflateError::DataError),
175            other => unreachable!("set_dictionary does not return {other:?}"),
176        }
177    }
178}
179
180impl Drop for Inflate {
181    fn drop(&mut self) {
182        let _ = crate::inflate::end(&mut self.inner);
183    }
184}
185
186/// Errors that can occur when compressing.
187#[derive(Copy, Clone, PartialEq, Eq, Debug)]
188pub enum DeflateError {
189    /// The [`Deflate`] is in an inconsistent state, most likely
190    /// due to an invalid configuration parameter.
191    StreamError = -2,
192    /// The input is not a valid deflate stream.
193    DataError = -3,
194    /// A memory allocation failed.
195    MemError = -4,
196}
197
198impl From<DeflateError> for ReturnCode {
199    fn from(value: DeflateError) -> Self {
200        match value {
201            DeflateError::StreamError => ReturnCode::StreamError,
202            DeflateError::DataError => ReturnCode::DataError,
203            DeflateError::MemError => ReturnCode::MemError,
204        }
205    }
206}
207
208impl DeflateError {
209    pub fn as_str(self) -> &'static str {
210        ReturnCode::from(self).error_message_str()
211    }
212}
213
214impl From<ReturnCode> for Result<Status, DeflateError> {
215    fn from(value: ReturnCode) -> Self {
216        match value {
217            ReturnCode::Ok => Ok(Status::Ok),
218            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
219            ReturnCode::NeedDict => unreachable!("compression does not use dictionary"),
220            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
221            ReturnCode::StreamError => Err(DeflateError::StreamError),
222            ReturnCode::DataError => Err(DeflateError::DataError),
223            ReturnCode::MemError => Err(DeflateError::MemError),
224            ReturnCode::BufError => Ok(Status::BufError),
225            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
226        }
227    }
228}
229
230/// The state that is used to compress an input.
231pub struct Deflate {
232    inner: crate::deflate::DeflateStream<'static>,
233    total_in: u64,
234    total_out: u64,
235}
236
237impl Deflate {
238    /// The number of bytes that were read from the input.
239    pub fn total_in(&self) -> u64 {
240        self.total_in
241    }
242
243    /// The number of compressed bytes that were written to the output.
244    pub fn total_out(&self) -> u64 {
245        self.total_out
246    }
247
248    /// The error message if the previous operation failed.
249    pub fn error_message(&self) -> Option<&'static str> {
250        if self.inner.msg.is_null() {
251            None
252        } else {
253            unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
254        }
255    }
256
257    /// Create a new instance - this allocates so should be done with care.
258    ///
259    /// The `window_bits` must be in the range `8..=15`, with `15` being most common.
260    pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
261        let config = DeflateConfig {
262            window_bits: if zlib_header {
263                i32::from(window_bits)
264            } else {
265                -i32::from(window_bits)
266            },
267            level,
268            ..DeflateConfig::default()
269        };
270
271        Self {
272            inner: crate::deflate::DeflateStream::new(config),
273            total_in: 0,
274            total_out: 0,
275        }
276    }
277
278    /// Prepare the instance for a new stream.
279    pub fn reset(&mut self) {
280        self.total_in = 0;
281        self.total_out = 0;
282
283        crate::deflate::reset(&mut self.inner);
284    }
285
286    /// Compress `input` and write compressed bytes to `output`, with `flush` controlling additional characteristics.
287    pub fn compress(
288        &mut self,
289        input: &[u8],
290        output: &mut [u8],
291        flush: DeflateFlush,
292    ) -> Result<Status, DeflateError> {
293        // Limit the length of the input and output to the maximum value of a c_uint. For larger
294        // inputs, this will either complete or signal that more input and output is needed. The
295        // caller should be able to handle this regardless.
296        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
297        self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
298
299        // This cast_mut is unfortunate, that is just how the types are.
300        self.inner.next_in = input.as_ptr().cast_mut();
301        self.inner.next_out = output.as_mut_ptr();
302
303        let start_in = self.inner.next_in;
304        let start_out = self.inner.next_out;
305
306        let ret = crate::deflate::deflate(&mut self.inner, flush).into();
307
308        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
309        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
310
311        ret
312    }
313
314    /// Specifies the compression dictionary to use.
315    ///
316    /// Returns the Adler-32 checksum of the dictionary.
317    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DeflateError> {
318        match crate::deflate::set_dictionary(&mut self.inner, dictionary) {
319            ReturnCode::Ok => Ok(self.inner.adler as u32),
320            ReturnCode::StreamError => Err(DeflateError::StreamError),
321            other => unreachable!("set_dictionary does not return {other:?}"),
322        }
323    }
324
325    /// Dynamically updates the compression level.
326    ///
327    /// This can be used to switch between compression levels for different
328    /// kinds of data, or it can be used in conjunction with a call to [`Deflate::reset`]
329    /// to reuse the compressor.
330    ///
331    /// This may return an error if there wasn't enough output space to complete
332    /// the compression of the available input data before changing the
333    /// compression level. Flushing the stream before calling this method
334    /// ensures that the function will succeed on the first call.
335    pub fn set_level(&mut self, level: i32) -> Result<Status, DeflateError> {
336        match crate::deflate::params(&mut self.inner, level, Default::default()) {
337            ReturnCode::Ok => Ok(Status::Ok),
338            ReturnCode::StreamError => Err(DeflateError::StreamError),
339            ReturnCode::BufError => Ok(Status::BufError),
340            other => unreachable!("set_level does not return {other:?}"),
341        }
342    }
343}
344
345impl Drop for Deflate {
346    fn drop(&mut self) {
347        let _ = crate::deflate::end(&mut self.inner);
348    }
349}