use std::default::Default;
use std::{fmt, mem};
use libc::c_ulong;
use crate::tdef::Compressor;
use miniz_oxide::deflate::core::{
create_comp_flags_from_zip_params, deflate_flags, CompressionStrategy, CompressorOxide,
};
use miniz_oxide::deflate::stream::deflate;
use miniz_oxide::inflate::stream::{inflate, InflateState};
use miniz_oxide::*;
const MZ_DEFLATED: i32 = 8;
pub use miniz_oxide::MZ_ADLER32_INIT;
pub enum InternalState {
Inflate(Box<InflateState>),
Deflate(Box<Compressor>),
}
impl fmt::Debug for InternalState {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match &self {
InternalState::Inflate(_) => "Decompressor",
InternalState::Deflate(_) => "Compressor",
};
f.write_str(name)
}
}
pub type MZResult = Result<MZStatus, MZError>;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum StateTypeEnum {
None = 0,
InflateType,
DeflateType,
}
pub trait StateType {
const STATE_TYPE: StateTypeEnum;
fn from_enum(value: &mut InternalState) -> Option<&mut Self>;
}
impl StateType for InflateState {
const STATE_TYPE: StateTypeEnum = StateTypeEnum::InflateType;
fn from_enum(value: &mut InternalState) -> Option<&mut Self> {
if let InternalState::Inflate(state) = value {
Some(state.as_mut())
} else {
None
}
}
}
impl StateType for Compressor {
const STATE_TYPE: StateTypeEnum = StateTypeEnum::DeflateType;
fn from_enum(value: &mut InternalState) -> Option<&mut Self> {
if let InternalState::Deflate(state) = value {
Some(state.as_mut())
} else {
None
}
}
}
#[derive(Default)]
pub struct StreamOxide<'io, ST: StateType> {
pub next_in: Option<&'io [u8]>,
pub total_in: c_ulong,
pub next_out: Option<&'io mut [u8]>,
pub total_out: c_ulong,
pub(crate) state: Option<Box<InternalState>>,
pub adler: u32,
pub(crate) state_type: std::marker::PhantomData<ST>,
}
impl<'io, ST: StateType> StreamOxide<'io, ST> {
pub fn state(&mut self) -> Option<&mut ST> {
StateType::from_enum(self.state.as_mut()?.as_mut())
}
}
fn invalid_window_bits(window_bits: i32) -> bool {
(window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)
}
pub fn mz_compress2_oxide(
stream_oxide: &mut StreamOxide<Compressor>,
level: i32,
dest_len: &mut c_ulong,
) -> MZResult {
mz_deflate_init_oxide(stream_oxide, level)?;
let status = mz_deflate_oxide(stream_oxide, MZFlush::Finish as i32);
mz_deflate_end_oxide(stream_oxide)?;
match status {
Ok(MZStatus::StreamEnd) => {
*dest_len = stream_oxide.total_out;
Ok(MZStatus::Ok)
}
Ok(MZStatus::Ok) => Err(MZError::Buf),
_ => status,
}
}
pub fn mz_deflate_init_oxide(stream_oxide: &mut StreamOxide<Compressor>, level: i32) -> MZResult {
mz_deflate_init2_oxide(
stream_oxide,
level,
MZ_DEFLATED,
MZ_DEFAULT_WINDOW_BITS,
9,
CompressionStrategy::Default as i32,
)
}
pub fn mz_deflate_init2_oxide(
stream_oxide: &mut StreamOxide<Compressor>,
level: i32,
method: i32,
window_bits: i32,
mem_level: i32,
strategy: i32,
) -> MZResult {
let comp_flags = deflate_flags::TDEFL_COMPUTE_ADLER32
| create_comp_flags_from_zip_params(level, window_bits, strategy);
let invalid_level = !(1..=9).contains(&mem_level);
if (method != MZ_DEFLATED) || invalid_level || invalid_window_bits(window_bits) {
return Err(MZError::Param);
}
stream_oxide.adler = MZ_ADLER32_INIT;
stream_oxide.total_in = 0;
stream_oxide.total_out = 0;
let mut compr: Box<Compressor> = Box::default();
compr.inner = Some(CompressorOxide::new(comp_flags));
stream_oxide.state = Some(Box::new(InternalState::Deflate(compr)));
Ok(MZStatus::Ok)
}
pub fn mz_deflate_oxide(stream_oxide: &mut StreamOxide<Compressor>, flush: i32) -> MZResult {
let state: &mut Compressor = {
let enum_ref = stream_oxide.state.as_mut().ok_or(MZError::Stream)?;
StateType::from_enum(enum_ref)
}
.ok_or(MZError::Stream)?;
let next_in = stream_oxide.next_in.as_mut().ok_or(MZError::Stream)?;
let next_out = stream_oxide.next_out.as_mut().ok_or(MZError::Stream)?;
let flush = MZFlush::new(flush)?;
let ret = if let Some(compressor) = state.inner.as_mut() {
deflate(compressor, next_in, next_out, flush)
} else {
return Err(MZError::Param);
};
*next_in = &next_in[ret.bytes_consumed as usize..];
*next_out = &mut mem::take(next_out)[ret.bytes_written as usize..];
stream_oxide.total_in = stream_oxide
.total_in
.wrapping_add(ret.bytes_consumed as c_ulong);
stream_oxide.total_out = stream_oxide
.total_out
.wrapping_add(ret.bytes_written as c_ulong);
stream_oxide.adler = state.adler32();
ret.into()
}
pub fn mz_deflate_end_oxide(stream_oxide: &mut StreamOxide<Compressor>) -> MZResult {
stream_oxide.state = None;
Ok(MZStatus::Ok)
}
pub fn mz_deflate_reset_oxide(stream_oxide: &mut StreamOxide<Compressor>) -> MZResult {
stream_oxide.total_in = 0;
stream_oxide.total_out = 0;
stream_oxide.adler = 0;
stream_oxide.next_in = None;
stream_oxide.next_out = None;
let state = stream_oxide.state().ok_or(MZError::Stream)?;
state.reset();
Ok(MZStatus::Ok)
}
pub fn mz_inflate_init_oxide(stream_oxide: &mut StreamOxide<InflateState>) -> MZResult {
mz_inflate_init2_oxide(stream_oxide, MZ_DEFAULT_WINDOW_BITS)
}
pub fn mz_inflate_init2_oxide(
stream_oxide: &mut StreamOxide<InflateState>,
window_bits: i32,
) -> MZResult {
if invalid_window_bits(window_bits) {
return Err(MZError::Param);
}
stream_oxide.adler = 0;
stream_oxide.total_in = 0;
stream_oxide.total_out = 0;
stream_oxide.state = Some(Box::new(InternalState::Inflate(
InflateState::new_boxed_with_window_bits(window_bits),
)));
Ok(MZStatus::Ok)
}
pub fn mz_inflate_oxide(stream_oxide: &mut StreamOxide<InflateState>, flush: i32) -> MZResult {
let state: &mut InflateState = {
let enum_ref = stream_oxide.state.as_mut().ok_or(MZError::Stream)?;
StateType::from_enum(enum_ref)
}
.ok_or(MZError::Stream)?;
let next_in = stream_oxide.next_in.as_mut().ok_or(MZError::Stream)?;
let next_out = stream_oxide.next_out.as_mut().ok_or(MZError::Stream)?;
let flush = MZFlush::new(flush)?;
let ret = inflate(state, next_in, next_out, flush);
*next_in = &next_in[ret.bytes_consumed as usize..];
*next_out = &mut mem::take(next_out)[ret.bytes_written as usize..];
stream_oxide.total_in = stream_oxide
.total_in
.wrapping_add(ret.bytes_consumed as c_ulong);
stream_oxide.total_out = stream_oxide
.total_out
.wrapping_add(ret.bytes_written as c_ulong);
stream_oxide.adler = state.decompressor().adler32().unwrap_or(0);
ret.into()
}
pub fn mz_uncompress2_oxide(
stream_oxide: &mut StreamOxide<InflateState>,
dest_len: &mut c_ulong,
) -> MZResult {
mz_inflate_init_oxide(stream_oxide)?;
let status = mz_inflate_oxide(stream_oxide, MZFlush::Finish as i32);
mz_inflate_end_oxide(stream_oxide)?;
let empty_in = stream_oxide
.next_in
.map_or(true, |next_in| next_in.is_empty());
match (status, empty_in) {
(Ok(MZStatus::StreamEnd), _) => {
*dest_len = stream_oxide.total_out;
Ok(MZStatus::Ok)
}
(Err(MZError::Buf), true) => Err(MZError::Data),
(status, _) => status,
}
}
pub fn mz_inflate_end_oxide(stream_oxide: &mut StreamOxide<InflateState>) -> MZResult {
stream_oxide.state = None;
Ok(MZStatus::Ok)
}