Coverage Report

Created: 2026-08-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.7/src/stable.rs
Line
Count
Source
1
use core::{ffi::c_uint, mem::MaybeUninit};
2
3
use crate::deflate::DeflateConfig;
4
use crate::inflate::InflateConfig;
5
use crate::ReturnCode;
6
pub 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)]
11
pub 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)]
40
pub 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
52
impl From<InflateError> for ReturnCode {
53
0
    fn from(value: InflateError) -> Self {
54
0
        match value {
55
0
            InflateError::NeedDict { .. } => ReturnCode::NeedDict,
56
0
            InflateError::StreamError => ReturnCode::StreamError,
57
0
            InflateError::DataError => ReturnCode::DataError,
58
0
            InflateError::MemError => ReturnCode::MemError,
59
        }
60
0
    }
61
}
62
63
impl InflateError {
64
0
    pub fn as_str(self) -> &'static str {
65
0
        ReturnCode::from(self).error_message_str()
66
0
    }
67
}
68
69
/// The state that is used to decompress an input.
70
pub struct Inflate {
71
    inner: crate::inflate::InflateStream<'static>,
72
    total_in: u64,
73
    total_out: u64,
74
}
75
76
impl Inflate {
77
    /// The amount of bytes consumed from the input so far.
78
5.42k
    pub fn total_in(&self) -> u64 {
79
5.42k
        self.total_in
80
5.42k
    }
81
82
    /// The amount of decompressed bytes that have been written to the output thus far.
83
5.42k
    pub fn total_out(&self) -> u64 {
84
5.42k
        self.total_out
85
5.42k
    }
86
87
    /// The error message if the previous operation failed.
88
0
    pub fn error_message(&self) -> Option<&'static str> {
89
0
        if self.inner.msg.is_null() {
90
0
            None
91
        } else {
92
0
            unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
93
        }
94
0
    }
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
5.81k
    pub fn new(expect_header: bool, window_bits: u8) -> Self {
121
5.81k
        let config = InflateConfig {
122
5.81k
            window_bits: if expect_header {
123
5.81k
                i32::from(window_bits)
124
            } else {
125
0
                -i32::from(window_bits)
126
            },
127
        };
128
129
5.81k
        Self {
130
5.81k
            inner: crate::inflate::InflateStream::new(config),
131
5.81k
            total_in: 0,
132
5.81k
            total_out: 0,
133
5.81k
        }
134
5.81k
    }
135
136
    /// Reset the state to allow handling a new stream.
137
4.13k
    pub fn reset(&mut self, zlib_header: bool) {
138
4.13k
        let mut config = InflateConfig::default();
139
140
4.13k
        if !zlib_header {
141
0
            config.window_bits = -config.window_bits;
142
4.13k
        }
143
144
4.13k
        self.total_in = 0;
145
4.13k
        self.total_out = 0;
146
147
4.13k
        crate::inflate::reset_with_config(&mut self.inner, config);
148
4.13k
    }
149
150
    /// Decompress `input` and write all decompressed bytes into `output`,
151
    /// with `flush` defining some details about this.
152
4.13k
    pub fn decompress(
153
4.13k
        &mut self,
154
4.13k
        input: &[u8],
155
4.13k
        output: &mut [u8],
156
4.13k
        flush: InflateFlush,
157
4.13k
    ) -> Result<Status, InflateError> {
158
4.13k
        self.decompress_uninit(
159
4.13k
            input,
160
4.13k
            unsafe { &mut *(output as *mut _ as *mut [MaybeUninit<u8>]) },
161
4.13k
            flush,
162
        )
163
4.13k
    }
164
165
    /// Decompress `input` and write all decompressed bytes into a potentially uninitialized `output`,
166
    /// with `flush` defining some details about this.
167
4.13k
    pub fn decompress_uninit(
168
4.13k
        &mut self,
169
4.13k
        input: &[u8],
170
4.13k
        output: &mut [MaybeUninit<u8>],
171
4.13k
        flush: InflateFlush,
172
4.13k
    ) -> 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
4.13k
        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
177
4.13k
        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
4.13k
        self.inner.next_in = input.as_ptr().cast_mut();
181
4.13k
        self.inner.next_out = output.as_mut_ptr().cast();
