use core::fmt;
use std::io::{
Error,
ErrorKind,
Read,
Result,
Seek,
SeekFrom,
};
use qubit_io::{
Buffer,
BufferedInput,
Input,
Seekable,
UncheckedSlice,
};
use crate::{
TranscodeError,
TranscodeStatus,
Transcoder,
};
pub struct TranscodeDecodeInput<I>
where
I: Input,
I::Item: Copy + Default,
{
input: BufferedInput<I>,
}
impl<I> TranscodeDecodeInput<I>
where
I: Input,
I::Item: Copy + Default,
{
#[inline]
#[must_use]
pub fn new(inner: I) -> Self {
Self {
input: BufferedInput::new(inner),
}
}
#[inline]
#[must_use]
pub fn with_capacity(inner: I, capacity: usize) -> Self {
Self {
input: BufferedInput::with_capacity(inner, capacity),
}
}
#[inline(always)]
#[must_use]
pub const fn inner(&self) -> &I {
self.input.inner()
}
#[inline(always)]
pub fn inner_mut(&mut self) -> &mut I {
self.input.inner_mut()
}
#[inline(always)]
#[must_use]
pub fn available(&self) -> usize {
self.input.available()
}
#[inline(always)]
#[must_use]
pub fn unread(&self) -> &[I::Item] {
self.input.unread()
}
#[inline(always)]
#[must_use]
pub fn capacity(&self) -> usize {
self.input.capacity()
}
#[inline(always)]
pub fn fill_until(&mut self, count: usize) -> std::io::Result<bool> {
self.input.fill_until(count)
}
#[inline(always)]
pub fn consume(&mut self, count: usize) {
debug_assert!(
count <= self.available(),
"cannot consume beyond buffered input",
);
unsafe {
self.input.consume(count);
}
}
#[inline(always)]
pub unsafe fn copy_unread_to(
&mut self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) {
let unread = self.input.unread();
debug_assert!(
UncheckedSlice::range_fits(unread.len(), 0, count),
"unchecked unread copy range exceeds unread source",
);
debug_assert!(
UncheckedSlice::range_fits(output.len(), output_index, count),
"unchecked copy destination range exceeds output buffer",
);
unsafe {
UncheckedSlice::copy_nonoverlapping(
unread,
0,
output,
output_index,
count,
);
}
}
#[inline]
#[must_use]
pub fn into_parts(self) -> (I, Buffer<I::Item>) {
self.input.into_parts()
}
#[inline(always)]
pub unsafe fn read_unchecked(
&mut self,
output: &mut [I::Item],
output_index: usize,
count: usize,
) -> Result<usize> {
unsafe { self.input.read_unchecked(output, output_index, count) }
}
#[inline]
pub fn transcode_into<D, M, Value>(
&mut self,
decoder: &mut D,
map_error: &mut M,
output: &mut [Value],
output_index: usize,
count: usize,
) -> Result<usize>
where
D: Transcoder<I::Item, Value>,
M: FnMut(TranscodeError<D::Error>) -> Error,
{
let output_end = UncheckedSlice::checked_range_end(
output.len(),
output_index,
count,
"decoded output range exceeds destination buffer",
)?;
if count == 0 {
return Ok(0);
}
let output = &mut output[..output_end];
let mut written_total = 0;
loop {
if self.input.available() == 0 && !self.input.fill_more()? {
return Ok(written_total);
}
let units = self.input.unread();
let available_input = units.len();
let remaining_output = count - written_total;
let progress = decoder
.transcode(units, 0, output, output_index + written_total)
.map_err(&mut *map_error)?;
progress
.validate(
0,
available_input,
output_index + written_total,
remaining_output,
)
.map_err(|error| Error::new(ErrorKind::InvalidData, error))?;
let consumed = progress.read();
let written = progress.written();
unsafe {
self.input.consume(consumed);
}
written_total += written;
match progress.status() {
TranscodeStatus::Complete => {
if written_total == count || consumed == 0 {
return Ok(written_total);
}
}
TranscodeStatus::NeedOutput { .. } => {
return Ok(written_total);
}
TranscodeStatus::NeedInput { required, .. } => {
if self.input.fill_until(required.get())? {
continue;
}
return Ok(written_total);
}
}
}
}
#[inline]
pub fn finish_transcode_into<D, M, Value>(
&mut self,
decoder: &mut D,
map_error: &mut M,
output: &mut [Value],
output_index: usize,
count: usize,
) -> Result<usize>
where
D: Transcoder<I::Item, Value>,
M: FnMut(TranscodeError<D::Error>) -> Error,
{
let required = decoder
.max_finish_output_len()
.map_err(capacity_to_io_error)?;
let output_end = UncheckedSlice::checked_range_end(
output.len(),
output_index,
count,
"finish output range exceeds destination buffer",
)?;
let available = output.len().saturating_sub(output_index);
if available < required {
return Err(Error::new(
ErrorKind::InvalidData,
"insufficient output for decoder finish bound",
));
}
let output_end = output_end.max(output_index + required);
let output = &mut output[..output_end];
let written = decoder
.finish(output, output_index)
.map_err(&mut *map_error)?;
debug_assert!(written <= required, "finish wrote beyond its bound");
Ok(written)
}
}
impl<I> TranscodeDecodeInput<I>
where
I: Input<Item = u8> + Seekable<Item = u8>,
{
#[inline]
pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
self.input.seek_to(position)
}
}
impl<I> Read for TranscodeDecodeInput<I>
where
I: Input<Item = u8>,
{
#[inline]
fn read(&mut self, output: &mut [u8]) -> Result<usize> {
unsafe { self.input.read_unchecked(output, 0, output.len()) }
}
}
impl<I> Seek for TranscodeDecodeInput<I>
where
I: Input<Item = u8> + Seekable<Item = u8>,
{
#[inline]
fn seek(&mut self, position: SeekFrom) -> Result<u64> {
self.seek(position)
}
}
impl<I> fmt::Debug for TranscodeDecodeInput<I>
where
I: Input,
I::Item: Copy + Default,
BufferedInput<I>: fmt::Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("TranscodeDecodeInput")
.field("input", &self.input)
.finish()
}
}
fn capacity_to_io_error(error: crate::CapacityError) -> Error {
Error::new(ErrorKind::InvalidData, error)
}