use alloc::boxed::Box;
use alloc::vec::Vec;
use std::io::{Error, ErrorKind, Result, Write};
use crate::compressor::parallel::{
ParallelEncoderSession, ParallelEncoderStatus, ParallelProgress,
};
use crate::compressor::session::Operation;
use crate::io::FinishError;
const PULL_CHUNK: usize = 128 * 1024;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum State {
Open,
Finishing,
Finished,
}
pub struct ParallelEncoderWriter<'c, W: Write> {
session: Box<ParallelEncoderSession<'c>>,
sink: W,
outbox: Vec<u8>,
head: usize,
end: usize,
state: State,
}
impl<W: Write> std::fmt::Debug for ParallelEncoderWriter<'_, W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParallelEncoderWriter")
.field("session", &self.session)
.field("undelivered", &(self.end - self.head))
.field("state", &self.state)
.finish_non_exhaustive()
}
}
impl<'c, W: Write> ParallelEncoderWriter<'c, W> {
pub(super) fn new(session: ParallelEncoderSession<'c>, sink: W) -> Self {
Self {
session: Box::new(session),
sink,
outbox: Vec::new(),
head: 0,
end: 0,
state: State::Open,
}
}
#[must_use]
pub const fn get_ref(&self) -> &W {
&self.sink
}
pub const fn get_mut(&mut self) -> &mut W {
&mut self.sink
}
#[must_use]
pub fn is_finished(&self) -> bool {
self.state == State::Finished
}
pub fn try_finish(&mut self) -> Result<()> {
if self.state == State::Finished {
return Ok(());
}
self.drain()?;
loop {
let progress = self.pump(&[], Operation::End)?;
self.state = State::Finishing;
self.drain()?;
if self.settle(progress)? {
break;
}
}
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::from_parts(error, self)),
}
}
fn drain(&mut self) -> Result<()> {
while self.head < self.end {
match self.sink.write(&self.outbox[self.head..self.end]) {
Ok(0) => {
return Err(Error::new(
ErrorKind::WriteZero,
"the sink accepted no bytes",
));
}
Ok(n) => self.head += n,
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<ParallelProgress> {
self.outbox.resize(PULL_CHUNK, 0);
let progress = self.session.process(input, &mut self.outbox, operation)?;
self.end = progress.produced;
Ok(progress)
}
fn settle(&mut self, progress: ParallelProgress) -> Result<bool> {
match progress.status {
ParallelEncoderStatus::NeedsInput | ParallelEncoderStatus::Finished => Ok(true),
ParallelEncoderStatus::Pending if progress.produced == 0 => {
self.session.wait()?;
Ok(false)
}
ParallelEncoderStatus::Pending | ParallelEncoderStatus::NeedsOutput => Ok(false),
}
}
}
impl<W: Write> Write for ParallelEncoderWriter<'_, W> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
if self.state != State::Open {
return Err(Error::other("the frame has already been finished"));
}
self.drain()?;
if buf.is_empty() {
return Ok(0);
}
loop {
let progress = self.pump(buf, Operation::Continue)?;
if progress.consumed != 0 {
drop(self.drain());
return Ok(progress.consumed);
}
self.drain()?;
if progress.produced == 0 {
self.session.wait()?;
}
}
}
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 self.settle(progress)? {
break;
}
}
self.sink.flush()
}
}