| #![allow(dead_code)] |
| |
| use libc::*; |
| use std::{ptr, slice, usize}; |
| use std::io::Cursor; |
| use miniz_oxide::inflate::TINFLStatus; |
| pub use miniz_oxide::inflate::core::{decompress, inflate_flags}; |
| pub use miniz_oxide::inflate::core::DecompressorOxide as tinfl_decompressor; |
| |
| pub const TINFL_DECOMPRESS_MEM_TO_MEM_FAILED: size_t = usize::MAX; |
| |
| unmangle!( |
| pub unsafe extern "C" fn tinfl_decompress( |
| r: *mut tinfl_decompressor, |
| in_buf: *const u8, |
| in_buf_size: *mut usize, |
| out_buf_start: *mut u8, |
| out_buf_next: *mut u8, |
| out_buf_size: *mut usize, |
| flags: u32, |
| ) -> i32 { |
| let next_pos = out_buf_next as usize - out_buf_start as usize; |
| let out_size = *out_buf_size + next_pos; |
| let mut out_cursor = Cursor::new(slice::from_raw_parts_mut(out_buf_start, out_size)); |
| out_cursor.set_position(next_pos as u64); |
| let (status, in_consumed, out_consumed) = decompress( |
| r.as_mut().expect("bad decompressor pointer"), |
| slice::from_raw_parts(in_buf, *in_buf_size), |
| &mut out_cursor, |
| flags, |
| ); |
| |
| *in_buf_size = in_consumed; |
| *out_buf_size = out_consumed; |
| status as i32 |
| } |
| |
| pub unsafe extern "C" fn tinfl_decompress_mem_to_mem( |
| p_out_buf: *mut c_void, |
| out_buf_len: size_t, |
| p_src_buf: *const c_void, |
| src_buf_len: size_t, |
| flags: c_int, |
| ) -> size_t { |
| let flags = flags as u32; |
| let mut decomp = tinfl_decompressor::with_init_state_only(); |
| |
| let (status, _, out_consumed) = |
| decompress( |
| &mut decomp, |
| slice::from_raw_parts(p_src_buf as *const u8, src_buf_len), |
| &mut Cursor::new(slice::from_raw_parts_mut(p_out_buf as *mut u8, out_buf_len)), |
| (flags & !inflate_flags::TINFL_FLAG_HAS_MORE_INPUT) | inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF, |
| ); |
| |
| if status != TINFLStatus::Done { |
| TINFL_DECOMPRESS_MEM_TO_MEM_FAILED as size_t |
| } else { |
| out_consumed |
| } |
| } |
| |
| /// Decompress data from `p_src_buf` to a continuously growing heap-allocated buffer. |
| /// |
| /// Sets `p_out_len` to the length of the returned buffer. |
| /// Returns `ptr::null()` if decompression or allocation fails. |
| /// The buffer should be freed with `miniz_def_free_func`. |
| pub unsafe extern "C" fn tinfl_decompress_mem_to_heap( |
| p_src_buf: *const c_void, |
| src_buf_len: size_t, |
| p_out_len: *mut size_t, |
| flags: c_int, |
| ) -> *mut c_void { |
| let flags = flags as u32; |
| const MIN_BUFFER_CAPACITY: size_t = 128; |
| |
| // We're not using a Vec for the buffer here to make sure the buffer is allocated and freed by |
| // the same allocator. |
| |
| let mut decomp = tinfl_decompressor::with_init_state_only(); |
| // Pointer to the buffer to place the decompressed data into. |
| let mut p_buf: *mut c_void = ::miniz_def_alloc_func(ptr::null_mut(), MIN_BUFFER_CAPACITY, 1); |
| // Capacity of the current output buffer. |
| let mut out_buf_capacity = MIN_BUFFER_CAPACITY; |
| |
| *p_out_len = 0; |
| // How far into the source buffer we have read. |
| let mut src_buf_ofs = 0; |
| loop { |
| let mut out_cur = Cursor::new(slice::from_raw_parts_mut( |
| p_buf as *mut u8, |
| out_buf_capacity, |
| )); |
| out_cur.set_position(*p_out_len as u64); |
| let (status, in_consumed, out_consumed) = |
| decompress( |
| &mut decomp, |
| slice::from_raw_parts( |
| p_src_buf.offset(src_buf_ofs as isize) as *const u8, |
| src_buf_len - src_buf_ofs, |
| ), |
| &mut out_cur, |
| (flags & !inflate_flags::TINFL_FLAG_HAS_MORE_INPUT) | |
| inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF, |
| ); |
| |
| // If decompression fails or we don't have any input, bail out. |
| if (status as i32) < 0 || status == TINFLStatus::NeedsMoreInput { |
| ::miniz_def_free_func(ptr::null_mut(), p_buf); |
| *p_out_len = 0; |
| return ptr::null_mut(); |
| } |
| |
| src_buf_ofs += in_consumed; |
| *p_out_len += out_consumed; |
| |
| if status == TINFLStatus::Done { |
| break; |
| } |
| |
| // If we need more space, double the capacity of the output buffer |
| // and keep going. |
| let mut new_out_buf_capacity = out_buf_capacity * 2; |
| |
| // Try to get at least 128 bytes of buffer capacity. |
| if new_out_buf_capacity < MIN_BUFFER_CAPACITY { |
| new_out_buf_capacity = MIN_BUFFER_CAPACITY |
| } |
| |
| let p_new_buf = ::miniz_def_realloc_func(ptr::null_mut(), p_buf, 1, new_out_buf_capacity); |
| // Bail out if growing fails. |
| if p_new_buf.is_null() { |
| ::miniz_def_free_func(ptr::null_mut(), p_buf); |
| *p_out_len = 0; |
| return ptr::null_mut(); |
| } |
| |
| // Otherwise, continue using the reallocated buffer. |
| p_buf = p_new_buf; |
| out_buf_capacity = new_out_buf_capacity; |
| } |
| |
| p_buf |
| } |
| ); |
| |
| #[cfg(test)] |
| mod test { |
| use miniz_oxide::inflate::core::inflate_flags::{ |
| TINFL_FLAG_COMPUTE_ADLER32, |
| TINFL_FLAG_PARSE_ZLIB_HEADER, |
| }; |
| |
| use super::*; |
| use libc::c_void; |
| use std::{ops, slice}; |
| /// Safe wrapper for `tinfl_decompress_mem_to_mem` using slices. |
| /// |
| /// Could maybe make this public later. |
| fn tinfl_decompress_mem_to_mem_wrapper( |
| source: &mut [u8], |
| dest: &mut [u8], |
| flags: i32, |
| ) -> Option<usize> { |
| let status = unsafe { |
| let source_len = source.len(); |
| let dest_len = dest.len(); |
| tinfl_decompress_mem_to_mem( |
| dest.as_mut_ptr() as *mut c_void, |
| dest_len, |
| source.as_mut_ptr() as *const c_void, |
| source_len, |
| flags, |
| ) |
| }; |
| if status != TINFL_DECOMPRESS_MEM_TO_MEM_FAILED { |
| Some(status) |
| } else { |
| None |
| } |
| } |
| |
| /// Safe wrapper around a buffer allocated with the miniz_def functions. |
| pub struct TinflHeapBuf { |
| buf: *mut c_void, |
| len: size_t, |
| } |
| |
| impl TinflHeapBuf { |
| fn as_slice(&self) -> &[u8] { |
| unsafe { slice::from_raw_parts(self.buf as *const u8, self.len) } |
| } |
| } |
| |
| impl ops::Drop for TinflHeapBuf { |
| fn drop(&mut self) { |
| unsafe { |
| ::miniz_def_free_func(ptr::null_mut(), self.buf); |
| } |
| } |
| } |
| |
| /// Safe wrapper for `tinfl_decompress_mem_to_heap` using slices. |
| /// |
| /// Could maybe make something like this public later. |
| fn tinfl_decompress_mem_to_heap_wrapper(source: &mut [u8], flags: i32) -> Option<TinflHeapBuf> { |
| let source_len = source.len(); |
| let mut out_len = 0; |
| unsafe { |
| let buf_ptr = tinfl_decompress_mem_to_heap( |
| source.as_ptr() as *const c_void, |
| source_len, |
| &mut out_len, |
| flags, |
| ); |
| if !buf_ptr.is_null() { |
| Some(TinflHeapBuf { |
| buf: buf_ptr, |
| len: out_len, |
| }) |
| } else { |
| None |
| } |
| } |
| } |
| |
| #[test] |
| fn mem_to_mem() { |
| let mut encoded = [ |
| 120, 156, 243, 72, 205, 201, 201, 215, 81, 168, |
| 202, 201, 76, 82, 4, 0, 27, 101, 4, 19, |
| ]; |
| let mut out_buf = vec![0; 50]; |
| let flags = TINFL_FLAG_COMPUTE_ADLER32 | TINFL_FLAG_PARSE_ZLIB_HEADER; |
| let size = tinfl_decompress_mem_to_mem_wrapper( |
| &mut encoded[..], |
| out_buf.as_mut_slice(), |
| flags as i32, |
| ).unwrap(); |
| assert_eq!(&out_buf[..size], &b"Hello, zlib!"[..]); |
| } |
| |
| #[test] |
| fn mem_to_heap() { |
| let mut encoded = [ |
| 120, 156, 243, 72, 205, 201, 201, 215, 81, 168, |
| 202, 201, 76, 82, 4, 0, 27, 101, 4, 19, |
| ]; |
| let flags = TINFL_FLAG_COMPUTE_ADLER32 | TINFL_FLAG_PARSE_ZLIB_HEADER; |
| let out_buf = tinfl_decompress_mem_to_heap_wrapper(&mut encoded[..], flags as i32).unwrap(); |
| assert_eq!(out_buf.as_slice(), &b"Hello, zlib!"[..]); |
| } |
| } |