#![deny(missing_docs)]
use flate2::{Compress, Compression, Decompress, FlushCompress, FlushDecompress, Status};
use std::{
io::{Error, ErrorKind},
slice,
};
pub use flate2;
#[repr(C)]
pub enum FlateResult {
Success = 0,
StreamEnd = 1,
BufError = -1,
OtherError = -2,
}
impl From<Status> for FlateResult {
fn from(r: Status) -> Self {
match r {
Status::BufError => FlateResult::BufError,
Status::Ok => FlateResult::Success,
Status::StreamEnd => FlateResult::StreamEnd,
}
}
}
#[no_mangle]
pub unsafe extern "C" fn tectonic_flate_compress(
output_ptr: *mut u8,
output_len: *mut u64,
input_ptr: *const u8,
input_len: u64,
compression_level: u32,
) -> FlateResult {
let mut c = Compress::new(Compression::new(compression_level), true);
let input = slice::from_raw_parts(input_ptr, input_len as usize);
let output = slice::from_raw_parts_mut(output_ptr, *output_len as usize);
let (size, result) = if c.compress(input, output, FlushCompress::Finish).is_err() {
(0, FlateResult::OtherError)
} else {
(c.total_out(), FlateResult::Success)
};
*output_len = size;
result
}
#[no_mangle]
pub unsafe extern "C" fn tectonic_flate_decompress(
output_ptr: *mut u8,
output_len: *mut u64,
input_ptr: *const u8,
input_len: u64,
) -> FlateResult {
let mut dc = Decompress::new(true);
let input = slice::from_raw_parts(input_ptr, input_len as usize);
let output = slice::from_raw_parts_mut(output_ptr, *output_len as usize);
let (size, result) = match dc.decompress(input, output, FlushDecompress::Finish) {
Ok(status) => (dc.total_out(), status.into()),
Err(_) => (0, FlateResult::OtherError),
};
*output_len = size;
result
}
struct Decompressor<'a> {
deflate: Decompress,
input_data: &'a [u8],
amount_written: u64,
done: bool,
}
impl<'a> Decompressor<'a> {
fn decompress_chunk(&mut self, output: &mut [u8]) -> Result<usize, Error> {
if self.done {
return Ok(0);
}
let inslice = &self.input_data[self.deflate.total_in() as usize..];
let status = self
.deflate
.decompress(inslice, output, FlushDecompress::None)?;
let delta = self.deflate.total_out() - self.amount_written;
self.amount_written = self.deflate.total_out();
match status {
Status::Ok => {}
Status::StreamEnd => {
self.done = true;
}
Status::BufError => {
return Err(Error::new(
ErrorKind::Other,
"incomplete input or too-small output buffer",
));
}
}
Ok(delta as usize)
}
}
#[no_mangle]
pub unsafe extern "C" fn tectonic_flate_new_decompressor(
input_ptr: *const u8,
input_len: u64,
) -> *mut libc::c_void {
let input = slice::from_raw_parts(input_ptr, input_len as usize);
let dc = Decompressor {
deflate: Decompress::new(true),
input_data: input,
amount_written: 0,
done: false,
};
Box::leak(Box::new(dc)) as *mut Decompressor as *mut _
}
#[no_mangle]
pub unsafe extern "C" fn tectonic_flate_decompress_chunk(
handle: *mut libc::c_void,
output_ptr: *mut u8,
output_len: *mut u64,
) -> libc::c_int {
let mut dc = Box::from_raw(handle as *mut Decompressor);
let output = slice::from_raw_parts_mut(output_ptr, *output_len as usize);
let (amount, flag) = match dc.decompress_chunk(output) {
Ok(n) => (n, 0),
Err(_) => (0, 1),
};
*output_len = amount as u64;
Box::leak(dc);
flag
}
#[no_mangle]
pub unsafe extern "C" fn tectonic_flate_free_decompressor(handle: *mut libc::c_void) {
let _dc = Box::from_raw(handle as *mut Decompressor);
}