use core::fmt;
use std::io::{
Error,
ErrorKind,
Result,
Seek,
SeekFrom,
Write,
};
use qubit_io::{
Buffer,
BufferedOutput,
Output,
Seekable,
};
use crate::{
TranscodeError,
TranscodeStatus,
Transcoder,
};
pub struct TranscodeEncodeOutput<O>
where
O: Output,
O::Item: Copy + Default,
{
output: BufferedOutput<O>,
}
impl<O> TranscodeEncodeOutput<O>
where
O: Output,
O::Item: Copy + Default,
{
#[must_use]
#[inline]
pub fn new(inner: O) -> Self {
Self {
output: BufferedOutput::new(inner),
}
}
#[must_use]
#[inline]
pub fn with_capacity(inner: O, capacity: usize) -> Self {
Self {
output: BufferedOutput::with_capacity(inner, capacity),
}
}
#[must_use]
#[inline(always)]
pub const fn inner(&self) -> &O {
self.output.inner()
}
#[inline(always)]
pub fn inner_mut(&mut self) -> &mut O {
self.output.inner_mut()
}
#[must_use]
#[inline(always)]
pub fn spare_capacity(&self) -> usize {
self.output.spare_capacity()
}
#[inline(always)]
#[must_use]
pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
self.output.spare_raw_parts_mut()
}
#[inline(always)]
pub unsafe fn advance(&mut self, count: usize) {
unsafe { self.output.advance(count) }
}
#[inline(always)]
pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
self.output.ensure_spare_capacity(count)
}
#[must_use]
#[inline]
pub fn into_parts(self) -> (O, Buffer<O::Item>) {
self.output.into_parts()
}
#[inline]
pub fn flush(&mut self) -> Result<()> {
self.output.flush()
}
pub unsafe fn transcode_from<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
input: &[Value],
input_index: usize,
count: usize,
) -> Result<usize>
where
E: Transcoder<Value, O::Item>,
M: FnMut(TranscodeError<E::Error>) -> Error,
{
debug_assert!(
qubit_io::UncheckedSlice::range_fits(
input.len(),
input_index,
count
),
"unchecked encode input range exceeds source buffer",
);
if count == 0 {
return Ok(0);
}
let input_end = input_index + count;
let input = &input[..input_end];
let mut read_total = 0;
while read_total < count {
self.output.ensure_spare_capacity(1)?;
let (units, output_index, available_output) =
self.output.spare_raw_parts_mut();
let remaining_input = count - read_total;
let progress = encoder
.transcode(input, input_index + read_total, units, output_index)
.map_err(&mut *map_error)?;
let read = progress.read();
let written = progress.written();
if read > remaining_input {
return Err(Error::new(
ErrorKind::InvalidData,
"transcoder consumed beyond input range",
));
}
if written > available_output {
return Err(Error::new(
ErrorKind::InvalidData,
"transcoder wrote beyond spare output",
));
}
unsafe {
self.output.advance(written);
}
read_total += read;
match progress.status() {
TranscodeStatus::Complete => return Ok(read_total),
TranscodeStatus::NeedOutput {
output_index: status_output_index,
additional,
available,
..
} => {
if status_output_index != output_index + written {
return Err(Error::new(
ErrorKind::InvalidData,
"transcoder reported inconsistent NeedOutput index",
));
}
let required = available
.checked_add(additional.get())
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"transcoder output requirement overflowed",
)
})?;
self.output.ensure_spare_capacity(required)?;
}
TranscodeStatus::NeedInput { .. } => {
return Err(Error::new(
ErrorKind::InvalidData,
"encoder unexpectedly requested more input",
));
}
}
}
Ok(read_total)
}
pub fn finish<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
) -> Result<()>
where
E: Transcoder<Value, O::Item>,
M: FnMut(TranscodeError<E::Error>) -> Error,
{
let required = encoder
.max_finish_output_len()
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
self.output.ensure_spare_capacity(required)?;
let (units, output_index, available) =
self.output.spare_raw_parts_mut();
debug_assert!(
available >= required,
"insufficient finish capacity reserved in spare output buffer",
);
let written = encoder
.finish(units, output_index)
.map_err(&mut *map_error)?;
assert!(written <= required, "finish wrote beyond its bound");
unsafe {
self.output.advance(written);
}
self.output.flush()
}
}
impl<O> TranscodeEncodeOutput<O>
where
O: Output<Item = u8> + Seekable<Item = u8>,
{
#[inline]
pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
self.output.seek_to(position)
}
}
impl<O> Write for TranscodeEncodeOutput<O>
where
O: Output<Item = u8>,
{
#[inline]
fn write(&mut self, input: &[u8]) -> Result<usize> {
Output::write(&mut self.output, input)
}
#[inline]
fn write_all(&mut self, input: &[u8]) -> Result<()> {
self.output.write_all(input)
}
#[inline]
fn flush(&mut self) -> Result<()> {
TranscodeEncodeOutput::flush(self)
}
}
impl<O> Seek for TranscodeEncodeOutput<O>
where
O: Output<Item = u8> + Seekable<Item = u8>,
{
#[inline]
fn seek(&mut self, position: SeekFrom) -> Result<u64> {
self.seek(position)
}
}
impl<O> fmt::Debug for TranscodeEncodeOutput<O>
where
O: Output,
O::Item: Copy + Default,
BufferedOutput<O>: fmt::Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("TranscodeEncodeOutput")
.field("output", &self.output)
.finish()
}
}