miniz_oxide/inflate/
mod.rs

1//! This module contains functionality for decompression.
2
3use std::io::Cursor;
4use std::usize;
5
6pub mod core;
7mod output_buffer;
8pub mod stream;
9use self::core::*;
10
11const TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS: i32 = -4;
12const TINFL_STATUS_BAD_PARAM: i32 = -3;
13const TINFL_STATUS_ADLER32_MISMATCH: i32 = -2;
14const TINFL_STATUS_FAILED: i32 = -1;
15const TINFL_STATUS_DONE: i32 = 0;
16const TINFL_STATUS_NEEDS_MORE_INPUT: i32 = 1;
17const TINFL_STATUS_HAS_MORE_OUTPUT: i32 = 2;
18
19/// Return status codes.
20#[repr(i8)]
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22pub enum TINFLStatus {
23    /// More input data was expected, but the caller indicated that there was more data, so the
24    /// input stream is likely truncated.
25    FailedCannotMakeProgress = TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS as i8,
26    /// One or more of the input parameters were invalid.
27    BadParam = TINFL_STATUS_BAD_PARAM as i8,
28    /// The decompression went fine, but the adler32 checksum did not match the one
29    /// provided in the header.
30    Adler32Mismatch = TINFL_STATUS_ADLER32_MISMATCH as i8,
31    /// Failed to decompress due to invalid data.
32    Failed = TINFL_STATUS_FAILED as i8,
33    /// Finished decomression without issues.
34    Done = TINFL_STATUS_DONE as i8,
35    /// The decompressor needs more input data to continue decompressing.
36    NeedsMoreInput = TINFL_STATUS_NEEDS_MORE_INPUT as i8,
37    /// There is still pending data that didn't fit in the output buffer.
38    HasMoreOutput = TINFL_STATUS_HAS_MORE_OUTPUT as i8,
39}
40
41impl TINFLStatus {
42    pub fn from_i32(value: i32) -> Option<TINFLStatus> {
43        use self::TINFLStatus::*;
44        match value {
45            TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS => Some(FailedCannotMakeProgress),
46            TINFL_STATUS_BAD_PARAM => Some(BadParam),
47            TINFL_STATUS_ADLER32_MISMATCH => Some(Adler32Mismatch),
48            TINFL_STATUS_FAILED => Some(Failed),
49            TINFL_STATUS_DONE => Some(Done),
50            TINFL_STATUS_NEEDS_MORE_INPUT => Some(NeedsMoreInput),
51            TINFL_STATUS_HAS_MORE_OUTPUT => Some(HasMoreOutput),
52            _ => None,
53        }
54    }
55}
56
57/// Decompress the deflate-encoded data in `input` to a vector.
58///
59/// Returns a status and an integer representing where the decompressor failed on failure.
60#[inline]
61pub fn decompress_to_vec(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
62    decompress_to_vec_inner(input, 0)
63}
64
65/// Decompress the deflate-encoded data (with a zlib wrapper) in `input` to a vector.
66///
67/// Returns a status and an integer representing where the decompressor failed on failure.
68#[inline]
69pub fn decompress_to_vec_zlib(input: &[u8]) -> Result<Vec<u8>, TINFLStatus> {
70    decompress_to_vec_inner(input, inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER)
71}
72
73fn decompress_to_vec_inner(input: &[u8], flags: u32) -> Result<Vec<u8>, TINFLStatus> {
74    let flags = flags | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
75    let mut ret: Vec<u8> = vec![0; input.len() * 2];
76
77    let mut decomp = Box::<DecompressorOxide>::default();
78
79    let mut in_pos = 0;
80    let mut out_pos = 0;
81    loop {
82        let (status, in_consumed, out_consumed) = {
83            // Wrap the whole output slice so we know we have enough of the
84            // decompressed data for matches.
85            let mut c = Cursor::new(ret.as_mut_slice());
86            c.set_position(out_pos as u64);
87            decompress(&mut decomp, &input[in_pos..], &mut c, flags)
88        };
89        in_pos += in_consumed;
90        out_pos += out_consumed;
91
92        match status {
93            TINFLStatus::Done => {
94                ret.truncate(out_pos);
95                return Ok(ret);
96            }
97
98            TINFLStatus::HasMoreOutput => {
99                // We need more space so resize the buffer.
100                ret.resize(ret.len() + out_pos, 0);
101            }
102
103            _ => return Err(status),
104        }
105    }
106}
107
108#[cfg(test)]
109mod test {
110    use super::decompress_to_vec_zlib;
111
112    #[test]
113    fn decompress_vec() {
114        let encoded = [
115            120, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4, 19,
116        ];
117        let res = decompress_to_vec_zlib(&encoded[..]).unwrap();
118        assert_eq!(res.as_slice(), &b"Hello, zlib!"[..]);
119    }
120}