use core::num::NonZeroUsize;
use super::super::internal::{
convert_state::ConvertState,
encode_attempt::EncodeAttempt,
lifecycle_guard::LifecycleGuard,
pending_value::PendingValue,
pending_value_slot::PendingValueSlot,
};
use super::{
TranscodeDecodeHooks,
TranscodeEncodeHooks,
transcode_decode_engine::TranscodeDecodeEngine,
transcode_encode_engine::TranscodeEncodeEngine,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
TranscodeConvertError,
TranscodeConvertErrorOf,
TranscodeConverter,
TranscodeFailure,
TranscodeProgress,
Transcoder,
};
fn add_convert_output_bounds(
first: usize,
second: usize,
) -> Result<usize, CapacityError> {
first
.checked_add(second)
.ok_or(CapacityError::OutputLengthOverflow)
}
fn sum_convert_output_bounds(
first: usize,
second: usize,
third: usize,
) -> Result<usize, CapacityError> {
let partial = add_convert_output_bounds(first, second)?;
add_convert_output_bounds(partial, third)
}
fn assert_reserved_output_drained(
progress: Option<TranscodeProgress>,
message: &'static str,
) {
assert!(progress.is_none(), "{message}");
}
#[derive(Debug)]
pub struct TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec,
E: Codec<Value = D::Value>,
DH: TranscodeDecodeHooks<D>,
EH: TranscodeEncodeHooks<E>,
{
decode_engine: TranscodeDecodeEngine<D, DH>,
encode_engine: TranscodeEncodeEngine<E, EH>,
pending: PendingValueSlot<D::Value>,
lifecycle: LifecycleGuard,
}
impl<D, E, DH, EH> TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec,
E: Codec<Value = D::Value>,
DH: TranscodeDecodeHooks<D>,
EH: TranscodeEncodeHooks<E>,
{
#[inline]
#[must_use]
pub fn new(
decoder: D,
encoder: E,
decode_hooks: DH,
encode_hooks: EH,
) -> Self {
assert_unit_bounds::<D>();
assert_unit_bounds::<E>();
Self {
decode_engine: TranscodeDecodeEngine::new(decoder, decode_hooks),
encode_engine: TranscodeEncodeEngine::new(encoder, encode_hooks),
pending: PendingValueSlot::empty(),
lifecycle: LifecycleGuard::new(),
}
}
#[inline(always)]
#[must_use]
pub fn source_codec(&self) -> &D {
self.decode_engine.codec()
}
#[inline(always)]
#[must_use]
pub fn source_codec_mut(&mut self) -> &mut D {
self.decode_engine.codec_mut()
}
#[inline(always)]
#[must_use]
pub fn target_codec(&self) -> &E {
self.encode_engine.codec()
}
#[inline(always)]
#[must_use]
pub fn target_codec_mut(&mut self) -> &mut E {
self.encode_engine.codec_mut()
}
#[inline(always)]
#[must_use]
pub const fn decode_hooks(&self) -> &DH {
self.decode_engine.hooks()
}
#[inline(always)]
#[must_use]
pub fn decode_hooks_mut(&mut self) -> &mut DH {
self.decode_engine.hooks_mut()
}
#[inline(always)]
#[must_use]
pub const fn encode_hooks(&self) -> &EH {
self.encode_engine.hooks()
}
#[inline(always)]
#[must_use]
pub fn encode_hooks_mut(&mut self) -> &mut EH {
self.encode_engine.hooks_mut()
}
#[inline]
#[must_use]
pub fn into_parts(self) -> (D, E, DH, EH) {
let Self {
decode_engine,
encode_engine,
..
} = self;
let (source, decode_hooks) = decode_engine.into_parts();
let (target, encode_hooks) = encode_engine.into_parts();
(source, target, decode_hooks, encode_hooks)
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
let pending_units = self.encode_engine.max_transcode_output_len(1)?;
let decoded_values =
self.decode_engine.max_transcode_output_len(input_len)?;
let converted_units = self
.encode_engine
.max_transcode_output_len(decoded_values)?;
add_convert_output_bounds(converted_units, pending_units)
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
let decode_reset_units = self
.encode_engine
.max_transcode_output_len(D::MAX_DECODE_RESET_VALUES)?;
let encode_reset_units = E::MAX_ENCODE_RESET_UNITS;
add_convert_output_bounds(decode_reset_units, encode_reset_units)
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
let pending_units = self.encode_engine.max_transcode_output_len(1)?;
let decoder_finish_values =
self.decode_engine.max_finish_output_len()?;
let decoder_finish_units = self
.encode_engine
.max_transcode_output_len(decoder_finish_values)?;
let encoder_finish_units =
self.encode_engine.max_finish_output_len()?;
sum_convert_output_bounds(
pending_units,
decoder_finish_units,
encoder_finish_units,
)
}
fn current_finish_output_len(&self) -> Result<usize, CapacityError> {
let pending_units =
self.pending.current_output_len(&self.encode_engine)?;
let decoder_finish_values =
self.decode_engine.max_finish_output_len()?;
let decoder_finish_units = self
.encode_engine
.max_transcode_output_len(decoder_finish_values)?;
let encoder_finish_units =
self.encode_engine.max_finish_output_len()?;
sum_convert_output_bounds(
pending_units,
decoder_finish_units,
encoder_finish_units,
)
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_total_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
let reset = self.max_reset_output_len()?;
let transcode = self.max_transcode_output_len(input_len)?;
let finish = self.max_finish_output_len()?;
sum_convert_output_bounds(reset, transcode, finish)
}
pub fn reset(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, TranscodeConvertErrorOf<D, E>>
where
D::Value: Default,
{
let required = self.max_reset_output_len()?;
TranscodeFailure::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.lifecycle.on_reset_start();
self.pending.clear();
let empty_input: &[D::Unit] = &[];
let mut state = ConvertState::new(empty_input, 0, output, output_index);
let output_cursor = state.output_cursor();
let encoder_written = self
.encode_engine
.reset(state.output_mut(), output_cursor)?;
state.advance_output(encoder_written);
self.drain_decoder_reset(&mut state)?;
self.lifecycle.on_reset_success();
Ok(state.written())
}
pub fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
self.transcode_with_eof(input, input_index, output, output_index, false)
}
#[inline(always)]
pub fn transcode_eof(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
self.transcode_with_eof(input, input_index, output, output_index, true)
}
fn transcode_with_eof(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
end_of_input: bool,
) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
self.lifecycle.on_transcode()?;
TranscodeFailure::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let mut state =
ConvertState::new(input, input_index, output, output_index);
if let Some(progress) = self.drain_pending(&mut state)? {
return Ok(progress);
}
let min_input_units = NonZeroUsize::new(D::MIN_UNITS_PER_VALUE)
.expect("Codec::MIN_UNITS_PER_VALUE is non-zero");
let min_input_len = min_input_units.get();
while state.has_input() {
let available = state.available_input();
if available < min_input_len && !end_of_input {
return Ok(state.need_input_progress(min_input_units));
}
let previous_read = state.read();
if let Some(progress) =
self.convert_next(&mut state, end_of_input)?
{
return Ok(progress);
}
debug_assert!(
state.read() > previous_read,
"TranscodeConvertEngine conversion step must consume input or stop",
);
}
Ok(state.complete_progress())
}
pub fn finish(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, TranscodeConvertErrorOf<D, E>>
where
D::Value: Default,
{
self.lifecycle.on_finish_attempt()?;
let required = self.current_finish_output_len()?;
TranscodeFailure::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.lifecycle.on_finish_start();
let empty_input: &[D::Unit] = &[];
let mut state = ConvertState::new(empty_input, 0, output, output_index);
let progress = self.drain_pending(&mut state)?;
assert_reserved_output_drained(
progress,
"converter finish bound must reserve space for pending values",
);
self.drain_decoder_finish(&mut state)?;
let output_cursor = state.output_cursor();
let written = self
.encode_engine
.finish(state.output_mut(), output_cursor)?;
state.advance_output(written);
self.lifecycle.on_finish_success();
Ok(state.written())
}
#[inline]
pub fn transcode_complete_into(
&mut self,
input: &[D::Unit],
output: &mut [E::Unit],
) -> Result<usize, TranscodeConvertErrorOf<D, E>>
where
D::Value: Default,
{
<Self as Transcoder>::transcode_complete_into(self, input, output)
}
fn drain_decoder_reset(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<(), TranscodeConvertErrorOf<D, E>>
where
D::Value: Default,
{
let value_count = D::MAX_DECODE_RESET_VALUES;
if value_count == 0 {
self.decode_engine.reset(&mut [], 0)?;
return Ok(());
}
let mut reset_values: Vec<D::Value> =
(0..value_count).map(|_| D::Value::default()).collect();
let written = self.decode_engine.reset(&mut reset_values, 0)?;
for value in reset_values.into_iter().take(written) {
let pending = PendingValue::new(value, 0);
let progress = self.encode_pending(pending, state)?;
assert_reserved_output_drained(
progress,
"converter reset bound must reserve space for decode reset values",
);
}
Ok(())
}
#[inline(always)]
fn convert_next(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
end_of_input: bool,
) -> Result<Option<TranscodeProgress>, TranscodeConvertErrorOf<D, E>> {
let (outcome, pending) = self.decode_engine.decode_one(
state.input(),
state.decode_context(),
end_of_input,
PendingValue::new,
)?;
if let Some(pending) = pending {
self.pending.put(pending);
}
if let Some(progress) = state.apply_decode_outcome(outcome) {
return Ok(Some(progress));
}
let Some(pending) = self.pending.take() else {
return Ok(None);
};
self.encode_pending(pending, state)
}
#[inline(always)]
fn drain_pending(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<Option<TranscodeProgress>, TranscodeConvertErrorOf<D, E>> {
let Some(pending) = self.pending.take() else {
return Ok(None);
};
self.encode_pending(pending, state)
}
fn drain_decoder_finish(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<(), TranscodeConvertErrorOf<D, E>>
where
D::Value: Default,
{
let value_count = self.decode_engine.max_finish_output_len()?;
if value_count == 0 {
self.decode_engine.finish(&mut [], 0)?;
return Ok(());
}
let mut decoded: Vec<D::Value> =
(0..value_count).map(|_| D::Value::default()).collect();
let written = self.decode_engine.finish(&mut decoded, 0)?;
for value in decoded.into_iter().take(written) {
let pending = PendingValue::new(value, 0);
let progress = self.encode_pending(pending, state)?;
assert_reserved_output_drained(
progress,
"converter finish bound must reserve space for decode finish values",
);
}
Ok(())
}
fn encode_pending(
&mut self,
pending: PendingValue<D::Value>,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<Option<TranscodeProgress>, TranscodeConvertErrorOf<D, E>> {
let input_index = pending.input_index();
let output_index = state.output_cursor();
let attempt = EncodeAttempt::new(
pending.value(),
input_index,
state.output_mut(),
output_index,
);
let outcome = match self.encode_engine.encode_one(attempt) {
Ok(outcome) => outcome,
Err(error) => {
return Err(
TranscodeConvertError::from_encode_error_with_value(
error,
pending.into_value(),
),
);
}
};
let progress = state.apply_encode_outcome(outcome);
if progress.is_some() {
self.pending.put(pending);
}
Ok(progress)
}
}
impl<D, E, DH, EH> Default for TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec + Default,
E: Codec<Value = D::Value> + Default,
DH: TranscodeDecodeHooks<D> + Default,
EH: TranscodeEncodeHooks<E> + Default,
{
#[inline(always)]
fn default() -> Self {
Self::new(D::default(), E::default(), DH::default(), EH::default())
}
}
impl<D, E, DH, EH> Transcoder for TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec,
E: Codec<Value = D::Value>,
D::Value: Default,
DH: TranscodeDecodeHooks<D>,
EH: TranscodeEncodeHooks<E>,
{
type Input = D::Unit;
type Output = E::Unit;
type Error = TranscodeConvertErrorOf<D, E>;
#[inline(always)]
fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_transcode_output_len(self, input_len)
}
#[inline(always)]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_finish_output_len(self)
}
#[inline(always)]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_reset_output_len(self)
}
#[inline(always)]
fn reset(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
TranscodeConvertEngine::reset(self, output, output_index)
}
#[inline(always)]
fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
TranscodeConvertEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn transcode_eof(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeConvertErrorOf<D, E>> {
TranscodeConvertEngine::transcode_eof(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
TranscodeConvertEngine::finish(self, output, output_index)
}
}
impl<D, E, DH, EH> TranscodeConverter for TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec,
E: Codec<Value = D::Value>,
D::Value: Default,
DH: TranscodeDecodeHooks<D>,
EH: TranscodeEncodeHooks<E>,
{
type DecodeError = D::DecodeError;
type EncodeError = E::EncodeError;
type Value = D::Value;
}