use core::num::NonZeroUsize;
use std::io::{
Error,
ErrorKind,
Result,
};
use qubit_io::{
BufferedInput,
Input,
};
use crate::{
Codec,
DecodeFailure,
};
pub(super) struct CodecDecodeDriver<'a, I>
where
I: Input,
I::Item: Copy + Default,
{
input: &'a mut BufferedInput<I>,
}
impl<'a, I> CodecDecodeDriver<'a, I>
where
I: Input,
I::Item: Copy + Default,
{
#[inline(always)]
pub(super) const fn new(input: &'a mut BufferedInput<I>) -> Self {
Self { input }
}
pub(super) fn read_one<C, M>(
&mut self,
codec: &mut C,
map_error: &mut M,
) -> Result<C::Value>
where
C: Codec<Unit = I::Item>,
M: FnMut(C::DecodeError) -> Error,
{
let min_units_per_value = C::MIN_UNITS_PER_VALUE;
let max_units_per_value =
C::MAX_DECODE_UNITS_PER_VALUE.max(min_units_per_value);
self.input
.try_reserve_capacity(min_units_per_value)
.map_err(|error| Error::new(ErrorKind::OutOfMemory, error))?;
loop {
let available = self.prepare_buffered_window(
min_units_per_value,
max_units_per_value,
)?;
let units = &self.input.unread()[..available];
debug_assert!(units.len() >= min_units_per_value);
let decode_result = unsafe {
codec.decode(units, 0)
};
match decode_result {
Ok((value, consumed)) => {
return self.accept(value, consumed, available);
}
Err(DecodeFailure::Incomplete {
source,
required_total,
}) => {
assert!(
required_total.get() <= C::MAX_DECODE_UNITS_PER_VALUE,
"Codec::decode incomplete required_total exceeded Codec::MAX_DECODE_UNITS_PER_VALUE",
);
if !self
.refill_after_incomplete(required_total, available)?
{
let available = self.input.unread_len();
unsafe {
self.input.consume(available);
}
return match source {
Some(source) => Err(map_error(source)),
None => Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to decode complete value",
)),
};
}
}
Err(DecodeFailure::Invalid { source, consumed }) => {
return self.reject::<C, M>(
source, consumed, available, map_error,
);
}
}
}
}
fn prepare_buffered_window(
&mut self,
min_units_per_value: usize,
max_units_per_value: usize,
) -> Result<usize> {
let available = self.input.unread_len();
if available < min_units_per_value
&& !self.input.fill_until(min_units_per_value)?
{
let available = self.input.unread_len();
unsafe {
self.input.consume(available);
}
return Err(Error::new(
ErrorKind::UnexpectedEof,
"failed to decode complete value",
));
}
if self.input.unread_len() < max_units_per_value
&& max_units_per_value <= self.input.capacity()
{
let _ = self.input.fill_until(max_units_per_value)?;
}
Ok(self.input.unread_len().min(max_units_per_value))
}
fn accept<Value>(
&mut self,
value: Value,
consumed: NonZeroUsize,
available: usize,
) -> Result<Value> {
if consumed.get() > available {
return Err(Error::new(
ErrorKind::InvalidData,
"codec consumed units exceed unread window",
));
}
unsafe {
self.input.consume(consumed.get());
}
Ok(value)
}
fn refill_after_incomplete(
&mut self,
required_total: NonZeroUsize,
available: usize,
) -> Result<bool> {
let required_total = required_total.get();
if available >= required_total {
return Err(Error::new(
ErrorKind::InvalidData,
"codec reported incomplete input within available window",
));
}
self.input
.try_reserve_capacity(required_total)
.map_err(|error| Error::new(ErrorKind::OutOfMemory, error))?;
self.input.fill_until(required_total)
}
fn reject<C, M>(
&mut self,
source: C::DecodeError,
consumed: Option<NonZeroUsize>,
available: usize,
map_error: &mut M,
) -> Result<C::Value>
where
C: Codec<Unit = I::Item>,
M: FnMut(C::DecodeError) -> Error,
{
if let Some(consumed) = consumed {
if consumed.get() > available {
return Err(Error::new(
ErrorKind::InvalidData,
"decode error consumed units exceed unread window",
));
}
unsafe {
self.input.consume(consumed.get());
}
}
Err(map_error(source))
}
}