1 #![allow(dead_code)]
2 
3 use libc::*;
4 use std::{ptr, slice, usize};
5 use std::io::Cursor;
6 use miniz_oxide::inflate::TINFLStatus;
7 pub use miniz_oxide::inflate::core::{decompress, inflate_flags};
8 pub use miniz_oxide::inflate::core::DecompressorOxide as tinfl_decompressor;
9 
10 pub const TINFL_DECOMPRESS_MEM_TO_MEM_FAILED: size_t = usize::MAX;
11 
12 unmangle!(
13 pub unsafe extern "C" fn tinfl_decompress(
14     r: *mut tinfl_decompressor,
15     in_buf: *const u8,
16     in_buf_size: *mut usize,
17     out_buf_start: *mut u8,
18     out_buf_next: *mut u8,
19     out_buf_size: *mut usize,
20     flags: u32,
21 ) -> i32 {
22     let next_pos = out_buf_next as usize - out_buf_start as usize;
23     let out_size = *out_buf_size + next_pos;
24     let mut out_cursor = Cursor::new(slice::from_raw_parts_mut(out_buf_start, out_size));
25     out_cursor.set_position(next_pos as u64);
26     let (status, in_consumed, out_consumed) = decompress(
27         r.as_mut().expect("bad decompressor pointer"),
28         slice::from_raw_parts(in_buf, *in_buf_size),
29         &mut out_cursor,
30         flags,
31     );
32 
33     *in_buf_size = in_consumed;
34     *out_buf_size = out_consumed;
35     status as i32
36 }
37 
38 pub unsafe extern "C" fn tinfl_decompress_mem_to_mem(
39     p_out_buf: *mut c_void,
40     out_buf_len: size_t,
41     p_src_buf: *const c_void,
42     src_buf_len: size_t,
43     flags: c_int,
44 ) -> size_t {
45     let flags = flags as u32;
46     let mut decomp = tinfl_decompressor::with_init_state_only();
47 
48     let (status, _, out_consumed) =
49         decompress(
50             &mut decomp,
51             slice::from_raw_parts(p_src_buf as *const u8, src_buf_len),
52             &mut Cursor::new(slice::from_raw_parts_mut(p_out_buf as *mut u8, out_buf_len)),
53             ((flags & !inflate_flags::TINFL_FLAG_HAS_MORE_INPUT) | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF),
54         );
55 
56     if status != TINFLStatus::Done {
57         TINFL_DECOMPRESS_MEM_TO_MEM_FAILED as size_t
58     } else {
59         out_consumed
60     }
61 }
62 
63 /// Decompress data from `p_src_buf` to a continuously growing heap-allocated buffer.
64 ///
65 /// Sets `p_out_len` to the length of the returned buffer.
66 /// Returns `ptr::null()` if decompression or allocation fails.
67 /// The buffer should be freed with `miniz_def_free_func`.
68 pub unsafe extern "C" fn tinfl_decompress_mem_to_heap(
69     p_src_buf: *const c_void,
70     src_buf_len: size_t,
71     p_out_len: *mut size_t,
72     flags: c_int,
73 ) -> *mut c_void {
74     let flags = flags as u32;
75     const MIN_BUFFER_CAPACITY: size_t = 128;
76 
77     // We're not using a Vec for the buffer here to make sure the buffer is allocated and freed by
78     // the same allocator.
79 
80     let mut decomp = tinfl_decompressor::with_init_state_only();
81     // Pointer to the buffer to place the decompressed data into.
82     let mut p_buf: *mut c_void = ptr::null_mut();
83     // Capacity of the current output buffer.
84     let mut out_buf_capacity = 0;
85 
86     *p_out_len = 0;
87     // How far into the source buffer we have read.
88     let mut src_buf_ofs = 0;
89     loop {
90         let mut out_cur = Cursor::new(slice::from_raw_parts_mut(
91             p_buf as *mut u8,
92             out_buf_capacity,
93         ));
94         out_cur.set_position(*p_out_len as u64);
95         let (status, in_consumed, out_consumed) =
96             decompress(
97                 &mut decomp,
98                 slice::from_raw_parts(
99                     p_src_buf.offset(src_buf_ofs as isize) as *const u8,
100                     src_buf_len - src_buf_ofs,
101                 ),
102                 &mut out_cur,
103                 ((flags & !inflate_flags::TINFL_FLAG_HAS_MORE_INPUT) |
104                  inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF),
105             );
106 
107         // If decompression fails or we don't have any input, bail out.
108         if (status as i32) < 0 || status == TINFLStatus::NeedsMoreInput {
109             ::miniz_def_free_func(ptr::null_mut(), p_buf);
110             *p_out_len = 0;
111             return ptr::null_mut();
112         }
113 
114         src_buf_ofs += in_consumed;
115         *p_out_len += out_consumed;
116 
117         if status == TINFLStatus::Done {
118             break;
119         }
120 
121         // If we need more space, double the capacity of the output buffer
122         // and keep going.
123         let mut new_out_buf_capacity = out_buf_capacity * 2;
124 
125         // Try to get at least 128 bytes of buffer capacity.
126         if new_out_buf_capacity < MIN_BUFFER_CAPACITY {
127             new_out_buf_capacity = MIN_BUFFER_CAPACITY
128         }
129 
130         let p_new_buf = ::miniz_def_realloc_func(ptr::null_mut(), p_buf, 1, new_out_buf_capacity);
131         // Bail out if growing fails.
132         if p_new_buf.is_null() {
133             ::miniz_def_free_func(ptr::null_mut(), p_buf);
134             *p_out_len = 0;
135             return ptr::null_mut();
136         }
137 
138         // Otherwise, continue using the reallocated buffer.
139         p_buf = p_new_buf;
140         out_buf_capacity = new_out_buf_capacity;
141     }
142 
143     p_buf
144 }
145 );
146 
147 #[cfg(test)]
148 mod test {
149     use miniz_oxide::inflate::core::inflate_flags::{
150         TINFL_FLAG_COMPUTE_ADLER32,
151         TINFL_FLAG_PARSE_ZLIB_HEADER,
152     };
153 
154     use super::*;
155     use libc::c_void;
156     use std::{ops, slice};
157     /// Safe wrapper for `tinfl_decompress_mem_to_mem` using slices.
158     ///
159     /// Could maybe make this public later.
tinfl_decompress_mem_to_mem_wrapper( source: &mut [u8], dest: &mut [u8], flags: i32, ) -> Option<usize>160     fn tinfl_decompress_mem_to_mem_wrapper(
161         source: &mut [u8],
162         dest: &mut [u8],
163         flags: i32,
164     ) -> Option<usize> {
165         let status = unsafe {
166             let source_len = source.len();
167             let dest_len = dest.len();
168             tinfl_decompress_mem_to_mem(
169                 dest.as_mut_ptr() as *mut c_void,
170                 dest_len,
171                 source.as_mut_ptr() as *const c_void,
172                 source_len,
173                 flags,
174             )
175         };
176         if status != TINFL_DECOMPRESS_MEM_TO_MEM_FAILED {
177             Some(status)
178         } else {
179             None
180         }
181     }
182 
183     /// Safe wrapper around a buffer allocated with the miniz_def functions.
184     pub struct TinflHeapBuf {
185         buf: *mut c_void,
186         len: size_t,
187     }
188 
189     impl TinflHeapBuf {
as_slice(&self) -> &[u8]190         fn as_slice(&self) -> &[u8] {
191             unsafe { slice::from_raw_parts(self.buf as *const u8, self.len) }
192         }
193     }
194 
195     impl ops::Drop for TinflHeapBuf {
drop(&mut self)196         fn drop(&mut self) {
197             unsafe {
198                 ::miniz_def_free_func(ptr::null_mut(), self.buf);
199             }
200         }
201     }
202 
203     /// Safe wrapper for `tinfl_decompress_mem_to_heap` using slices.
204     ///
205     /// Could maybe make something like this public later.
tinfl_decompress_mem_to_heap_wrapper(source: &mut [u8], flags: i32) -> Option<TinflHeapBuf>206     fn tinfl_decompress_mem_to_heap_wrapper(source: &mut [u8], flags: i32) -> Option<TinflHeapBuf> {
207         let source_len = source.len();
208         let mut out_len = 0;
209         unsafe {
210             let buf_ptr = tinfl_decompress_mem_to_heap(
211                 source.as_ptr() as *const c_void,
212                 source_len,
213                 &mut out_len,
214                 flags,
215             );
216             if !buf_ptr.is_null() {
217                 Some(TinflHeapBuf {
218                     buf: buf_ptr,
219                     len: out_len,
220                 })
221             } else {
222                 None
223             }
224         }
225     }
226 
227     #[test]
mem_to_mem()228     fn mem_to_mem() {
229         let mut encoded = [
230             120, 156, 243, 72, 205, 201, 201, 215, 81, 168,
231             202, 201,  76, 82,   4,   0,  27, 101,  4,  19,
232         ];
233         let mut out_buf = vec![0; 50];
234         let flags = TINFL_FLAG_COMPUTE_ADLER32 | TINFL_FLAG_PARSE_ZLIB_HEADER;
235         let size = tinfl_decompress_mem_to_mem_wrapper(
236             &mut encoded[..],
237             out_buf.as_mut_slice(),
238             flags as i32,
239         ).unwrap();
240         assert_eq!(&out_buf[..size], &b"Hello, zlib!"[..]);
241     }
242 
243     #[test]
mem_to_heap()244     fn mem_to_heap() {
245         let mut encoded = [
246             120, 156, 243, 72, 205, 201, 201, 215, 81, 168,
247             202, 201,  76, 82,   4,   0,  27, 101,  4,  19,
248         ];
249         let flags = TINFL_FLAG_COMPUTE_ADLER32 | TINFL_FLAG_PARSE_ZLIB_HEADER;
250         let out_buf = tinfl_decompress_mem_to_heap_wrapper(&mut encoded[..], flags as i32).unwrap();
251         assert_eq!(out_buf.as_slice(), &b"Hello, zlib!"[..]);
252     }
253 }
254