use core::{
fmt,
num::NonZeroUsize,
};
use std::collections::TryReserveError;
use std::io::{
Error,
ErrorKind,
Result,
Seek,
SeekFrom,
Write,
};
use qubit_io::{
Buffer,
BufferedOutput,
Output,
Seekable,
UncheckedSlice,
};
use crate::{
CapacityError,
Codec,
TranscodeEncodeError,
TranscodeEncodeErrorOf,
TranscodeEncoder,
TranscodeStatus,
Transcoder,
value::codec_value_lifecycle::{
encode_complete_value_into_reserved,
max_complete_encode_units,
},
};
use super::transcode_progress_validation::validate_encode_progress;
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]
pub fn new(inner: O) -> Self {
Self {
output: BufferedOutput::new(inner),
}
}
#[must_use]
pub fn with_capacity(inner: O, capacity: usize) -> Self {
Self {
output: BufferedOutput::with_capacity(inner, capacity),
}
}
#[inline]
pub fn try_with_capacity(
inner: O,
capacity: usize,
) -> std::result::Result<Self, TryReserveError> {
Ok(Self {
output: BufferedOutput::try_with_capacity(inner, capacity)?,
})
}
#[must_use]
pub const fn inner(&self) -> &O {
self.output.inner()
}
#[must_use]
pub fn spare_capacity(&self) -> usize {
self.output.spare_capacity()
}
#[must_use]
pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
self.output.spare_raw_parts_mut()
}
pub unsafe fn advance(&mut self, count: usize) {
unsafe { self.output.advance(count) }
}
pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
let pending = self.output.capacity() - self.output.spare_capacity();
let required_capacity = pending.saturating_add(count);
self.output
.try_reserve_capacity(required_capacity)
.map_err(allocation_to_io_error)?;
self.output.ensure_spare_capacity(count)
}
fn ensure_transcode_spare_capacity(
&mut self,
required: NonZeroUsize,
) -> Result<()> {
self.ensure_spare_capacity(required.get())
}
#[must_use = "the returned output and pending buffer must be handled"]
pub fn into_parts(self) -> (O, Buffer<O::Item>) {
self.output.into_parts()
}
pub fn flush(&mut self) -> Result<()> {
self.output.flush()
}
pub fn write_encoded_with<C, M>(
&mut self,
codec: &mut C,
value: &C::Value,
mut map_error: M,
) -> Result<()>
where
C: Codec<Unit = O::Item>,
M: FnMut(C::EncodeError) -> Error,
{
let max_units = map_encode_value_result(
max_complete_encode_units::<C>()
.map_err(TranscodeEncodeErrorOf::<C>::from),
&mut map_error,
)?;
self.ensure_spare_capacity(max_units)?;
let (units, output_index, available) =
self.output.spare_raw_parts_mut();
debug_assert!(
available >= max_units,
"reserved spare buffer is smaller than codec upper bound",
);
let written = map_encode_value_result(
encode_complete_value_into_reserved(
codec,
value,
units,
output_index,
max_units,
),
&mut map_error,
)?;
unsafe {
self.output.advance(written);
}
Ok(())
}
pub fn reset<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
) -> Result<()>
where
E: Transcoder<Input = Value, Output = O::Item>,
M: FnMut(E::Error) -> Error,
{
let required = encoder
.max_reset_output_len()
.map_err(capacity_error_to_invalid_data)?;
self.ensure_spare_capacity(required)?;
let (units, output_index, available) =
self.output.spare_raw_parts_mut();
debug_assert!(
available >= required,
"insufficient reset capacity reserved in spare output buffer",
);
let written = encoder
.reset(units, output_index)
.map_err(&mut *map_error)?;
assert!(written <= required, "reset wrote beyond its bound");
unsafe {
self.output.advance(written);
}
Ok(())
}
pub fn transcode<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
input: &[Value],
input_index: usize,
count: usize,
) -> Result<usize>
where
E: TranscodeEncoder<Input = Value, Output = O::Item>,
M: FnMut(E::Error) -> Error,
{
let input_end = UncheckedSlice::checked_range_end(
input.len(),
input_index,
count,
"encode input range exceeds source buffer",
)?;
if count == 0 {
return Ok(0);
}
let input = &input[..input_end];
let mut read_total = 0;
let mut required_spare = NonZeroUsize::MIN;
while read_total < count {
self.ensure_transcode_spare_capacity(required_spare)?;
let (units, output_index, available_output) =
self.output.spare_raw_parts_mut();
debug_assert!(
available_output >= required_spare.get(),
"reserved spare buffer is smaller than required encoder output",
);
let remaining_input = count - read_total;
let progress = encoder
.transcode(input, input_index + read_total, units, output_index)
.map_err(&mut *map_error)?;
let progress = validate_encode_progress(
progress,
input_index + read_total,
remaining_input,
output_index,
available_output,
)?;
let read = progress.read();
let written = progress.written();
unsafe {
self.output.advance(written);
}
read_total += read;
if progress.is_complete() {
return Ok(read_total);
}
if let TranscodeStatus::NeedOutput { required, .. } =
progress.status()
{
required_spare = required;
if read_total == count {
self.ensure_transcode_spare_capacity(required)?;
}
}
}
Ok(read_total)
}
pub fn finish<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
) -> Result<()>
where
E: Transcoder<Input = Value, Output = O::Item>,
M: FnMut(E::Error) -> Error,
{
self.finish_to_buffer(encoder, map_error)?;
self.output.flush()
}
pub fn finish_to_buffer<E, M, Value>(
&mut self,
encoder: &mut E,
map_error: &mut M,
) -> Result<()>
where
E: Transcoder<Input = Value, Output = O::Item>,
M: FnMut(E::Error) -> Error,
{
let required = match encoder.max_finish_output_len() {
Ok(required) => required,
Err(error) => return Err(capacity_error_to_invalid_data(error)),
};
self.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);
}
Ok(())
}
}
impl<O> TranscodeEncodeOutput<O>
where
O: Output<Item = u8> + Seekable<Unit = u8>,
{
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>,
{
fn write(&mut self, input: &[u8]) -> Result<usize> {
Output::write(&mut self.output, input)
}
fn write_all(&mut self, input: &[u8]) -> Result<()> {
let mut written = 0;
while written < input.len() {
let count = Output::write(&mut self.output, &input[written..])?;
if count == 0 {
return Err(Error::from(ErrorKind::WriteZero));
}
assert!(
count <= input.len() - written,
"Output::write returned a count beyond the input length",
);
written += count;
}
Ok(())
}
fn flush(&mut self) -> Result<()> {
TranscodeEncodeOutput::flush(self)
}
}
impl<O> Seek for TranscodeEncodeOutput<O>
where
O: Output<Item = u8> + Seekable<Unit = u8>,
{
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()
}
}
fn allocation_to_io_error(error: std::collections::TryReserveError) -> Error {
Error::new(ErrorKind::OutOfMemory, error)
}
fn capacity_error_to_invalid_data(error: CapacityError) -> Error {
Error::new(ErrorKind::InvalidData, error)
}
fn map_encode_value_result<T, E, Value>(
result: core::result::Result<T, TranscodeEncodeError<E, Value>>,
map_error: &mut dyn FnMut(E) -> Error,
) -> Result<T> {
match result {
Ok(value) => Ok(value),
Err(error) => Err(map_encode_value_error(error, map_error)),
}
}
#[inline(never)]
fn map_encode_value_error<E, Value>(
error: TranscodeEncodeError<E, Value>,
map_error: &mut dyn FnMut(E) -> Error,
) -> Error {
match error {
TranscodeEncodeError::Domain(error) => map_error(error.into_source()),
TranscodeEncodeError::Unencodable { .. } => {
Error::new(ErrorKind::InvalidInput, "codec cannot encode value")
}
TranscodeEncodeError::Failure(_) => {
Error::new(ErrorKind::InvalidInput, "codec output bound overflow")
}
}
}