use core::num::NonZeroUsize;
use crate::{
CapacityError,
Codec,
CodecDecodeError,
CodecEncodeError,
DecodeFailure,
TranscodeError,
};
pub type CodecEncodeValueResult<E> =
Result<usize, TranscodeError<CodecEncodeError<E>>>;
pub type CodecDecodeValueWithFlushResult<V, E> =
Result<(V, NonZeroUsize, usize), TranscodeError<CodecDecodeError<E>>>;
pub type CodecDecodeExactValueWithFlushResult<V, E> =
Result<(V, usize), TranscodeError<CodecDecodeError<E>>>;
pub trait CodecValueExt: Codec {
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
fn max_encode_value_units(&self) -> Result<usize, CapacityError> {
Self::MAX_ENCODE_RESET_UNITS
.checked_add(Self::MAX_UNITS_PER_VALUE.get())
.ok_or(CapacityError::OutputLengthOverflow)
}
fn encode_value_with_reset(
&mut self,
value: &Self::Value,
output: &mut [Self::Unit],
output_index: usize,
) -> CodecEncodeValueResult<Self::EncodeError> {
if !self.can_encode_value(value) {
return Err(TranscodeError::domain(
CodecEncodeError::unencodable_value(0),
));
}
let reset_units = Self::MAX_ENCODE_RESET_UNITS;
let value_units = self.encode_len(value).get();
let required = reset_units
.checked_add(value_units)
.ok_or_else(TranscodeError::output_length_overflow)?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let reset_written = unsafe {
self.encode_reset(output, output_index)
}
.map_err(|error| {
TranscodeError::domain(CodecEncodeError::encode_reset(error))
})?;
assert!(
reset_written <= reset_units,
"Codec::encode_reset wrote beyond its reset bound",
);
let value_written = unsafe {
self.encode(value, output, output_index + reset_written)
}
.map_err(|error| {
TranscodeError::domain(CodecEncodeError::encode(error, 0))
})?
.get();
assert!(
value_written == value_units,
"Codec::encode wrote a different length than Codec::encode_len",
);
Ok(reset_written + value_written)
}
fn decode_value_with_flush(
&mut self,
input: &[Self::Unit],
input_index: usize,
flush_output: &mut [Self::Value],
flush_output_index: usize,
) -> CodecDecodeValueWithFlushResult<Self::Value, Self::DecodeError> {
TranscodeError::ensure_input_index(input.len(), input_index)?;
let min_units = Self::MIN_UNITS_PER_VALUE.get();
ensure_min_input(input.len(), input_index, min_units)?;
let flush_cap = Self::MAX_DECODE_FLUSH_VALUES;
TranscodeError::ensure_output_capacity(
flush_output.len(),
flush_output_index,
flush_cap,
)?;
let (value, consumed) = unsafe {
self.decode(input, input_index)
}
.map_err(|failure| {
TranscodeError::domain(map_decode_failure(
failure,
input_index,
input.len() - input_index,
))
})?;
let available = input.len() - input_index;
assert!(
consumed.get() <= available,
"Codec::decode consumed beyond available input",
);
let flushed = unsafe {
self.decode_flush(flush_output, flush_output_index)
}
.map_err(|error| {
TranscodeError::domain(CodecDecodeError::decode_flush(error))
})?;
assert!(
flushed <= flush_cap,
"Codec::decode_flush wrote beyond its flush bound",
);
Ok((value, consumed, flushed))
}
fn decode_exact_value_with_flush(
&mut self,
input: &[Self::Unit],
flush_output: &mut [Self::Value],
flush_output_index: usize,
) -> CodecDecodeExactValueWithFlushResult<Self::Value, Self::DecodeError>
{
let min_units = Self::MIN_UNITS_PER_VALUE.get();
ensure_min_input(input.len(), 0, min_units)?;
let flush_cap = Self::MAX_DECODE_FLUSH_VALUES;
TranscodeError::ensure_output_capacity(
flush_output.len(),
flush_output_index,
flush_cap,
)?;
let (value, consumed) = unsafe {
self.decode(input, 0)
}
.map_err(|failure| {
TranscodeError::domain(map_decode_failure(failure, 0, input.len()))
})?;
assert!(
consumed.get() <= input.len(),
"Codec::decode consumed beyond available input",
);
ensure_no_trailing_input(consumed.get(), input.len())?;
let flushed = unsafe {
self.decode_flush(flush_output, flush_output_index)
}
.map_err(|error| {
TranscodeError::domain(CodecDecodeError::decode_flush(error))
})?;
assert!(
flushed <= flush_cap,
"Codec::decode_flush wrote beyond its flush bound",
);
Ok((value, flushed))
}
}
impl<C> CodecValueExt for C where C: Codec + ?Sized {}
#[inline]
fn map_decode_failure<E>(
failure: DecodeFailure<E>,
input_index: usize,
available: usize,
) -> CodecDecodeError<E> {
match failure {
DecodeFailure::Incomplete { required_total } => {
CodecDecodeError::incomplete(
input_index,
required_total.get(),
available,
)
}
DecodeFailure::Invalid { source, .. } => {
CodecDecodeError::decode(source, input_index)
}
}
}
#[inline]
fn ensure_min_input<E>(
input_len: usize,
input_index: usize,
min_required: usize,
) -> Result<(), TranscodeError<CodecDecodeError<E>>> {
let available = input_len.saturating_sub(input_index);
if available < min_required {
return Err(TranscodeError::domain(CodecDecodeError::incomplete(
input_index,
min_required,
available,
)));
}
Ok(())
}
#[inline]
fn ensure_no_trailing_input<E>(
consumed: usize,
total: usize,
) -> Result<(), TranscodeError<CodecDecodeError<E>>> {
let remaining = total.saturating_sub(consumed);
if remaining != 0 {
return Err(TranscodeError::domain(CodecDecodeError::trailing_input(
consumed, remaining,
)));
}
Ok(())
}