use std::error;
use std::fmt;
use std::io;
use std::mem;
use std::ptr;
use std::slice;
use brotli_sys;
use libc::c_int;
use super::CompressParams;
pub struct Decompress {
state: *mut brotli_sys::BrotliDecoderState,
}
unsafe impl Send for Decompress {}
unsafe impl Sync for Decompress {}
pub struct Compress {
state: *mut brotli_sys::BrotliEncoderState,
}
unsafe impl Send for Compress {}
unsafe impl Sync for Compress {}
#[repr(isize)]
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum CompressOp {
Process = brotli_sys::BROTLI_OPERATION_PROCESS as isize,
Flush = brotli_sys::BROTLI_OPERATION_FLUSH as isize,
Finish = brotli_sys::BROTLI_OPERATION_FINISH as isize,
EmitMetadata = brotli_sys::BROTLI_OPERATION_EMIT_METADATA as isize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Error(());
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoStatus {
Finished,
Unfinished,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeStatus {
Finished,
NeedInput,
NeedOutput,
}
impl Decompress {
pub fn new() -> Decompress {
unsafe {
let state = brotli_sys::BrotliDecoderCreateInstance(None, None, 0 as *mut _);
assert!(!state.is_null());
Decompress { state: state }
}
}
pub fn decompress(&mut self,
input: &mut &[u8],
output: &mut &mut [u8]) -> Result<DeStatus, Error> {
let mut available_in = input.len();
let mut next_in = input.as_ptr();
let mut available_out = output.len();
let mut next_out = output.as_mut_ptr();
let r = unsafe {
brotli_sys::BrotliDecoderDecompressStream(self.state,
&mut available_in,
&mut next_in,
&mut available_out,
&mut next_out,
ptr::null_mut())
};
*input = &input[input.len() - available_in..];
let out_len = output.len();
*output = &mut mem::replace(output, &mut [])[out_len - available_out..];
Decompress::rc(r)
}
pub fn take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]> {
if let Some(0) = size_limit { return None }
let mut size_limit = size_limit.unwrap_or(0); unsafe {
let ptr = brotli_sys::BrotliDecoderTakeOutput(self.state, &mut size_limit);
if size_limit == 0 { None
} else {
assert!(!ptr.is_null());
Some(slice::from_raw_parts(ptr, size_limit))
}
}
}
fn rc(rc: brotli_sys::BrotliDecoderResult) -> Result<DeStatus, Error> {
match rc {
brotli_sys::BROTLI_DECODER_RESULT_ERROR => Err(Error(())),
brotli_sys::BROTLI_DECODER_RESULT_SUCCESS => Ok(DeStatus::Finished),
brotli_sys::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT => Ok(DeStatus::NeedInput),
brotli_sys::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT => Ok(DeStatus::NeedOutput),
n => panic!("unknown return code: {}", n)
}
}
}
impl Drop for Decompress {
fn drop(&mut self) {
unsafe {
brotli_sys::BrotliDecoderDestroyInstance(self.state);
}
}
}
pub fn decompress_buf(input: &[u8],
output: &mut &mut [u8]) -> Result<usize, Error> {
let mut size = output.len();
let r = unsafe {
brotli_sys::BrotliDecoderDecompress(input.len(),
input.as_ptr(),
&mut size,
output.as_mut_ptr())
};
*output = &mut mem::replace(output, &mut [])[..size];
if r == 0 {
Err(Error(()))
} else {
Ok(size)
}
}
impl Compress {
pub fn new() -> Compress {
unsafe {
let state = brotli_sys::BrotliEncoderCreateInstance(None, None, 0 as *mut _);
assert!(!state.is_null());
Compress { state: state }
}
}
pub fn compress(&mut self,
op: CompressOp,
input: &mut &[u8],
output: &mut &mut [u8]) -> Result<CoStatus, Error> {
let mut available_in = input.len();
let mut next_in = input.as_ptr();
let mut available_out = output.len();
let mut next_out = output.as_mut_ptr();
let r = unsafe {
brotli_sys::BrotliEncoderCompressStream(self.state,
op as brotli_sys::BrotliEncoderOperation,
&mut available_in,
&mut next_in,
&mut available_out,
&mut next_out,
ptr::null_mut())
};
*input = &input[input.len() - available_in..];
let out_len = output.len();
*output = &mut mem::replace(output, &mut [])[out_len - available_out..];
if r == 0 { return Err(Error(())) }
Ok(if op == CompressOp::Process {
CoStatus::Finished
} else if available_in != 0 {
CoStatus::Unfinished
} else if unsafe { brotli_sys::BrotliEncoderHasMoreOutput(self.state) } == 1 {
CoStatus::Unfinished
} else if op == CompressOp::Finish &&
unsafe { brotli_sys::BrotliEncoderIsFinished(self.state) } == 0 {
CoStatus::Unfinished
} else {
CoStatus::Finished
})
}
pub fn take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]> {
if let Some(0) = size_limit { return None }
let mut size_limit = size_limit.unwrap_or(0); unsafe {
let ptr = brotli_sys::BrotliEncoderTakeOutput(self.state, &mut size_limit);
if size_limit == 0 { None
} else {
assert!(!ptr.is_null());
Some(slice::from_raw_parts(ptr, size_limit))
}
}
}
pub fn set_params(&mut self, params: &CompressParams) {
unsafe {
brotli_sys::BrotliEncoderSetParameter(self.state,
brotli_sys::BROTLI_PARAM_MODE,
params.mode);
brotli_sys::BrotliEncoderSetParameter(self.state,
brotli_sys::BROTLI_PARAM_QUALITY,
params.quality);
brotli_sys::BrotliEncoderSetParameter(self.state,
brotli_sys::BROTLI_PARAM_LGWIN,
params.lgwin);
brotli_sys::BrotliEncoderSetParameter(self.state,
brotli_sys::BROTLI_PARAM_LGBLOCK,
params.lgblock);
}
}
}
impl Drop for Compress {
fn drop(&mut self) {
unsafe {
brotli_sys::BrotliEncoderDestroyInstance(self.state);
}
}
}
pub fn compress_buf(params: &CompressParams,
input: &[u8],
output: &mut &mut [u8]) -> Result<usize, Error> {
let mut size = output.len();
let r = unsafe {
brotli_sys::BrotliEncoderCompress(params.quality as c_int,
params.lgwin as c_int,
params.mode as brotli_sys::BrotliEncoderMode,
input.len(),
input.as_ptr(),
&mut size,
output.as_mut_ptr())
};
*output = &mut mem::replace(output, &mut [])[..size];
if r == 0 {
Err(Error(()))
} else {
Ok(size)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
error::Error::description(self).fmt(f)
}
}
impl error::Error for Error {
fn description(&self) -> &str {
"brotli error"
}
}
impl From<Error> for io::Error {
fn from(_err: Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, "brotli error")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decompress_error() {
let mut d = Decompress::new();
d.decompress(&mut &[0; 1024][..], &mut &mut [0; 2048][..]).unwrap_err();
}
#[test]
fn compress_buf_smoke() {
let mut data = [0; 128];
let mut data = &mut data[..];
compress_buf(&CompressParams::new(), b"hello!", &mut data).unwrap();
let mut dst = [0; 128];
{
let mut dst = &mut dst[..];
let n = decompress_buf(data, &mut dst).unwrap();
assert_eq!(n, dst.len());
assert_eq!(dst.len(), 6);
}
assert_eq!(&dst[..6], b"hello!");
}
#[test]
fn decompress_smoke() {
let mut data = [0; 128];
let mut data = &mut data[..];
compress_buf(&CompressParams::new(), b"hello!", &mut data).unwrap();
let mut d = Decompress::new();
let mut dst = [0; 128];
{
let mut data = &data[..];
let mut dst = &mut dst[..];
assert_eq!(d.decompress(&mut data, &mut dst), Ok(DeStatus::Finished));
}
assert_eq!(&dst[..6], b"hello!");
}
#[test]
fn compress_smoke() {
let mut data = [0; 128];
let mut dst = [0; 128];
{
let mut data = &mut data[..];
let mut c = Compress::new();
let mut input = &mut &b"hello!"[..];
assert_eq!(c.compress(CompressOp::Finish, input, &mut data), Ok(CoStatus::Finished));
assert!(input.is_empty());
}
decompress_buf(&data, &mut &mut dst[..]).unwrap();
assert_eq!(&dst[..6], b"hello!");
{
let mut data = &mut data[..];
let mut c = Compress::new();
let mut input = &mut &b"hel"[..];
assert_eq!(c.compress(CompressOp::Flush, input, &mut data), Ok(CoStatus::Finished));
assert!(input.is_empty());
let mut input = &mut &b"lo!"[..];
assert_eq!(c.compress(CompressOp::Finish, input, &mut data), Ok(CoStatus::Finished));
assert!(input.is_empty());
}
decompress_buf(&data, &mut &mut dst[..]).unwrap();
assert_eq!(&dst[..6], b"hello!");
}
}