182
183
4.13k
        let start_in = self.inner.next_in;
184
4.13k
        let start_out = self.inner.next_out;
185
186
        // SAFETY: the inflate state was properly initialized.
187
4.13k
        let ret = unsafe { crate::inflate::inflate(&mut self.inner, flush) };
188
189
4.13k
        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
190
4.13k
        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
191
192
4.13k
        match ret {
193
1.11k
            ReturnCode::Ok => Ok(Status::Ok),
194
169
            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
195
14
            ReturnCode::NeedDict => Err(InflateError::NeedDict {
196
14
                dict_id: self.inner.adler as u32,
197
14
            }),
198
0
            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
199
0
            ReturnCode::StreamError => Err(InflateError::StreamError),
200
2.83k
            ReturnCode::DataError => Err(InflateError::DataError),
201
0
            ReturnCode::MemError => Err(InflateError::MemError),
202
0
            ReturnCode::BufError => Ok(Status::BufError),
203
0
            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
204
        }
205
4.13k
    }
206
207
0
    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, InflateError> {
208
0
        match crate::inflate::set_dictionary(&mut self.inner, dictionary) {
209
0
            ReturnCode::Ok => Ok(self.inner.adler as u32),
210
0
            ReturnCode::StreamError => Err(InflateError::StreamError),
211
0
            ReturnCode::DataError => Err(InflateError::DataError),
212
0
            other => unreachable!("set_dictionary does not return {other:?}"),
213
        }
214
0
    }
215
}
216
217
impl Drop for Inflate {
218
5.81k
    fn drop(&mut self) {
219
5.81k
        let _ = crate::inflate::end(&mut self.inner);
220
5.81k
    }
221
}
222
223
/// Errors that can occur when compressing.
224
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
225
pub 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
235
impl From<DeflateError> for ReturnCode {
236
0
    fn from(value: DeflateError) -> Self {
237
0
        match value {
238
0
            DeflateError::StreamError => ReturnCode::StreamError,
239
0
            DeflateError::DataError => ReturnCode::DataError,
240
0
            DeflateError::MemError => ReturnCode::MemError,
241
        }
242
0
    }
243
}
244
245
impl DeflateError {
246
0
    pub fn as_str(self) -> &'static str {
247
0
        ReturnCode::from(self).error_message_str()
248
0
    }
249
}
250
251
impl From<ReturnCode> for Result<Status, DeflateError> {
252
0
    fn from(value: ReturnCode) -> Self {
253
0
        match value {
254
0
            ReturnCode::Ok => Ok(Status::Ok),
255
0
            ReturnCode::StreamEnd => Ok(Status::StreamEnd),
256
0
            ReturnCode::NeedDict => unreachable!("compression does not use dictionary"),
257
0
            ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
258
0
            ReturnCode::StreamError => Err(DeflateError::StreamError),
259
0
            ReturnCode::DataError => Err(DeflateError::DataError),
260
0
            ReturnCode::MemError => Err(DeflateError::MemError),
261
0
            ReturnCode::BufError => Ok(Status::BufError),
262
0
            ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
263
        }
264
0
    }
265
}
266
267
/// The state that is used to compress an input.
268
pub struct Deflate {
269
    inner: crate::deflate::DeflateStream<'static>,
270
    total_in: u64,
271
    total_out: u64,
272
}
273
274
impl Deflate {
275
    /// The number of bytes that were read from the input.
276
0
    pub fn total_in(&self) -> u64 {
277
0
        self.total_in
278
0
    }
279
280
    /// The number of compressed bytes that were written to the output.
281
0
    pub fn total_out(&self) -> u64 {
282
0
        self.total_out
283
0
    }
284
285
    /// The error message if the previous operation failed.
286
0
    pub fn error_message(&self) -> Option<&'static str> {
287
0
        if self.inner.msg.is_null() {
288
0
            None
289
        } else {
290
0
            unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
291
        }
292
0
    }
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
0
    pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
298
0
        let config = DeflateConfig {
299
0
            window_bits: if zlib_header {
300
0
                i32::from(window_bits)
301
            } else {
302
0
                -i32::from(window_bits)
303
            },
304
0
            level,
305
0
            ..DeflateConfig::default()
306
        };
