]> git.proxmox.com Git - cargo.git/blob - vendor/miniz_oxide/src/inflate/mod.rs
New upstream version 0.52.0
[cargo.git] / vendor / miniz_oxide / src / inflate / mod.rs
1 //! This module contains functionality for decompression.
2
3 use crate::alloc::boxed::Box;
4 use crate::alloc::vec;
5 use crate::alloc::vec::Vec;
6 use ::core::cmp::min;
7 use ::core::usize;
8
9 pub mod core;
10 mod output_buffer;
11 pub mod stream;
12 use self::core::*;
13
14 const TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS: i32 = -4;
15 const TINFL_STATUS_BAD_PARAM: i32 = -3;
16 const TINFL_STATUS_ADLER32_MISMATCH: i32 = -2;
17 const TINFL_STATUS_FAILED: i32 = -1;
18 const TINFL_STATUS_DONE: i32 = 0;
19 const TINFL_STATUS_NEEDS_MORE_INPUT: i32 = 1;
20 const TINFL_STATUS_HAS_MORE_OUTPUT: i32 = 2;
21
22 /// Return status codes.
23 #[repr(i8)]
24 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
25 pub enum TINFLStatus {
26 /// More input data was expected, but the caller indicated that there was more data, so the
27 /// input stream is likely truncated.
28 FailedCannotMakeProgress = TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS as i8,
29 /// One or more of the input parameters were invalid.
30 BadParam = TINFL_STATUS_BAD_PARAM as i8,
31 /// The decompression went fine, but the adler32 checksum did not match the one
32 /// provided in the header.
33 Adler32Mismatch = TINFL_STATUS_ADLER32_MISMATCH as i8,
34 /// Failed to decompress due to invalid data.
35 Failed = TINFL_STATUS_FAILED as i8,
36 /// Finished decomression without issues.
37 Done = TINFL_STATUS_DONE as i8,
38 /// The decompressor needs more input data to continue decompressing.
39 NeedsMoreInput = TINFL_STATUS_NEEDS_MORE_INPUT as i8,
40 /// There is still pending data that didn't fit in the output buffer.
41 HasMoreOutput = TINFL_STATUS_HAS_MORE_OUTPUT as i8,
42 }
43
44 impl TINFLStatus {
45 pub fn from_i32(value: i32) -> Option<TINFLStatus> {
46 use self::TINFLStatus::*;
47 match value {
48 TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS => Some(FailedCannotMakeProgress),
49 TINFL_STATUS_BAD_PARAM => Some(BadParam),
50 TINFL_STATUS_ADLER32_MISMATCH => Some(Adler32Mismatch),
51 TINFL_STATUS_FAILED => Some(Failed),
52 TINFL_STATUS_DONE => Some(Done),
53 TINFL_STATUS_NEEDS_MORE_INPUT => Some(NeedsMoreInput),
54 TINFL_STATUS_HAS_MORE_OUTPUT => Some(HasMoreOutput),
55 _ => None,
56 }
57 }
58 }
59
60 /// Decompress the deflate-encoded data in `input` to a vector.
61 ///
62 /// Returns a status and an integer representing where the decompressor failed on failure.
63 #[inline]
64 pub fn decompress_to_vec(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
65 decompress_to_vec_inner(input, 0, usize::max_value())
66 }
67
68 /// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
69 ///
70 /// Returns a status and an integer representing where the decompressor failed on failure.
71 #[inline]
72 pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
73 decompress_to_vec_inner(
74 input,
75 inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER,
76 usize::max_value(),
77 )
78 }
79
80 /// Decompress the deflate-encoded data in `input` to a vector.
81 /// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
82 /// `TINFLStatus::HasMoreOutput` error is returned.
83 ///
84 /// Returns a status and an integer representing where the decompressor failed on failure.
85 #[inline]
86 pub fn decompress_to_vec_with_limit(input: &[u8], max_size: usize) -> Result<Vec<u8>, TINFLStatus> {
87 decompress_to_vec_inner(input, 0, max_size)
88 }
89
90 /// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
91 /// The vector is grown to at most `max_size` bytes; if the data does not fit in that size,
92 /// `TINFLStatus::HasMoreOutput` error is returned.
93 ///
94 /// Returns a status and an integer representing where the decompressor failed on failure.
95 #[inline]
96 pub fn decompress_to_vec_zlib_with_limit(
97 input: &[u8],
98 max_size: usize,
99 ) -> Result<Vec<u8>, TINFLStatus> {
100 decompress_to_vec_inner(input, inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER, max_size)
101 }
102
103 fn decompress_to_vec_inner(
104 input: &[u8],
105 flags: u32,
106 max_output_size: usize,
107 ) -> Result<Vec<u8>, TINFLStatus> {
108 let flags = flags | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
109 let mut ret: Vec<u8> = vec![0; min(input.len().saturating_mul(2), max_output_size)];
110
111 let mut decomp = Box::<DecompressorOxide>::default();
112
113 let mut in_pos = 0;
114 let mut out_pos = 0;
115 loop {
116 // Wrap the whole output slice so we know we have enough of the
117 // decompressed data for matches.
118 let (status, in_consumed, out_consumed) =
119 decompress(&mut decomp, &input[in_pos..], &mut ret, out_pos, flags);
120 in_pos += in_consumed;
121 out_pos += out_consumed;
122
123 match status {
124 TINFLStatus::Done => {
125 ret.truncate(out_pos);
126 return Ok(ret);
127 }
128
129 TINFLStatus::HasMoreOutput => {
130 // We need more space, so check if we can resize the buffer and do it.
131 let new_len = ret
132 .len()
133 .checked_add(out_pos)
134 .ok_or(TINFLStatus::HasMoreOutput)?;
135 if new_len > max_output_size {
136 return Err(TINFLStatus::HasMoreOutput);
137 };
138 ret.resize(new_len, 0);
139 }
140
141 _ => return Err(status),
142 }
143 }
144 }
145
146 #[cfg(test)]
147 mod test {
148 use super::TINFLStatus;
149 use super::{decompress_to_vec_zlib, decompress_to_vec_zlib_with_limit};
150 const encoded: [u8; 20] = [
151 120, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4, 19,
152 ];
153
154 #[test]
155 fn decompress_vec() {
156 let res = decompress_to_vec_zlib(&encoded[..]).unwrap();
157 assert_eq!(res.as_slice(), &b"Hello, zlib!"[..]);
158 }
159
160 #[test]
161 fn decompress_vec_with_high_limit() {
162 let res = decompress_to_vec_zlib_with_limit(&encoded[..], 100_000).unwrap();
163 assert_eq!(res.as_slice(), &b"Hello, zlib!"[..]);
164 }
165
166 #[test]
167 fn fail_to_decompress_with_limit() {
168 let res = decompress_to_vec_zlib_with_limit(&encoded[..], 8);
169 match res {
170 Err(TINFLStatus::HasMoreOutput) => (), // expected result
171 _ => panic!("Decompression output size limit was not enforced"),
172 }
173 }
174 }