use std::error;
use std::fmt;
use std::marker;
use std::mem;
use core::ffi::{c_int, c_uint};
use crate::{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) -> Self {
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
);
Self {
inner: Stream {
raw,
_marker: marker::PhantomData,
},
}
}
}
unsafe fn compress_inner(
&mut self,
input: &[u8],
output_ptr: *mut u8,
output_len: usize,
action: Action,
) -> Result<Status, Error> {
if input.is_empty() && action == Action::Run {
return Ok(Status::RunOk);
}
self.inner.raw.next_in = input.as_ptr() as *mut _;
self.inner.raw.avail_in = input.len().min(c_uint::MAX as usize) as c_uint;
self.inner.raw.next_out = output_ptr as *mut _;
self.inner.raw.avail_out = output_len.min(c_uint::MAX as usize) 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(
&mut self,
input: &[u8],
output: &mut [u8],
action: Action,
) -> Result<Status, Error> {
unsafe { self.compress_inner(input, output.as_mut_ptr(), output.len(), action) }
}
pub fn compress_uninit(
&mut self,
input: &[u8],
output: &mut [mem::MaybeUninit<u8>],
action: Action,
) -> Result<Status, Error> {
unsafe { self.compress_inner(input, output.as_mut_ptr() as *mut _, output.len(), action) }
}
pub fn compress_vec(
&mut self,
input: &[u8],
output: &mut Vec<u8>,
action: Action,
) -> Result<Status, Error> {
let len = output.len();
unsafe {
let before = self.total_out();
let ret = self.compress_uninit(input, output.spare_capacity_mut(), action);
output.set_len((self.total_out() - before) as usize + len);
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) -> Self {
unsafe {
let mut raw = Box::new(mem::zeroed());
assert_eq!(ffi::BZ2_bzDecompressInit(&mut *raw, 0, small as c_int), 0);
Self {
inner: Stream {
raw,
_marker: marker::PhantomData,
},
}
}
}
unsafe fn decompress_inner(
&mut self,
input: &[u8],
output_ptr: *mut u8,
output_len: usize,
) -> Result<Status, Error> {
self.inner.raw.next_in = input.as_ptr() as *mut _;
self.inner.raw.avail_in = input.len().min(c_uint::MAX as usize) as c_uint;
self.inner.raw.next_out = output_ptr as *mut _;
self.inner.raw.avail_out = output_len.min(c_uint::MAX as usize) 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(&mut self, input: &[u8], output: &mut [u8]) -> Result<Status, Error> {
unsafe { self.decompress_inner(input, output.as_mut_ptr(), output.len()) }
}
pub fn decompress_uninit(
&mut self,
input: &[u8],
output: &mut [mem::MaybeUninit<u8>],
) -> Result<Status, Error> {
unsafe { self.decompress_inner(input, output.as_mut_ptr() as *mut _, output.len()) }
}
pub fn decompress_vec(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Status, Error> {
let len = output.len();
unsafe {
let before = self.total_out();
let ret = self.decompress_uninit(input, output.spare_capacity_mut());
output.set_len((self.total_out() - before) as usize + len);
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 {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match self {
Self::Sequence => "bzip2: sequence of operations invalid",
Self::Data => "bzip2: invalid data",
Self::DataMagic => "bzip2: bz2 header missing",
Self::Param => "bzip2: invalid parameters",
};
f.write_str(description)
}
}
impl From<Error> for std::io::Error {
fn from(data: Error) -> Self {
Self::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);
}
}
}