use enough::Stop;
use crate::{CompressResult, DeflateEncoder, Error, Options, Write};
pub struct GzipEncoder<W: Write, S: Stop = enough::Unstoppable> {
deflate_encoder: Option<DeflateEncoder<W, S>>,
crc32_hasher: zenflate::Crc32Hasher,
input_size: u32,
}
impl<W: Write> GzipEncoder<W> {
pub fn new(options: Options, mut sink: W) -> Result<Self, Error> {
static HEADER: &[u8] = &[
31, 139, 8, 0, 0, 0, 0, 0, 2, 3, ];
sink.write_all(HEADER)?;
Ok(Self {
deflate_encoder: Some(DeflateEncoder::new(options, sink)),
crc32_hasher: zenflate::Crc32Hasher::new(),
input_size: 0,
})
}
#[cfg(feature = "std")]
pub fn new_buffered(options: Options, sink: W) -> Result<std::io::BufWriter<Self>, Error> {
Ok(std::io::BufWriter::with_capacity(
crate::util::ZOPFLI_MASTER_BLOCK_SIZE,
Self::new(options, sink)?,
))
}
}
impl<W: Write, S: Stop> GzipEncoder<W, S> {
#[cfg(feature = "std")]
pub fn with_stop_buffered(
options: Options,
sink: W,
stop: S,
) -> Result<std::io::BufWriter<Self>, Error> {
Ok(std::io::BufWriter::with_capacity(
crate::util::ZOPFLI_MASTER_BLOCK_SIZE,
Self::with_stop(options, sink, stop)?,
))
}
pub fn with_stop(options: Options, mut sink: W, stop: S) -> Result<Self, Error> {
static HEADER: &[u8] = &[
31, 139, 8, 0, 0, 0, 0, 0, 2, 3, ];
sink.write_all(HEADER)?;
Ok(Self {
deflate_encoder: Some(DeflateEncoder::with_stop(options, sink, stop)),
crc32_hasher: zenflate::Crc32Hasher::new(),
input_size: 0,
})
}
pub fn finish(mut self) -> Result<CompressResult<W>, Error> {
self.__finish().map(|sink| sink.unwrap())
}
fn __finish(&mut self) -> Result<Option<CompressResult<W>>, Error> {
if self.deflate_encoder.is_none() {
return Ok(None);
}
let mut result = self.deflate_encoder.take().unwrap().finish()?;
result
.inner
.write_all(&self.crc32_hasher.finalize().to_le_bytes())?;
result.inner.write_all(&self.input_size.to_le_bytes())?;
Ok(Some(result))
}
pub fn get_ref(&self) -> &W {
self.deflate_encoder.as_ref().unwrap().get_ref()
}
pub fn get_mut(&mut self) -> &mut W {
self.deflate_encoder.as_mut().unwrap().get_mut()
}
}
impl<W: Write, S: Stop> core::fmt::Debug for GzipEncoder<W, S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GzipEncoder")
.field("input_size", &self.input_size)
.finish_non_exhaustive()
}
}
impl<W: Write, S: Stop> Write for GzipEncoder<W, S> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.deflate_encoder
.as_mut()
.unwrap()
.write(buf)
.inspect(|&bytes_written| {
self.crc32_hasher.update(&buf[..bytes_written]);
self.input_size = self.input_size.wrapping_add(bytes_written as u32);
})
}
fn flush(&mut self) -> Result<(), Error> {
self.deflate_encoder.as_mut().unwrap().flush()
}
}
impl<W: Write, S: Stop> Drop for GzipEncoder<W, S> {
fn drop(&mut self) {
self.__finish().ok();
}
}
#[cfg(all(doc, feature = "std"))]
impl<W: crate::io::Write, S: Stop> std::io::Write for GzipEncoder<W, S> {
fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
unimplemented!()
}
fn flush(&mut self) -> std::io::Result<()> {
unimplemented!()
}
}