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