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}