blob: 33aec49bef87725f9510abb56b966b765846458e [file] [log] [blame]
use libc::*;
use std::{mem, cmp, ptr, slice};
use std::panic::{catch_unwind, AssertUnwindSafe};
use miniz_oxide::deflate::core::{compress, compress_to_output, create_comp_flags_from_zip_params,
CompressorOxide, TDEFLFlush, TDEFLStatus};
/// Compression callback function type.
pub type PutBufFuncPtrNotNull = unsafe extern "C" fn(
*const c_void, c_int, *mut c_void) -> bool;
/// `Option` alias for compression callback function type.
pub type PutBufFuncPtr = Option<PutBufFuncPtrNotNull>;
pub struct CallbackFunc {
pub put_buf_func: PutBufFuncPtrNotNull,
pub put_buf_user: *mut c_void,
}
/// Deflate flush modes.
pub mod flush_modes {
use libc::c_int;
use miniz_oxide::deflate::core::TDEFLFlush;
pub const MZ_NO_FLUSH: c_int = TDEFLFlush::None as c_int;
// TODO: This is simply sync flush for now, miniz also treats it as such.
pub const MZ_PARTIAL_FLUSH: c_int = 1;
pub const MZ_SYNC_FLUSH: c_int = TDEFLFlush::Sync as c_int;
pub const MZ_FULL_FLUSH: c_int = TDEFLFlush::Full as c_int;
pub const MZ_FINISH: c_int = TDEFLFlush::Finish as c_int;
// TODO: Doesn't seem to be implemented by miniz.
pub const MZ_BLOCK: c_int = 5;
}
pub mod strategy {
use libc::c_int;
use miniz_oxide::deflate::core::CompressionStrategy::*;
pub const MZ_DEFAULT_STRATEGY: c_int = Default as c_int;
pub const MZ_FILTERED: c_int = Filtered as c_int;
pub const MZ_HUFFMAN_ONLY: c_int = HuffmanOnly as c_int;
pub const MZ_RLE: c_int = RLE as c_int;
pub const MZ_FIXED: c_int = Fixed as c_int;
}
/// Main compression struct. Not the same as `CompressorOxide`
#[repr(C)]
#[allow(bad_style)]
pub struct tdefl_compressor {
pub inner: Option<CompressorOxide>,
pub callback: Option<CallbackFunc>,
}
impl tdefl_compressor {
pub(crate) fn new(flags: u32) -> Self {
tdefl_compressor {
inner: Some(CompressorOxide::new(flags)),
callback: None,
}
}
pub(crate) fn new_with_callback(flags: u32, func: CallbackFunc) -> Self {
tdefl_compressor {
inner: Some(CompressorOxide::new(flags)),
callback: Some(func),
}
}
/// Sets the inner state to `None` and thus drops it.
pub fn drop_inner(&mut self) {
self.inner = None;
}
pub fn adler32(&self) -> u32 {
self.inner.as_ref().map(|i| i.adler32()).unwrap_or(0)
}
pub fn prev_return_status(&self) -> TDEFLStatus {
// Not sure we should return on inner not existing, but that shouldn't happen
// anyway.
self.inner.as_ref().map(|i| i.prev_return_status()).unwrap_or(TDEFLStatus::BadParam)
}
pub fn flags(&self) -> i32 {
self.inner.as_ref().map(|i| i.flags()).unwrap_or(0)
}
}
unmangle!(
pub unsafe extern "C" fn tdefl_compress(
d: Option<&mut tdefl_compressor>,
in_buf: *const c_void,
in_size: Option<&mut usize>,
out_buf: *mut c_void,
out_size: Option<&mut usize>,
flush: TDEFLFlush,
) -> TDEFLStatus {
match d {
None => {
in_size.map(|size| *size = 0);
out_size.map(|size| *size = 0);
TDEFLStatus::BadParam
},
Some(compressor_wrap) => {
if let Some(ref mut compressor) = compressor_wrap.inner {
let in_buf_size = in_size.as_ref().map_or(0, |size| **size);
let out_buf_size = out_size.as_ref().map_or(0, |size| **size);
if in_buf_size > 0 && in_buf.is_null() {
in_size.map(|size| *size = 0);
out_size.map(|size| *size = 0);
return TDEFLStatus::BadParam;
}
let in_slice = (in_buf as *const u8).as_ref().map_or(&[][..],|in_buf| {
slice::from_raw_parts(in_buf, in_buf_size)
});
let res = match compressor_wrap.callback {
None => {
match (out_buf as *mut u8).as_mut() {
Some(out_buf) => compress(
compressor,
in_slice,
slice::from_raw_parts_mut(out_buf, out_buf_size),
flush,
),
None => {
in_size.map(|size| *size = 0);
out_size.map(|size| *size = 0);
return TDEFLStatus::BadParam;
}
}
},
Some(ref func) => {
if out_buf_size > 0 || !out_buf.is_null() {
in_size.map(|size| *size = 0);
out_size.map(|size| *size = 0);
return TDEFLStatus::BadParam;
}
let res = compress_to_output(
compressor,
in_slice,
flush,
|out: &[u8]| {
(func.put_buf_func)(
&(out[0]) as *const u8 as *const c_void,
out.len() as i32,
func.put_buf_user
)
},
);
(res.0, res.1, 0)
},
};
in_size.map(|size| *size = res.1);
out_size.map(|size| *size = res.2);
res.0
} else {
TDEFLStatus::BadParam
}
}
}
}
pub unsafe extern "C" fn tdefl_compress_buffer(
d: Option<&mut tdefl_compressor>,
in_buf: *const c_void,
mut in_size: usize,
flush: TDEFLFlush,
) -> TDEFLStatus {
tdefl_compress(d, in_buf, Some(&mut in_size), ptr::null_mut(), None, flush)
}
/// Allocate a compressor.
///
/// This does initialize the struct, but not the inner constructor,
/// tdefl_init has to be called before doing anything with it.
pub unsafe extern "C" fn tdefl_allocate() -> *mut tdefl_compressor {
Box::into_raw(Box::<tdefl_compressor>::new(
tdefl_compressor {
inner: None,
callback: None,
}
))
}
/// Deallocate the compressor. (Does nothing if the argument is null).
///
/// This also calles the compressor's destructor, freeing the internal memory
/// allocated by it.
pub unsafe extern "C" fn tdefl_deallocate(c: *mut tdefl_compressor) {
if !c.is_null() {
Box::from_raw(c);
}
}
/// Initialize the compressor struct in the space pointed to by `d`.
/// if d is null, an error is returned.
///
/// Deinitialization is handled by tdefl_deallocate, and thus
/// tdefl_compressor should not be allocated or freed manually, but only through
/// tdefl_allocate and tdefl_deallocate
pub unsafe extern "C" fn tdefl_init(
d: Option<&mut tdefl_compressor>,
put_buf_func: PutBufFuncPtr,
put_buf_user: *mut c_void,
flags: c_int,
) -> TDEFLStatus {
if let Some(d) = d {
match catch_unwind( AssertUnwindSafe(|| {
d.inner = Some(CompressorOxide::new(flags as u32));
if let Some(f) = put_buf_func {
d.callback = Some(CallbackFunc {
put_buf_func: f,
put_buf_user: put_buf_user,
})
} else {
d.callback = None;
};
})) {
Ok(_) => TDEFLStatus::Okay,
Err(_) => {
eprintln!("FATAL ERROR: Caught panic when initializing the compressor!");
TDEFLStatus::BadParam
}
}
} else {
TDEFLStatus::BadParam
}
}
pub unsafe extern "C" fn tdefl_get_prev_return_status(
d: Option<&mut tdefl_compressor>,
) -> TDEFLStatus {
d.map_or(TDEFLStatus::Okay, |d| d.prev_return_status())
}
pub unsafe extern "C" fn tdefl_get_adler32(d: Option<&mut tdefl_compressor>) -> c_uint {
d.map_or(::MZ_ADLER32_INIT as u32, |d| d.adler32())
}
pub unsafe extern "C" fn tdefl_compress_mem_to_output(
buf: *const c_void,
buf_len: usize,
put_buf_func: PutBufFuncPtr,
put_buf_user: *mut c_void,
flags: c_int,
) -> bool {
if let Some(put_buf_func) = put_buf_func {
let compressor = ::miniz_def_alloc_func(
ptr::null_mut(),
1,
mem::size_of::<tdefl_compressor>(),
) as *mut tdefl_compressor;
ptr::write(compressor,tdefl_compressor::new_with_callback(flags as u32, CallbackFunc {
put_buf_func: put_buf_func,
put_buf_user: put_buf_user,
}));
let res = tdefl_compress_buffer(compressor.as_mut(), buf, buf_len, TDEFLFlush::Finish) ==
TDEFLStatus::Done;
compressor.as_mut().map(|c| c.drop_inner());
::miniz_def_free_func(ptr::null_mut(), compressor as *mut c_void);
res
} else {
false
}
}
);
struct BufferUser {
pub size: usize,
pub capacity: usize,
pub buf: *mut u8,
pub expandable: bool,
}
pub unsafe extern "C" fn output_buffer_putter(
buf: *const c_void,
len: c_int,
user: *mut c_void,
) -> bool {
let user = (user as *mut BufferUser).as_mut();
match user {
None => false,
Some(user) => {
let new_size = user.size + len as usize;
if new_size > user.capacity {
if !user.expandable {
return false;
}
let mut new_capacity = cmp::max(user.capacity, 128);
while new_size > new_capacity {
new_capacity <<= 1;
}
let new_buf = ::miniz_def_realloc_func(
ptr::null_mut(),
user.buf as *mut c_void,
1,
new_capacity,
);
if new_buf.is_null() {
return false;
}
user.buf = new_buf as *mut u8;
user.capacity = new_capacity;
}
ptr::copy_nonoverlapping(
buf as *const u8,
user.buf.offset(user.size as isize),
len as usize,
);
user.size = new_size;
true
}
}
}
unmangle!(
pub unsafe extern "C" fn tdefl_compress_mem_to_heap(
src_buf: *const c_void,
src_buf_len: usize,
out_len: *mut usize,
flags: c_int,
) -> *mut c_void {
match out_len.as_mut() {
None => ptr::null_mut(),
Some(len) => {
*len = 0;
let mut buffer_user = BufferUser {
size: 0,
capacity: 0,
buf: ptr::null_mut(),
expandable: true,
};
if !tdefl_compress_mem_to_output(
src_buf,
src_buf_len,
Some(output_buffer_putter),
&mut buffer_user as *mut BufferUser as *mut c_void,
flags,
) {
ptr::null_mut()
} else {
*len = buffer_user.size;
buffer_user.buf as *mut c_void
}
}
}
}
pub unsafe extern "C" fn tdefl_compress_mem_to_mem(
out_buf: *mut c_void,
out_buf_len: usize,
src_buf: *const c_void,
src_buf_len: usize,
flags: c_int,
) -> usize {
if out_buf.is_null() {
return 0;
}
let mut buffer_user = BufferUser {
size: 0,
capacity: out_buf_len,
buf: out_buf as *mut u8,
expandable: false,
};
if tdefl_compress_mem_to_output(
src_buf,
src_buf_len,
Some(output_buffer_putter),
&mut buffer_user as *mut BufferUser as *mut c_void,
flags,
)
{
buffer_user.size
} else {
0
}
}
pub extern "C" fn tdefl_create_comp_flags_from_zip_params(
level: c_int,
window_bits: c_int,
strategy: c_int,
) -> c_uint {
create_comp_flags_from_zip_params(level, window_bits, strategy)
}
);
#[cfg(test)]
mod test {
use super::*;
use miniz_oxide::inflate::decompress_to_vec;
#[test]
fn mem_to_heap() {
let data = b"blargharghawrf31086t13qa9pt7gnseatgawe78vtb6p71v";
let mut out_len = 0;
let data_len = data.len();
let out_data = unsafe {
let res = tdefl_compress_mem_to_heap(
data.as_ptr() as *const c_void, data_len, &mut out_len, 0);
assert!(!res.is_null());
res
};
{
let out_slice = unsafe {
slice::from_raw_parts(out_data as *const u8, out_len)
};
let dec = decompress_to_vec(out_slice).unwrap();
assert!(dec.as_slice() == &data[..]);
}
}
}