use std::error;
use std::fmt;
use std::marker;
use std::mem;
use std::slice;
use libc::{c_int, c_uint};
use {ffi, Compression};
pub struct Compress {
inner: Stream<DirCompress>,
}
pub struct Decompress {
inner: Stream<DirDecompress>,
}
struct Stream<D: Direction> {
raw: Box<ffi::bz_stream>,
_marker: marker::PhantomData<D>,
}
unsafe impl<D: Direction> Send for Stream<D> {}
unsafe impl<D: Direction> Sync for Stream<D> {}
trait Direction {
unsafe fn destroy(stream: *mut ffi::bz_stream) -> c_int;
}
enum DirCompress {}
enum DirDecompress {}
#[derive(PartialEq, Eq, Copy, Debug, Clone)]
pub enum Action {
Run = ffi::BZ_RUN as isize,
Flush = ffi::BZ_FLUSH as isize,
Finish = ffi::BZ_FINISH as isize,
}
#[derive(PartialEq, Eq, Copy, Debug, Clone)]
pub enum Status {
Ok,
FlushOk,
RunOk,
FinishOk,
StreamEnd,
MemNeeded,
}
#[derive(PartialEq, Eq, Copy, Debug, Clone)]
pub enum Error {
Sequence,
Data,
DataMagic,
Param,
}
impl Compress {
pub fn new(lvl: Compression, work_factor: u32) -> Compress {
unsafe {
let mut raw = Box::new(mem::zeroed());
assert_eq!(
ffi::BZ2_bzCompressInit(&mut *raw, lvl.level() as c_int, 0, work_factor as c_int),
0
);
Compress {
inner: Stream {
raw: raw,
_marker: marker::PhantomData,
},
}
}
}
pub fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
action: Action,
) -> Result<Status, Error> {
if input.len() == 0 && action == Action::Run {
return Ok(Status::RunOk);
}
self.inner.raw.next_in = input.as_ptr() as *mut _;
self.inner.raw.avail_in = input.len() as c_uint;
self.inner.raw.next_out = output.as_mut_ptr() as *mut _;
self.inner.raw.avail_out = output.len() as c_uint;
unsafe {
match ffi::BZ2_bzCompress(&mut *self.inner.raw, action as c_int) {
ffi::BZ_RUN_OK => Ok(Status::RunOk),
ffi::BZ_FLUSH_OK => Ok(Status::FlushOk),
ffi::BZ_FINISH_OK => Ok(Status::FinishOk),
ffi::BZ_STREAM_END => Ok(Status::StreamEnd),
ffi::BZ_SEQUENCE_ERROR => Err(Error::Sequence),
c => panic!("unknown return status: {}", c),
}
}
}
pub fn compress_vec(
&mut self,
input: &[u8],
output: &mut Vec<u8>,
action: Action,
) -> Result<Status, Error> {
let cap = output.capacity();
let len = output.len();
unsafe {
let before = self.total_out();
let ret = {
let ptr = output.as_mut_ptr().offset(len as isize);
let out = slice::from_raw_parts_mut(ptr, cap - len);
self.compress(input, out, action)
};
output.set_len((self.total_out() - before) as usize + len);
return ret;
}
}
pub fn total_in(&self) -> u64 {
self.inner.total_in()
}
pub fn total_out(&self) -> u64 {
self.inner.total_out()
}
}
impl Decompress {
pub fn new(small: bool) -> Decompress {
unsafe {
let mut raw = Box::new(mem::zeroed());
assert_eq!(ffi::BZ2_bzDecompressInit(&mut *raw, 0, small as c_int), 0);
Decompress {
inner: Stream {
raw: raw,
_marker: marker::PhantomData,
},
}
}
}
pub fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<Status, Error> {
self.inner.raw.next_in = input.as_ptr() as *mut _;
self.inner.raw.avail_in = input.len() as c_uint;
self.inner.raw.next_out = output.as_mut_ptr() as *mut _;
self.inner.raw.avail_out = output.len() as c_uint;
unsafe {
match ffi::BZ2_bzDecompress(&mut *self.inner.raw) {
ffi::BZ_OK => Ok(Status::Ok),
ffi::BZ_MEM_ERROR => Ok(Status::MemNeeded),
ffi::BZ_STREAM_END => Ok(Status::StreamEnd),
ffi::BZ_PARAM_ERROR => Err(Error::Param),
ffi::BZ_DATA_ERROR => Err(Error::Data),
ffi::BZ_DATA_ERROR_MAGIC => Err(Error::DataMagic),
ffi::BZ_SEQUENCE_ERROR => Err(Error::Sequence),
c => panic!("wut: {}", c),
}
}
}
pub fn decompress_vec(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Status, Error> {
let cap = output.capacity();
let len = output.len();
unsafe {
let before = self.total_out();
let ret = {
let ptr = output.as_mut_ptr().offset(len as isize);
let out = slice::from_raw_parts_mut(ptr, cap - len);
self.decompress(input, out)
};
output.set_len((self.total_out() - before) as usize + len);
return ret;
}
}
pub fn total_in(&self) -> u64 {
self.inner.total_in()
}
pub fn total_out(&self) -> u64 {
self.inner.total_out()
}
}
impl<D: Direction> Stream<D> {
fn total_in(&self) -> u64 {
(self.raw.total_in_lo32 as u64) | ((self.raw.total_in_hi32 as u64) << 32)
}
fn total_out(&self) -> u64 {
(self.raw.total_out_lo32 as u64) | ((self.raw.total_out_hi32 as u64) << 32)
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
Error::Sequence => "bzip2: sequence of operations invalid",
Error::Data => "bzip2: invalid data",
Error::DataMagic => "bzip2: bz2 header missing",
Error::Param => "bzip2: invalid parameters",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
error::Error::description(self).fmt(f)
}
}
impl From<Error> for std::io::Error {
fn from(data: Error) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::Other, data)
}
}
impl Direction for DirCompress {
unsafe fn destroy(stream: *mut ffi::bz_stream) -> c_int {
ffi::BZ2_bzCompressEnd(stream)
}
}
impl Direction for DirDecompress {
unsafe fn destroy(stream: *mut ffi::bz_stream) -> c_int {
ffi::BZ2_bzDecompressEnd(stream)
}
}
impl<D: Direction> Drop for Stream<D> {
fn drop(&mut self) {
unsafe {
let _ = D::destroy(&mut *self.raw);
}
}
}