307
308
0
        Self {
309
0
            inner: crate::deflate::DeflateStream::new(config),
310
0
            total_in: 0,
311
0
            total_out: 0,
312
0
        }
313
0
    }
314
315
    /// Prepare the instance for a new stream.
316
0
    pub fn reset(&mut self) {
317
0
        self.total_in = 0;
318
0
        self.total_out = 0;
319
320
0
        crate::deflate::reset(&mut self.inner);
321
0
    }
322
323
    /// Compress `input` and write compressed bytes to `output`,
324
    /// with `flush` controlling additional characteristics.
325
0
    pub fn compress(
326
0
        &mut self,
327
0
        input: &[u8],
328
0
        output: &mut [u8],
329
0
        flush: DeflateFlush,
330
0
    ) -> Result<Status, DeflateError> {
331
0
        self.compress_uninit(
332
0
            input,
333
0
            unsafe { &mut *(output as *mut _ as *mut [MaybeUninit<u8>]) },
334
0
            flush,
335
        )
336
0
    }
337
338
    /// Compress `input` and write compressed bytes to a potentially uninitialized `output`,
339
    /// with `flush` controlling additional characteristics.
340
0
    pub fn compress_uninit(
341
0
        &mut self,
342
0
        input: &[u8],
343
0
        output: &mut [MaybeUninit<u8>],
344
0
        flush: DeflateFlush,
345
0
    ) -> 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
0
        self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
350
0
        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
0
        self.inner.next_in = input.as_ptr().cast_mut();
354
0
        self.inner.next_out = output.as_mut_ptr().cast();
355
356
0
        let start_in = self.inner.next_in;
357
0
        let start_out = self.inner.next_out;
358
359
0
        let ret = crate::deflate::deflate(&mut self.inner, flush).into();
360
361
0
        self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
362
0
        self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
363
364
        // Clear these pointers so there can be no use after free.
365
0
        self.inner.next_in = core::ptr::null_mut();
366
0
        self.inner.next_out = core::ptr::null_mut();
367
368
0
        self.inner.avail_in = 0;
369
0
        self.inner.avail_out = 0;
370
371
0
        ret
372
0
    }
373
374
    /// Specifies the compression dictionary to use.
375
    ///
376
    /// Returns the Adler-32 checksum of the dictionary.
377
0
    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DeflateError> {
378
0
        match crate::deflate::set_dictionary(&mut self.inner, dictionary) {
379
0
            ReturnCode::Ok => Ok(self.inner.adler as u32),
380
0
            ReturnCode::StreamError => Err(DeflateError::StreamError),
381
0
            other => unreachable!("set_dictionary does not return {other:?}"),
382
        }
383
0
    }
384
385
    /// Dynamically updates the compression level.
386
    ///
387
    /// This can be used to switch between compression levels for different
388
    /// kinds of data, or it can be used in conjunction with a call to [`Deflate::reset`]
389
    /// to reuse the compressor.
390
    ///
391
    /// This may return an error if there wasn't enough output space to complete
392
    /// the compression of the available input data before changing the
393
    /// compression level. Flushing the stream before calling this method
394
    /// ensures that the function will succeed on the first call.
395
0
    pub fn set_level(&mut self, level: i32) -> Result<Status, DeflateError> {
396
        // Clear these pointers so there can be no use after free.
397
0
        self.inner.next_in = core::ptr::null_mut();
398
0
        self.inner.next_out = core::ptr::null_mut();
399
400
0
        self.inner.avail_in = 0;
401
0
        self.inner.avail_out = 0;
402
403
0
        match crate::deflate::params(&mut self.inner, level, Default::default()) {
404
0
            ReturnCode::Ok => Ok(Status::Ok),
405
0
            ReturnCode::StreamError => Err(DeflateError::StreamError),
406
0
            ReturnCode::BufError => Ok(Status::BufError),
407
0
            other => unreachable!("set_level does not return {other:?}"),
408
        }
409
0
    }
410
}
411
412
impl Drop for Deflate {
413
0
    fn drop(&mut self) {
414
0
        let _ = crate::deflate::end(&mut self.inner);
415
0
    }
416
}