blob: db0e1dc9b4370f4b280df45ea046cada872acbdf [file] [log] [blame]
#![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!"[..]);
}
}