use crate::compressor::Operation;
use crate::compressor::parallel::{ParallelEncoderSession, ParallelEncoderStatus};
use crate::io::EncoderReaderParts;
use std::io::{ErrorKind, Read, Result};
const FILL_CHUNK: usize = 64 * 1024;
pub struct ParallelEncoderReader<'c, R: Read> {
session: ParallelEncoderSession<'c>,
source: R,
input: Vec<u8>,
head: usize,
end: usize,
eof: bool,
finished: bool,
}
impl<R: Read> std::fmt::Debug for ParallelEncoderReader<'_, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParallelEncoderReader")
.field("buffered", &(self.end - self.head))
.field("eof", &self.eof)
.field("finished", &self.finished)
.finish_non_exhaustive()
}
}
impl<'c, R: Read> ParallelEncoderReader<'c, R> {
pub(in crate::compressor::parallel) const fn new(
session: ParallelEncoderSession<'c>,
source: R,
) -> Self {
Self {
session,
source,
input: Vec::new(),
head: 0,
end: 0,
eof: false,
finished: false,
}
}
pub const fn get_ref(&self) -> &R {
&self.source
}
pub const fn get_mut(&mut self) -> &mut R {
&mut self.source
}
pub const fn is_finished(&self) -> bool {
self.finished
}
#[must_use]
pub fn into_parts(self) -> EncoderReaderParts<R> {
let buffered_input = self
.input
.get(self.head..self.end)
.unwrap_or_default()
.to_vec();
EncoderReaderParts {
source: self.source,
unread_input: buffered_input,
}
}
fn fill(&mut self) -> Result<()> {
self.head = 0;
self.end = 0;
self.input.resize(FILL_CHUNK, 0);
loop {
match self.source.read(&mut self.input) {
Ok(0) => {
self.eof = true;
return Ok(());
}
Ok(count) => {
self.end = count.min(self.input.len());
return Ok(());
}
Err(error) if error.kind() == ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
}
}
impl<R: Read> Read for ParallelEncoderReader<'_, R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if buf.is_empty() || self.finished {
return Ok(0);
}
loop {
if self.head == self.end && !self.eof {
self.fill()?;
}
let operation = if self.eof {
Operation::End
} else {
Operation::Continue
};
let progress = {
let pending = self.input.get(self.head..self.end).unwrap_or_default();
self.session.process(pending, buf, operation)?
};
self.head += progress.consumed;
if self.head == self.end {
self.head = 0;
self.end = 0;
}
if progress.status == ParallelEncoderStatus::Finished {
self.finished = true;
}
if progress.produced > 0 {
return Ok(progress.produced);
}
if self.finished {
return Ok(0);
}
if progress.status == ParallelEncoderStatus::Pending {
self.session.wait()?;
}
}
}
}