use crate::zlib::Status;
use zlib_rs::DeflateError;
const BUF_SIZE: usize = 4096 * 8;
pub struct Write<W> {
compressor: Compress,
inner: W,
buf: [u8; BUF_SIZE],
}
impl<W> Clone for Write<W>
where
W: Clone,
{
fn clone(&self) -> Self {
Write {
compressor: impls::new_compress(),
inner: self.inner.clone(),
buf: self.buf,
}
}
}
pub struct Compress(zlib_rs::Deflate);
impl Default for Compress {
fn default() -> Self {
Self::new()
}
}
impl Compress {
pub fn total_in(&self) -> u64 {
self.0.total_in()
}
pub fn total_out(&self) -> u64 {
self.0.total_out()
}
pub fn new() -> Self {
let config = zlib_rs::DeflateConfig::best_speed();
let header = true;
let inner = zlib_rs::Deflate::new(config.level, header, config.window_bits as u8);
Self(inner)
}
pub fn reset(&mut self) {
self.0.reset();
}
pub fn compress(&mut self, input: &[u8], output: &mut [u8], flush: FlushCompress) -> Result<Status, CompressError> {
let flush = match flush {
FlushCompress::None => zlib_rs::DeflateFlush::NoFlush,
FlushCompress::Partial => zlib_rs::DeflateFlush::PartialFlush,
FlushCompress::Sync => zlib_rs::DeflateFlush::SyncFlush,
FlushCompress::Full => zlib_rs::DeflateFlush::FullFlush,
FlushCompress::Finish => zlib_rs::DeflateFlush::Finish,
};
let status = self.0.compress(input, output, flush)?;
match status {
zlib_rs::Status::Ok => Ok(Status::Ok),
zlib_rs::Status::BufError => Ok(Status::BufError),
zlib_rs::Status::StreamEnd => Ok(Status::StreamEnd),
}
}
}
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum CompressError {
#[error("stream error")]
StreamError,
#[error("The input is not a valid deflate stream.")]
DataError,
#[error("Not enough memory")]
InsufficientMemory,
}
impl From<zlib_rs::DeflateError> for CompressError {
fn from(value: zlib_rs::DeflateError) -> Self {
match value {
DeflateError::StreamError => CompressError::StreamError,
DeflateError::DataError => CompressError::DataError,
DeflateError::MemError => CompressError::InsufficientMemory,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
#[allow(clippy::unnecessary_cast)]
pub enum FlushCompress {
None = 0,
Partial = 1,
Sync = 2,
Full = 3,
Finish = 4,
}
mod impls {
use std::io;
use crate::zlib::stream::deflate::{self, Compress, FlushCompress};
use crate::zlib::Status;
pub(crate) fn new_compress() -> Compress {
Compress::new()
}
impl<W> deflate::Write<W>
where
W: io::Write,
{
pub fn new(inner: W) -> deflate::Write<W> {
deflate::Write {
compressor: new_compress(),
inner,
buf: [0; deflate::BUF_SIZE],
}
}
pub fn reset(&mut self) {
self.compressor.reset();
}
pub fn into_inner(self) -> W {
self.inner
}
fn write_inner(&mut self, mut buf: &[u8], flush: FlushCompress) -> io::Result<usize> {
let total_in_when_start = self.compressor.total_in();
loop {
let last_total_in = self.compressor.total_in();
let last_total_out = self.compressor.total_out();
let status = self
.compressor
.compress(buf, &mut self.buf, flush)
.map_err(io::Error::other)?;
let written = self.compressor.total_out() - last_total_out;
if written > 0 {
self.inner.write_all(&self.buf[..written as usize])?;
}
match status {
Status::StreamEnd => return Ok((self.compressor.total_in() - total_in_when_start) as usize),
Status::Ok | Status::BufError => {
let consumed = self.compressor.total_in() - last_total_in;
buf = &buf[consumed as usize..];
if self.compressor.total_out() > last_total_out {
continue;
}
if self.compressor.total_in() > last_total_in {
continue;
}
return Ok((self.compressor.total_in() - total_in_when_start) as usize);
}
}
}
}
}
impl<W: io::Write> io::Write for deflate::Write<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_inner(buf, FlushCompress::None)
}
fn flush(&mut self) -> io::Result<()> {
self.write_inner(&[], FlushCompress::Finish).map(|_| ())
}
}
}
#[cfg(test)]
mod tests;