Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use standard rust allocator in C API, save state type in mz_stream #49

Merged
merged 3 commits into from
Jun 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ impl DecompressorOxide {
/// Create a new decompressor with only the state field initialized.
///
/// This is how it's created in miniz. Unsafe due to uninitialized values.
/// Usage is not recommended.
#[inline]
#[deprecated(since = "0.2.2",
note="Will be removed as the safety of using this is hard to verify.")]
pub unsafe fn with_init_state_only() -> DecompressorOxide {
let mut decomp: DecompressorOxide = mem::uninitialized();
decomp.state = core::State::Start;
Expand Down Expand Up @@ -1785,7 +1788,7 @@ mod test {
}

fn check_result(input: &[u8], expected_status: TINFLStatus, expected_state: State, zlib: bool) {
let mut r = unsafe { DecompressorOxide::with_init_state_only() };
let mut r = DecompressorOxide::default();
let mut output_buf = vec![0; 1024 * 32];
let mut out_cursor = Cursor::new(output_buf.as_mut_slice());
let flags = if zlib {
Expand Down
80 changes: 71 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ mod libc {

use std::alloc::{alloc as rust_alloc, realloc as rust_realloc, dealloc as rust_dealloc, Layout};
use std::mem;
use std::ptr::NonNull;

pub type c_void = u8;
pub type c_int = i32;
Expand Down Expand Up @@ -146,10 +145,16 @@ macro_rules! oxidize {
// Make sure we catch a potential panic, as
// this is called from C.
match catch_unwind(AssertUnwindSafe(|| {
let mut stream_oxide = StreamOxide::new(&mut *stream);
let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
*stream = stream_oxide.into_mz_stream();
as_c_return_code(status)
// Do some checks to see if the stream object has the right type.
match StreamOxide::try_new(stream) {
Ok(mut stream_oxide) => {
let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
*stream = stream_oxide.into_mz_stream();
as_c_return_code(status) }
Err(e) => {
e as c_int
}
}
})) {
Ok(res) => res,
Err(_) => {
Expand All @@ -162,16 +167,12 @@ macro_rules! oxidize {
};
}

oxidize!(mz_deflateInit2, mz_deflate_init2_oxide;
level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int);
oxidize!(mz_deflate, mz_deflate_oxide;
flush: c_int);
oxidize!(mz_deflateEnd, mz_deflate_end_oxide;);
oxidize!(mz_deflateReset, mz_deflate_reset_oxide;);


oxidize!(mz_inflateInit2, mz_inflate_init2_oxide;
window_bits: c_int);
oxidize!(mz_inflate, mz_inflate_oxide;
flush: c_int);
oxidize!(mz_inflateEnd, mz_inflate_end_oxide;);
Expand All @@ -188,6 +189,64 @@ pub unsafe extern "C" fn mz_deflateInit(stream: *mut mz_stream, level: c_int) ->
)
}

pub unsafe extern "C" fn mz_deflateInit2(stream: *mut mz_stream, level: c_int, method: c_int,
window_bits: c_int, mem_level: c_int, strategy: c_int)
-> c_int {
match stream.as_mut() {
None => MZError::Stream as c_int,
Some(stream) => {
stream.data_type = StateTypeEnum::Deflate;
// Make sure we catch a potential panic, as
// this is called from C.
match catch_unwind(AssertUnwindSafe(|| {
match StreamOxide::try_new(stream) {
Ok(mut stream_oxide) => {
let status = mz_deflate_init2_oxide(&mut stream_oxide,level, method,
window_bits, mem_level, strategy);
*stream = stream_oxide.into_mz_stream();
as_c_return_code(status) }
Err(e) => {
e as c_int
}
}
})) {
Ok(res) => res,
Err(_) => {
println!("FATAL ERROR: Caught panic!");
MZError::Stream as c_int},
}
}
}
}

pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int)
-> c_int {
match stream.as_mut() {
None => MZError::Stream as c_int,
Some(stream) => {
stream.data_type = StateTypeEnum::Inflate;
// Make sure we catch a potential panic, as
// this is called from C.
match catch_unwind(AssertUnwindSafe(|| {
match StreamOxide::try_new(stream) {
Ok(mut stream_oxide) => {
let status = mz_inflate_init2_oxide(&mut stream_oxide,window_bits);
*stream = stream_oxide.into_mz_stream();
as_c_return_code(status) }
Err(e) => {
e as c_int
}
}
})) {
Ok(res) => res,
Err(_) => {
println!("FATAL ERROR: Caught panic!");
MZError::Stream as c_int},
}
}
}
}

pub unsafe extern "C" fn mz_compress(
dest: *mut u8,
dest_len: *mut c_ulong,
Expand Down Expand Up @@ -222,6 +281,7 @@ pub unsafe extern "C" fn mz_compress2(
avail_in: source_len as c_uint,
next_out: dest,
avail_out: (*dest_len) as c_uint,
data_type: StateTypeEnum::Deflate,
..Default::default()
};

Expand Down Expand Up @@ -260,9 +320,11 @@ pub unsafe extern "C" fn mz_uncompress(
avail_in: source_len as c_uint,
next_out: dest,
avail_out: (*dest_len) as c_uint,
data_type: StateTypeEnum::Inflate,
..Default::default()
};

// We don't expect this to fail since we supply the stream ourselves.
let mut stream_oxide = StreamOxide::new(&mut stream);
as_c_return_code(mz_uncompress2_oxide(&mut stream_oxide, dest_len))
},
Expand Down
67 changes: 54 additions & 13 deletions src/lib_oxide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::{cmp, mem, ptr, slice, usize};
use std::io::Cursor;
use std::alloc::{alloc_zeroed, dealloc, Layout};

use libc::{self, c_char, c_int, c_uint, c_ulong, c_void, size_t};

Expand Down Expand Up @@ -87,7 +88,7 @@ pub struct mz_stream {
pub opaque: *mut c_void,

// TODO: Not sure
pub data_type: c_int,
pub data_type: StateTypeEnum,
/// Adler32 checksum of the data that has been compressed or uncompressed.
pub adler: c_uint,
/// Reserved
Expand All @@ -112,7 +113,7 @@ impl Default for mz_stream {
zfree: None,
opaque: ptr::null_mut(),

data_type: 0,
data_type: StateTypeEnum::None,
adler: 0,
reserved: 0,
}
Expand All @@ -136,25 +137,38 @@ pub unsafe extern "C" fn def_free_func(_opaque: *mut c_void, address: *mut c_voi
libc::free(address)
}

/// Enum to keep track of what type the internal state is when moving over the C API boundary.
#[repr(C)]
#[derive(Debug,Copy,Clone,PartialEq)]
pub enum StateTypeEnum {
None = 0,
Inflate,
Deflate,
}

/// Trait used for states that can be carried by BoxedState.
pub trait StateType {
fn drop_state(&mut self);
const STATE_TYPE: StateTypeEnum;
}
impl StateType for tdefl_compressor {
const STATE_TYPE: StateTypeEnum = StateTypeEnum::Deflate;
fn drop_state(&mut self) {
self.drop_inner();
}
}
impl StateType for inflate_state {
const STATE_TYPE: StateTypeEnum = StateTypeEnum::Inflate;
fn drop_state(&mut self) {}
}

/// Wrapper for a heap-allocated compressor/decompressor that frees the stucture on drop.
struct BoxedState<ST: StateType> {
inner: *mut ST,
alloc: mz_alloc_func,
free: mz_free_func,
alloc: Option<mz_alloc_func>,
free: Option<mz_free_func>,
opaque: *mut c_void,
layout: Layout,
}

impl<ST: StateType> Drop for BoxedState<ST> {
Expand All @@ -171,9 +185,10 @@ impl<ST: StateType> BoxedState<ST> {
pub fn new(stream: &mut mz_stream) -> Self {
BoxedState {
inner: stream.state as *mut ST,
alloc: stream.zalloc.unwrap_or(def_alloc_func),
free: stream.zfree.unwrap_or(def_free_func),
alloc: stream.zalloc,
free: stream.zfree,
opaque: stream.opaque,
layout: Layout::new::<ST>(),
}
}

Expand All @@ -188,7 +203,10 @@ impl<ST: StateType> BoxedState<ST> {
return Err(MZError::Param);
}

self.inner = unsafe { (self.alloc)(self.opaque, 1, mem::size_of::<ST>()) as *mut ST };
// We use the default rust allocator for now. To use a custom allocator we could need to
// add support for the internal allocations using the same one.
self.inner = unsafe { alloc_zeroed(self.layout) as *mut ST };

if self.inner.is_null() {
Err(MZError::Mem)
} else {
Expand All @@ -200,7 +218,7 @@ impl<ST: StateType> BoxedState<ST> {
if !self.inner.is_null() {
unsafe {
self.inner.as_mut().map(|i| i.drop_state());
(self.free)(self.opaque, self.inner as *mut c_void)
dealloc(self.inner as *mut u8, self.layout);
}
self.inner = ptr::null_mut();
}
Expand All @@ -221,7 +239,30 @@ pub struct StreamOxide<'io, ST: StateType> {


impl<'io, ST: StateType> StreamOxide<'io, ST> {
/// Create a new StreamOxide wrapper from a [mz_stream] object.
/// Custom allocation functions are not supported, supplying an mz_stream with allocation
/// function will cause creation to fail.
///
/// Unsafe as the mz_stream object is not guaranteed to be valid. It is up to the
/// caller to ensure it is.
pub unsafe fn new(stream: &mut mz_stream) -> Self {
Self::try_new(stream)
.expect("Failed to create StreamOxide, wrong state type or tried to specify allocators.")
}

/// Try to create a new StreamOxide wrapper from a [mz_stream] object.
/// Custom allocation functions are not supported, supplying an mz_stream with allocation
/// functions will cause creation to fail.
///
/// Unsafe as the mz_stream object is not guaranteed to be valid. It is up to the
/// caller to ensure it is.
pub unsafe fn try_new(stream: &mut mz_stream) -> Result<Self, MZError> {
// Make sure we don't make an inflate stream from a deflate stream and vice versa.
if stream.data_type != ST::STATE_TYPE
|| stream.zalloc.is_some() || stream.zfree.is_some() {
return Err(MZError::Param);
}

let in_slice = stream.next_in.as_ref().map(|ptr| {
slice::from_raw_parts(ptr, stream.avail_in as usize)
});
Expand All @@ -230,14 +271,14 @@ impl<'io, ST: StateType> StreamOxide<'io, ST> {
slice::from_raw_parts_mut(ptr, stream.avail_out as usize)
});

StreamOxide {
Ok(StreamOxide {
next_in: in_slice,
total_in: stream.total_in,
next_out: out_slice,
total_out: stream.total_out,
state: BoxedState::new(stream),
adler: stream.adler,
}
})
}

pub fn into_mz_stream(mut self) -> mz_stream {
Expand All @@ -261,12 +302,12 @@ impl<'io, ST: StateType> StreamOxide<'io, ST> {

msg: ptr::null(),

zalloc: Some(self.state.alloc),
zfree: Some(self.state.free),
zalloc: self.state.alloc,
zfree: self.state.free,
opaque: self.state.opaque,
state: self.state.forget() as *mut mz_internal_state,

data_type: 0,
data_type: ST::STATE_TYPE,
adler: self.adler,
reserved: 0,
}
Expand Down
2 changes: 1 addition & 1 deletion src/tinfl.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub unsafe extern "C" fn tinfl_decompress_mem_to_heap(
// 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();
let mut decomp = tinfl_decompressor::default();
// 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.
Expand Down
56 changes: 56 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
extern crate miniz_oxide;
extern crate miniz_oxide_c_api;

use std::io::Read;

Expand Down Expand Up @@ -49,3 +50,58 @@ fn roundtrip_level_1() {
let dec = decompress_to_vec(enc.as_slice()).unwrap();
assert!(data == dec);
}

#[test]
fn c_api() {
use miniz_oxide_c_api::lib_oxide::mz_stream;
use miniz_oxide_c_api::{mz_deflateInit, mz_deflate, mz_deflateEnd,
mz_inflateInit, mz_inflate, mz_inflateEnd};
use miniz_oxide::{MZStatus, MZError};
let mut data = get_test_data();
let mut compressed = vec![0; data.len() + 50];
let compressed_size;
let decompressed_size;
unsafe {
let mut stream = mz_stream {
next_in: data.as_mut_ptr(),
avail_in: data.len() as u32,
next_out: compressed.as_mut_ptr(),
avail_out: compressed.len() as u32,
..Default::default()
};

assert_eq!(mz_deflateInit(&mut stream, 1), MZStatus::Ok as i32);
assert_eq!(mz_deflate(&mut stream, 4), MZStatus::StreamEnd as i32);
assert_eq!(mz_deflateEnd(&mut stream), MZStatus::Ok as i32);
compressed_size = stream.total_out;

assert_eq!(mz_inflate(&mut stream, 4), MZError::Param as i32);
assert_eq!(mz_inflateEnd(&mut stream), MZError::Param as i32);
}

assert!(compressed_size as usize <= compressed.len());

let mut decompressed = vec![0;data.len()];

unsafe {
let mut stream = mz_stream {
next_in: compressed.as_mut_ptr(),
avail_in: compressed_size as u32,
next_out: decompressed.as_mut_ptr(),
avail_out: decompressed.len() as u32,
..Default::default()
};

assert_eq!(mz_inflateInit(&mut stream),MZStatus::Ok as i32);
assert_eq!(mz_inflate(&mut stream, 4),MZStatus::StreamEnd as i32);
assert_eq!(mz_inflateEnd(&mut stream),MZStatus::Ok as i32);

decompressed_size = stream.total_out;

// This should fail as the stream is an inflate stream!
assert_eq!(mz_deflate(&mut stream, 4), MZError::Param as i32);
assert_eq!(mz_deflateEnd(&mut stream), MZError::Param as i32);
}

assert_eq!(data[..], decompressed[0..decompressed_size as usize]);
}