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(crate::inflate::InflateStream<'static>);
71
72impl Inflate {
73    /// The amount of bytes consumed from the input so far.
74    pub fn total_in(&self) -> u64 {
75        #[allow(clippy::useless_conversion)]
76        u64::from(self.0.total_in)
77    }
78
79    /// The amount of decompressed bytes that have been written to the output thus far.
80    pub fn total_out(&self) -> u64 {
81        #[allow(clippy::useless_conversion)]
82        u64::from(self.0.total_out)
83    }
84
85    /// The error message if the previous operation failed.
86    pub fn error_message(&self) -> Option<&'static str> {
87        if self.0.msg.is_null() {
88            None
89        } else {
90            unsafe { core::ffi::CStr::from_ptr(self.0.msg).to_str() }.ok()
91        }
92    }
93
94    /// Create a new instance. Note that it allocates in various ways and thus should be re-used.
95    ///
96    /// The `window_bits` must be in the range `8..=15`, with `15` being most common.
97    pub fn new(zlib_header: bool, window_bits: u8) -> Self {
98        let config = InflateConfig {
99            window_bits: if zlib_header {
100                i32::from(window_bits)
101            } else {
102                -i32::from(window_bits)
103            },
104        };
105
106        Self(crate::inflate::InflateStream::new(config))
107    }
108
109    /// Reset the state to allow handling a new stream.
110    pub fn reset(&mut self, zlib_header: bool) {
111        let mut config = InflateConfig::default();
112
113        if !zlib_header {
114            config.window_bits = -config.window_bits;
115        }
116
117        crate::inflate::reset_with_config(&mut self.0, config);
118    }
119
120    /// Decompress `input` and write all decompressed bytes into `output`, with `flush` defining some details about this.
121    pub fn decompress(
122        &mut self,
123        input: &[u8],
124        output: &mut [u8],
125        flush: InflateFlush,
126    ) -> Result<Status, InflateError> {
127        // Limit the length of the input and output to the maximum value of a c_uint. For larger
128        // inputs, this will either complete or signal that more input and output is needed. The
129        // caller should be able to handle this regardless.
130        self.0.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
131        self.0.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
132
133        // This cast_mut is unfortunate, that is just how the types are.
134        self.0.next_in = input.as_ptr().cast_mut();
135        self.0.next_out = output.as_mut_ptr();
136
137        // SAFETY: the inflate state was properly initialized.
138        match unsafe { crate::inflate::inflate(&mut self.0, flush) } {
139            ReturnCode::Ok => Ok(Status::Ok),
140            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
141            ReturnCode::NeedDict => Err(InflateError::NeedDict {
142                dict_id: self.0.adler as u32,
143            }),
144            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
145            ReturnCode::StreamError => Err(InflateError::StreamError),
146            ReturnCode::DataError => Err(InflateError::DataError),
147            ReturnCode::MemError => Err(InflateError::MemError),
148            ReturnCode::BufError => Ok(Status::BufError),
149            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
150        }
151    }
152
153    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, InflateError> {
154        match crate::inflate::set_dictionary(&mut self.0, dictionary) {
155            ReturnCode::Ok => Ok(self.0.adler as u32),
156            ReturnCode::StreamError => Err(InflateError::StreamError),
157            ReturnCode::DataError => Err(InflateError::DataError),
158            other => unreachable!("set_dictionary does not return {other:?}"),
159        }
160    }
161}
162
163impl Drop for Inflate {
164    fn drop(&mut self) {
165        let _ = crate::inflate::end(&mut self.0);
166    }
167}
168
169/// Errors that can occur when compressing.
170#[derive(Copy, Clone, PartialEq, Eq, Debug)]
171pub enum DeflateError {
172    /// The [`Deflate`] is in an inconsistent state, most likely
173    /// due to an invalid configuration parameter.
174    StreamError = -2,
175    /// The input is not a valid deflate stream.
176    DataError = -3,
177    /// A memory allocation failed.
178    MemError = -4,
179}
180
181impl From<DeflateError> for ReturnCode {
182    fn from(value: DeflateError) -> Self {
183        match value {
184            DeflateError::StreamError => ReturnCode::StreamError,
185            DeflateError::DataError => ReturnCode::DataError,
186            DeflateError::MemError => ReturnCode::MemError,
187        }
188    }
189}
190
191impl DeflateError {
192    pub fn as_str(self) -> &'static str {
193        ReturnCode::from(self).error_message_str()
194    }
195}
196
197impl From<ReturnCode> for Result<Status, DeflateError> {
198    fn from(value: ReturnCode) -> Self {
199        match value {
200            ReturnCode::Ok => Ok(Status::Ok),
201            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
202            ReturnCode::NeedDict => unreachable!("compression does not use dictionary"),
203            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
204            ReturnCode::StreamError => Err(DeflateError::StreamError),
205            ReturnCode::DataError => Err(DeflateError::DataError),
206            ReturnCode::MemError => Err(DeflateError::MemError),
207            ReturnCode::BufError => Ok(Status::BufError),
208            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
209        }
210    }
211}
212
213/// The state that is used to compress an input.
214pub struct Deflate(crate::deflate::DeflateStream<'static>);
215
216impl Deflate {
217    /// The number of bytes that were read from the input.
218    pub fn total_in(&self) -> u64 {
219        #[allow(clippy::useless_conversion)]
220        u64::from(self.0.total_in)
221    }
222
223    /// The number of compressed bytes that were written to the output.
224    pub fn total_out(&self) -> u64 {
225        #[allow(clippy::useless_conversion)]
226        u64::from(self.0.total_out)
227    }
228
229    /// The error message if the previous operation failed.
230    pub fn error_message(&self) -> Option<&'static str> {
231        if self.0.msg.is_null() {
232            None
233        } else {
234            unsafe { core::ffi::CStr::from_ptr(self.0.msg).to_str() }.ok()
235        }
236    }
237
238    /// Create a new instance - this allocates so should be done with care.
239    ///
240    /// The `window_bits` must be in the range `8..=15`, with `15` being most common.
241    pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
242        let config = DeflateConfig {
243            window_bits: if zlib_header {
244                i32::from(window_bits)
245            } else {
246                -i32::from(window_bits)
247            },
248            level,
249            ..DeflateConfig::default()
250        };
251
252        Self(crate::deflate::DeflateStream::new(config))
253    }
254
255    /// Prepare the instance for a new stream.
256    pub fn reset(&mut self) {
257        crate::deflate::reset(&mut self.0);
258    }
259
260    /// Compress `input` and write compressed bytes to `output`, with `flush` controlling additional characteristics.
261    pub fn compress(
262        &mut self,
263        input: &[u8],
264        output: &mut [u8],
265        flush: DeflateFlush,
266    ) -> Result<Status, DeflateError> {
267        // Limit the length of the input and output to the maximum value of a c_uint. For larger
268        // inputs, this will either complete or signal that more input and output is needed. The
269        // caller should be able to handle this regardless.
270        self.0.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
271        self.0.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
272
273        // This cast_mut is unfortunate, that is just how the types are.
274        self.0.next_in = input.as_ptr().cast_mut();
275        self.0.next_out = output.as_mut_ptr();
276
277        crate::deflate::deflate(&mut self.0, flush).into()
278    }
279
280    /// Specifies the compression dictionary to use.
281    ///
282    /// Returns the Adler-32 checksum of the dictionary.
283    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DeflateError> {
284        match crate::deflate::set_dictionary(&mut self.0, dictionary) {
285            ReturnCode::Ok => Ok(self.0.adler as u32),
286            ReturnCode::StreamError => Err(DeflateError::StreamError),
287            other => unreachable!("set_dictionary does not return {other:?}"),
288        }
289    }
290
291    /// Dynamically updates the compression level.
292    ///
293    /// This can be used to switch between compression levels for different
294    /// kinds of data, or it can be used in conjunction with a call to [`Deflate::reset`]
295    /// to reuse the compressor.
296    ///
297    /// This may return an error if there wasn't enough output space to complete
298    /// the compression of the available input data before changing the
299    /// compression level. Flushing the stream before calling this method
300    /// ensures that the function will succeed on the first call.
301    pub fn set_level(&mut self, level: i32) -> Result<Status, DeflateError> {
302        match crate::deflate::params(&mut self.0, level, Default::default()) {
303            ReturnCode::Ok => Ok(Status::Ok),
304            ReturnCode::StreamError => Err(DeflateError::StreamError),
305            ReturnCode::BufError => Ok(Status::BufError),
306            other => unreachable!("set_level does not return {other:?}"),
307        }
308    }
309}
310
311impl Drop for Deflate {
312    fn drop(&mut self) {
313        let _ = crate::deflate::end(&mut self.0);
314    }
315}