makepad_miniz/inflate/
mod.rs

1//! This module contains functionality for decompression.
2
3#[cfg(feature = "with-alloc")]
4use crate::alloc::{boxed::Box, vec, vec::Vec};
5use ::core::usize;
6
7pub mod core;
8mod output_buffer;
9//pub mod stream;
10use self::core::*;
11
12const TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS: i32 = -4;
13const TINFL_STATUS_BAD_PARAM: i32 = -3;
14const TINFL_STATUS_ADLER32_MISMATCH: i32 = -2;
15const TINFL_STATUS_FAILED: i32 = -1;
16const TINFL_STATUS_DONE: i32 = 0;
17const TINFL_STATUS_NEEDS_MORE_INPUT: i32 = 1;
18const TINFL_STATUS_HAS_MORE_OUTPUT: i32 = 2;
19
20/// Return status codes.
21#[repr(i8)]
22#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
23pub enum TINFLStatus {
24    /// More input data was expected, but the caller indicated that there was no more data, so the
25    /// input stream is likely truncated.
26    ///
27    /// This can't happen if you have provided the
28    /// [`TINFL_FLAG_HAS_MORE_INPUT`][core::inflate_flags::TINFL_FLAG_HAS_MORE_INPUT] flag to the
29    /// decompression.  By setting that flag, you indicate more input exists but is not provided,
30    /// and so reaching the end of the input data without finding the end of the compressed stream
31    /// would instead return a [`NeedsMoreInput`][Self::NeedsMoreInput] status.
32    FailedCannotMakeProgress = TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS as i8,
33
34    /// The output buffer is an invalid size; consider the `flags` parameter.
35    BadParam = TINFL_STATUS_BAD_PARAM as i8,
36
37    /// The decompression went fine, but the adler32 checksum did not match the one
38    /// provided in the header.
39    Adler32Mismatch = TINFL_STATUS_ADLER32_MISMATCH as i8,
40
41    /// Failed to decompress due to invalid data.
42    Failed = TINFL_STATUS_FAILED as i8,
43
44    /// Finished decompression without issues.
45    ///
46    /// This indicates the end of the compressed stream has been reached.
47    Done = TINFL_STATUS_DONE as i8,
48
49    /// The decompressor needs more input data to continue decompressing.
50    ///
51    /// This occurs when there's no more consumable input, but the end of the stream hasn't been
52    /// reached, and you have supplied the
53    /// [`TINFL_FLAG_HAS_MORE_INPUT`][core::inflate_flags::TINFL_FLAG_HAS_MORE_INPUT] flag to the
54    /// decompressor.  Had you not supplied that flag (which would mean you were asserting that you
55    /// believed all the data was available) you would have gotten a
56    /// [`FailedCannotMakeProcess`][Self::FailedCannotMakeProgress] instead.
57    NeedsMoreInput = TINFL_STATUS_NEEDS_MORE_INPUT as i8,
58
59    /// There is still pending data that didn't fit in the output buffer.
60    HasMoreOutput = TINFL_STATUS_HAS_MORE_OUTPUT as i8,
61}
62
63impl TINFLStatus {
64    pub fn from_i32(value: i32) -> Option<TINFLStatus> {
65        use self::TINFLStatus::*;
66        match value {
67            TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS => Some(FailedCannotMakeProgress),
68            TINFL_STATUS_BAD_PARAM => Some(BadParam),
69            TINFL_STATUS_ADLER32_MISMATCH => Some(Adler32Mismatch),
70            TINFL_STATUS_FAILED => Some(Failed),
71            TINFL_STATUS_DONE => Some(Done),
72            TINFL_STATUS_NEEDS_MORE_INPUT => Some(NeedsMoreInput),
73            TINFL_STATUS_HAS_MORE_OUTPUT => Some(HasMoreOutput),
74            _ => None,
75        }
76    }
77}
78
79/// Decompress the deflate-encoded data in `input` to a vector.
80///
81/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
82#[inline]
83pub fn decompress_to_vec(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
84    decompress_to_vec_inner(input, 0, usize::max_value())
85}
86
87/// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
88///
89/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
90#[inline]
91pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
92    decompress_to_vec_inner(
93        input,
94        inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER,
95        usize::max_value(),
96    )
97}
98
99/// Decompress the deflate-encoded data in `input` to a vector.
100/// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
101/// [`TINFLStatus::HasMoreOutput`] error is returned.
102///
103/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
104#[inline]
105pub fn decompress_to_vec_with_limit(input: &[u8], max_size: usize) -> Result<Vec<u8>, TINFLStatus> {
106    decompress_to_vec_inner(input, 0, max_size)
107}
108
109/// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
110/// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
111/// [`TINFLStatus::HasMoreOutput`] error is returned.
112///
113/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
114#[inline]
115pub fn decompress_to_vec_zlib_with_limit(
116    input: &[u8],
117    max_size: usize,
118) -> Result<Vec<u8>, TINFLStatus> {
119    decompress_to_vec_inner(input, inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER, max_size)
120}
121
122/// Backend of various to-[`Vec`] decompressions.
123///
124/// Returns a tuple of the [`Vec`] of decompressed data and the [status result][TINFLStatus].
125fn decompress_to_vec_inner(
126    input: &[u8],
127    flags: u32,
128    max_output_size: usize,
129) -> Result<Vec<u8>, TINFLStatus> {
130    let flags = flags | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
131    let mut ret: Vec<u8> = vec![0; input.len().saturating_mul(2).min(max_output_size)];
132
133    let mut decomp = Box::<DecompressorOxide>::default();
134
135    let mut in_pos = 0;
136    let mut out_pos = 0;
137    loop {
138        // Wrap the whole output slice so we know we have enough of the
139        // decompressed data for matches.
140        let (status, in_consumed, out_consumed) =
141            decompress(&mut decomp, &input[in_pos..], &mut ret, out_pos, flags);
142        in_pos += in_consumed;
143        out_pos += out_consumed;
144
145        match status {
146            TINFLStatus::Done => {
147                ret.truncate(out_pos);
148                return Ok(ret);
149            }
150
151            TINFLStatus::HasMoreOutput => {
152                // We need more space, so check if we can resize the buffer and do it.
153                let new_len = ret
154                    .len()
155                    .checked_add(out_pos)
156                    .ok_or(TINFLStatus::HasMoreOutput)?;
157                if new_len > max_output_size {
158                    return Err(TINFLStatus::HasMoreOutput);
159                };
160                ret.resize(new_len, 0);
161            }
162
163            _ => return Err(status),
164        }
165    }
166}
167
168/// Decompress one or more source slices from an iterator into the output slice.
169///
170/// * On success, returns the number of bytes that were written.
171/// * On failure, returns the failure status code.
172///
173/// This will fail if the output buffer is not large enough, but in that case
174/// the output buffer will still contain the partial decompression.
175///
176/// * `out` the output buffer.
177/// * `it` the iterator of input slices.
178/// * `zlib_header` if the first slice out of the iterator is expected to have a
179///   Zlib header. Otherwise the slices are assumed to be the deflate data only.
180/// * `ignore_adler32` if the adler32 checksum should be calculated or not.
181pub fn decompress_slice_iter_to_slice<'out, 'inp>(
182    out: &'out mut [u8],
183    it: impl Iterator<Item = &'inp [u8]>,
184    zlib_header: bool,
185    ignore_adler32: bool,
186) -> Result<usize, TINFLStatus> {
187    use self::core::inflate_flags::*;
188
189    let mut it = it.peekable();
190    let r = &mut DecompressorOxide::new();
191    let mut out_pos = 0;
192    while let Some(in_buf) = it.next() {
193        let has_more = it.peek().is_some();
194        let flags = {
195            let mut f = TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
196            if zlib_header {
197                f |= TINFL_FLAG_PARSE_ZLIB_HEADER;
198            }
199            if ignore_adler32 {
200                f |= TINFL_FLAG_IGNORE_ADLER32;
201            }
202            if has_more {
203                f |= TINFL_FLAG_HAS_MORE_INPUT;
204            }
205            f
206        };
207        let (status, _input_read, bytes_written) = decompress(r, in_buf, out, out_pos, flags);
208        out_pos += bytes_written;
209        match status {
210            TINFLStatus::NeedsMoreInput => continue,
211            TINFLStatus::Done => return Ok(out_pos),
212            e => return Err(e),
213        }
214    }
215    // If we ran out of source slices without getting a `Done` from the
216    // decompression we can call it a failure.
217    Err(TINFLStatus::FailedCannotMakeProgress)
218}