use core::num::NonZeroUsize;
use qubit_utils::try_reserve_vec;
use super::super::internal::convert_state::ConvertState;
use super::super::internal::encode_attempt::EncodeAttempt;
use super::super::internal::lifecycle_guard::LifecycleGuard;
use super::super::internal::pending_value::PendingValue;
use super::super::internal::pending_value_slot::PendingValueSlot;
use super::TranscodeDecodeHooks;
use super::TranscodeEncodeHooks;
use super::transcode_decode_engine::TranscodeDecodeEngine;
use super::transcode_encode_engine::TranscodeEncodeEngine;
use crate::CapacityError;
use crate::Codec;
use crate::TranscodeConvertError;
use crate::TranscodeConvertErrorOf;
use crate::TranscodeConverter;
use crate::TranscodeFailure;
use crate::TranscodeProgress;
use crate::Transcoder;
use crate::codec::assert_unit_bounds;
#[inline]
fn add_convert_output_bounds(first: usize, second: usize) -> Result<usize, CapacityError> {
first.checked_add(second).ok_or(CapacityError::OutputLengthOverflow)
}
#[inline]
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)
}
#[inline]
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]
#[must_use]
pub fn source_codec(&self) -> &D {
self.decode_engine.codec()
}
#[inline]
#[must_use]
pub fn source_codec_mut(&mut self) -> &mut D {
self.decode_engine.codec_mut()
}
#[inline]
#[must_use]
pub fn target_codec(&self) -> &E {
self.encode_engine.codec()
}
#[inline]
#[must_use]
pub fn target_codec_mut(&mut self) -> &mut E {
self.encode_engine.codec_mut()
}
#[inline]
#[must_use]
pub const fn decode_hooks(&self) -> &DH {
self.decode_engine.hooks()
}
#[inline]
#[must_use]
pub fn decode_hooks_mut(&mut self) -> &mut DH {
self.decode_engine.hooks_mut()
}
#[inline]
#[must_use]
pub const fn encode_hooks(&self) -> &EH {
self.encode_engine.hooks()
}
#[inline]
#[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)
}
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)
}
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)
}
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)
}
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())
}
#[inline]
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]
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)
}
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 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)
}
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())
}
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::new();
try_reserve_vec(&mut reset_values, value_count).map_err(|_| TranscodeFailure::allocation_failed())?;
reset_values.resize_with(value_count, D::Value::default);
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]
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]
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::new();
try_reserve_vec(&mut decoded, value_count).map_err(|_| TranscodeFailure::allocation_failed())?;
decoded.resize_with(value_count, D::Value::default);
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,
{
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]
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_transcode_output_len(self, input_len)
}
#[inline]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_finish_output_len(self)
}
#[inline]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_reset_output_len(self)
}
#[inline]
fn reset(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, TranscodeConvertErrorOf<D, E>> {
TranscodeConvertEngine::reset(self, output, output_index)
}
#[inline]
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]
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]
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;
}