use crate::compressor::session::{EncoderSession, EncoderStatus, Operation, Progress};
use std::io::{Error, ErrorKind, Result, Write};
const PULL_CHUNK: usize = 128 * 1024;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum State {
Open,
Finishing,
Finished,
}
pub struct EncoderWriter<'c, 'd, W: Write> {
session: EncoderSession<'c, 'd>,
sink: W,
outbox: Vec<u8>,
head: usize,
end: usize,
state: State,
}
impl<W: Write> std::fmt::Debug for EncoderWriter<'_, '_, W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EncoderWriter")
.field("state", &self.state)
.field("undelivered", &(self.end - self.head))
.finish_non_exhaustive()
}
}
impl<'c, 'd, W: Write> EncoderWriter<'c, 'd, W> {
pub(crate) fn new(session: EncoderSession<'c, 'd>, sink: W) -> Self {
Self {
session,
sink,
outbox: Vec::new(),
head: 0,
end: 0,
state: State::Open,
}
}
pub const fn get_ref(&self) -> &W {
&self.sink
}
pub const fn get_mut(&mut self) -> &mut W {
&mut self.sink
}
pub const fn is_finished(&self) -> bool {
matches!(self.state, State::Finished)
}
pub fn try_finish(&mut self) -> Result<()> {
if self.state == State::Finished {
return Ok(());
}
self.drain()?;
while !self.session.is_finished() {
self.pump(&[], Operation::Finish)?;
self.state = State::Finishing;
self.drain()?;
}
self.drain()?;
self.sink.flush()?;
self.state = State::Finished;
Ok(())
}
pub fn finish(mut self) -> std::result::Result<W, FinishError<Self>> {
match self.try_finish() {
Ok(()) => Ok(self.sink),
Err(error) => Err(FinishError {
error,
writer: self,
}),
}
}
fn drain(&mut self) -> Result<()> {
while self.head < self.end {
let remaining = &self.outbox[self.head..self.end];
match self.sink.write(remaining) {
Ok(0) => {
return Err(Error::new(
ErrorKind::WriteZero,
"the sink accepted none of the compressed stream",
));
}
Ok(count) => self.head += count,
Err(error) if error.kind() == ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
self.head = 0;
self.end = 0;
Ok(())
}
fn pump(&mut self, input: &[u8], operation: Operation) -> Result<Progress> {
debug_assert_eq!(self.end, 0);
self.outbox.resize(PULL_CHUNK, 0);
let progress = self.session.process(input, &mut self.outbox, operation)?;
self.end = progress.produced;
Ok(progress)
}
}
impl<W: Write> Write for EncoderWriter<'_, '_, W> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
if self.state != State::Open {
return Err(Error::new(
ErrorKind::InvalidInput,
"the compressed stream has already been finished",
));
}
self.drain()?;
if buf.is_empty() {
return Ok(0);
}
loop {
let progress = self.pump(buf, Operation::Process)?;
if progress.consumed != 0 {
drop(self.drain());
return Ok(progress.consumed);
}
self.drain()?;
}
}
fn flush(&mut self) -> Result<()> {
if self.state != State::Open {
return self.sink.flush();
}
self.drain()?;
loop {
let progress = self.pump(&[], Operation::Flush)?;
self.drain()?;
if progress.status != EncoderStatus::NeedsOutput {
break;
}
}
self.sink.flush()
}
}
pub struct FinishError<T> {
error: Error,
writer: T,
}
impl<T> FinishError<T> {
#[must_use]
pub const fn error(&self) -> &Error {
&self.error
}
#[must_use]
pub fn into_error(self) -> Error {
self.error
}
#[must_use]
pub fn into_inner(self) -> T {
self.writer
}
#[must_use]
pub fn into_parts(self) -> (Error, T) {
(self.error, self.writer)
}
}
impl<T> std::fmt::Debug for FinishError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FinishError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl<T> std::fmt::Display for FinishError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"the compressed stream could not be finished: {}",
self.error
)
}
}
impl<T> std::error::Error for FinishError<T> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
impl<T> From<FinishError<T>> for Error {
fn from(value: FinishError<T>) -> Self {
value.error
}
}