Skip to main content

zlib_rs/
stable.rs

1use core::{ffi::c_uint, mem::MaybeUninit};
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. This function allocates, and so it is recommended to re-use this
97    /// state when possible, using [`Inflate::reset`] as needed.
98    ///
99    /// This function will:
100    ///
101    /// - decode a raw deflate stream when `expect_header = false` and `window_bits` is in the
102    ///   range `8..=15`
103    /// - decode a zlib header followed by a deflate stream when `expect_header = true` and
104    ///   `window_bits` is in the range `8..=15`
105    /// - decode a gzip header followed by a deflate stream when `expect_header = true` and
106    ///   `window_bits` is in the range `16 + 8..=16 + 15`
107    /// - decode either a zlib or a gzip header, followed by a deflate stream when
108    ///   `expect_header = true` and `window_bits` is in the range `32 + 8..=32 + 15`
109    ///
110    /// `window_bits` can also be 0 to request that inflate use the window size in the
111    /// zlib header of the compressed stream when using zlib.
112    ///
113    /// Note that when deflating a value of `window_bits = 8` is silently converted to
114    /// `window_bits = 9` in most zlib implementations, and hence should be inflated using
115    /// `window_bits = 9`.
116    ///
117    /// # Panics
118    ///
119    /// This function may panic when the `window_bits` and `expect_header` have values not listed above.
120    pub fn new(expect_header: bool, window_bits: u8) -> Self {
121        let config = InflateConfig {
122            window_bits: if expect_header {
123                i32::from(window_bits)
124            } else {
125                -i32::from(window_bits)
126            },
127        };
128
129        Self {
130            inner: crate::inflate::InflateStream::new(config),
131            total_in: 0,
132            total_out: 0,
133        }
134    }
135
136    /// Reset the state to allow handling a new stream.
137    pub fn reset(&mut self, zlib_header: bool) {
138        let mut config = InflateConfig::default();
139
140        if !zlib_header {
141            config.window_bits = -config.window_bits;
142        }
143
144        self.total_in = 0;
145        self.total_out = 0;
146
147        crate::inflate::reset_with_config(&mut self.inner, config);
148    }
149
150    /// Decompress `input` and write all decompressed bytes into `output`,
151    /// with `flush` defining some details about this.
152    pub fn decompress(
153        &mut self,
154        input: &[u8],
155        output: &mut [u8],
156        flush: InflateFlush,
157    ) -> Result<Status, InflateError> {
158        self.decompress_uninit(
159            input,
160            unsafe { &mut *(output as *mut _ as *mut [MaybeUninit<u8>]) },
161            flush,
162        )
163    }
164
165    /// Decompress `input` and write all decompressed bytes into a potentially uninitialized `output`,
166    /// with `flush` defining some details about this.
167    pub fn decompress_uninit(
168        &mut self,
169        input: &[u8],
170        output: &mut [MaybeUninit<u8>],
171        flush: InflateFlush,
172    ) -> Result<Status, InflateError> {
173        // Limit the length of the input and output to the maximum value of a c_uint. For larger
174        // inputs, this will either complete or signal that more input and output is needed. The
175        // caller should be able to handle this regardless.
176        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
177        self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
178
179        // This cast_mut is unfortunate, that is just how the types are.
180        self.inner.next_in = input.as_ptr().cast_mut();
181        self.inner.next_out = output.as_mut_ptr().cast();
182
183        let start_in = self.inner.next_in;
184        let start_out = self.inner.next_out;
185
186        // SAFETY: the inflate state was properly initialized.
187        let ret = unsafe { crate::inflate::inflate(&mut self.inner, flush) };
188
189        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
190        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
191
192        match ret {
193            ReturnCode::Ok => Ok(Status::Ok),
194            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
195            ReturnCode::NeedDict => Err(InflateError::NeedDict {
196                dict_id: self.inner.adler as u32,
197            }),
198            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
199            ReturnCode::StreamError => Err(InflateError::StreamError),
200            ReturnCode::DataError => Err(InflateError::DataError),
201            ReturnCode::MemError => Err(InflateError::MemError),
202            ReturnCode::BufError => Ok(Status::BufError),
203            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
204        }
205    }
206
207    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, InflateError> {
208        match crate::inflate::set_dictionary(&mut self.inner, dictionary) {
209            ReturnCode::Ok => Ok(self.inner.adler as u32),
210            ReturnCode::StreamError => Err(InflateError::StreamError),
211            ReturnCode::DataError => Err(InflateError::DataError),
212            other => unreachable!("set_dictionary does not return {other:?}"),
213        }
214    }
215}
216
217impl Drop for Inflate {
218    fn drop(&mut self) {
219        let _ = crate::inflate::end(&mut self.inner);
220    }
221}
222
223/// Errors that can occur when compressing.
224#[derive(Copy, Clone, PartialEq, Eq, Debug)]
225pub enum DeflateError {
226    /// The [`Deflate`] is in an inconsistent state, most likely
227    /// due to an invalid configuration parameter.
228    StreamError = -2,
229    /// The input is not a valid deflate stream.
230    DataError = -3,
231    /// A memory allocation failed.
232    MemError = -4,
233}
234
235impl From<DeflateError> for ReturnCode {
236    fn from(value: DeflateError) -> Self {
237        match value {
238            DeflateError::StreamError => ReturnCode::StreamError,
239            DeflateError::DataError => ReturnCode::DataError,
240            DeflateError::MemError => ReturnCode::MemError,
241        }
242    }
243}
244
245impl DeflateError {
246    pub fn as_str(self) -> &'static str {
247        ReturnCode::from(self).error_message_str()
248    }
249}
250
251impl From<ReturnCode> for Result<Status, DeflateError> {
252    fn from(value: ReturnCode) -> Self {
253        match value {
254            ReturnCode::Ok => Ok(Status::Ok),
255            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
256            ReturnCode::NeedDict => unreachable!("compression does not use dictionary"),
257            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
258            ReturnCode::StreamError => Err(DeflateError::StreamError),
259            ReturnCode::DataError => Err(DeflateError::DataError),
260            ReturnCode::MemError => Err(DeflateError::MemError),
261            ReturnCode::BufError => Ok(Status::BufError),
262            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
263        }
264    }
265}
266
267/// The state that is used to compress an input.
268pub struct Deflate {
269    inner: crate::deflate::DeflateStream<'static>,
270    total_in: u64,
271    total_out: u64,
272}
273
274impl Deflate {
275    /// The number of bytes that were read from the input.
276    pub fn total_in(&self) -> u64 {
277        self.total_in
278    }
279
280    /// The number of compressed bytes that were written to the output.
281    pub fn total_out(&self) -> u64 {
282        self.total_out
283    }
284
285    /// The error message if the previous operation failed.
286    pub fn error_message(&self) -> Option<&'static str> {
287        if self.inner.msg.is_null() {
288            None
289        } else {
290            unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
291        }
292    }
293
294    /// Create a new instance - this allocates so should be done with care.
295    ///
296    /// The `window_bits` must be in the range `8..=15`, with `15` being most common.
297    pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
298        let config = DeflateConfig {
299            window_bits: if zlib_header {
300                i32::from(window_bits)
301            } else {
302                -i32::from(window_bits)
303            },
304            level,
305            ..DeflateConfig::default()
306        };
307
308        Self {
309            inner: crate::deflate::DeflateStream::new(config),
310            total_in: 0,
311            total_out: 0,
312        }
313    }
314
315    /// Prepare the instance for a new stream.
316    pub fn reset(&mut self) {
317        self.total_in = 0;
318        self.total_out = 0;
319
320        crate::deflate::reset(&mut self.inner);
321    }
322
323    /// Compress `input` and write compressed bytes to `output`,
324    /// with `flush` controlling additional characteristics.
325    pub fn compress(
326        &mut self,
327        input: &[u8],
328        output: &mut [u8],
329        flush: DeflateFlush,
330    ) -> Result<Status, DeflateError> {
331        self.compress_uninit(
332            input,
333            unsafe { &mut *(output as *mut _ as *mut [MaybeUninit<u8>]) },
334            flush,
335        )
336    }
337
338    /// Compress `input` and write compressed bytes to a potentially uninitialized `output`,
339    /// with `flush` controlling additional characteristics.
340    pub fn compress_uninit(
341        &mut self,
342        input: &[u8],
343        output: &mut [MaybeUninit<u8>],
344        flush: DeflateFlush,
345    ) -> Result<Status, DeflateError> {
346        // Limit the length of the input and output to the maximum value of a c_uint. For larger
347        // inputs, this will either complete or signal that more input and output is needed. The
348        // caller should be able to handle this regardless.
349        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
350        self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
351
352        // This cast_mut is unfortunate, that is just how the types are.
353        self.inner.next_in = input.as_ptr().cast_mut();
354        self.inner.next_out = output.as_mut_ptr().cast();
355
356        let start_in = self.inner.next_in;
357        let start_out = self.inner.next_out;
358
359        let ret = crate::deflate::deflate(&mut self.inner, flush).into();
360
361        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
362        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
363
364        ret
365    }
366
367    /// Specifies the compression dictionary to use.
368    ///
369    /// Returns the Adler-32 checksum of the dictionary.
370    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DeflateError> {
371        match crate::deflate::set_dictionary(&mut self.inner, dictionary) {
372            ReturnCode::Ok => Ok(self.inner.adler as u32),
373            ReturnCode::StreamError => Err(DeflateError::StreamError),
374            other => unreachable!("set_dictionary does not return {other:?}"),
375        }
376    }
377
378    /// Dynamically updates the compression level.
379    ///
380    /// This can be used to switch between compression levels for different
381    /// kinds of data, or it can be used in conjunction with a call to [`Deflate::reset`]
382    /// to reuse the compressor.
383    ///
384    /// This may return an error if there wasn't enough output space to complete
385    /// the compression of the available input data before changing the
386    /// compression level. Flushing the stream before calling this method
387    /// ensures that the function will succeed on the first call.
388    pub fn set_level(&mut self, level: i32) -> Result<Status, DeflateError> {
389        match crate::deflate::params(&mut self.inner, level, Default::default()) {
390            ReturnCode::Ok => Ok(Status::Ok),
391            ReturnCode::StreamError => Err(DeflateError::StreamError),
392            ReturnCode::BufError => Ok(Status::BufError),
393            other => unreachable!("set_level does not return {other:?}"),
394        }
395    }
396}
397
398impl Drop for Deflate {
399    fn drop(&mut self) {
400        let _ = crate::deflate::end(&mut self.inner);
401    }
402}