use super::super::internal::{
convert_error_of::ConvertErrorOf,
convert_state::ConvertState,
lifecycle::LifecycleGuard,
pending_value::PendingValue,
pending_value_slot::PendingValueSlot,
};
use super::{
transcode_decode_engine::TranscodeDecodeEngine,
transcode_encode_engine::TranscodeEncodeEngine,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
EncodeContext,
TranscodeConvertEngineError,
TranscodeDecodeEngineError,
TranscodeDecodeHooks,
TranscodeEncodeEngineError,
TranscodeEncodeHooks,
TranscodeError,
TranscodeProgress,
Transcoder,
};
#[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(),
}
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
let pending_units = self.pending_output_len()?;
let decoded_values = self.decode_engine.max_output_len(input_len)?;
let converted_units =
self.encode_engine.max_output_len(decoded_values)?;
converted_units
.checked_add(pending_units)
.ok_or(CapacityError::OutputLengthOverflow)
}
#[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_output_len(D::MAX_DECODE_RESET_VALUES)?;
let encode_reset_units = E::MAX_ENCODE_RESET_UNITS;
decode_reset_units
.checked_add(encode_reset_units)
.ok_or(CapacityError::OutputLengthOverflow)
}
#[must_use = "capacity planning can fail on overflow"]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
let pending_units = self.pending_output_len()?;
let decoder_finish_values =
self.decode_engine.max_finish_output_len()?;
let decoder_finish_units =
self.encode_engine.max_output_len(decoder_finish_values)?;
let encoder_finish_units =
self.encode_engine.max_finish_output_len()?;
let pending_and_decoder = pending_units
.checked_add(decoder_finish_units)
.ok_or(CapacityError::OutputLengthOverflow)?;
pending_and_decoder
.checked_add(encoder_finish_units)
.ok_or(CapacityError::OutputLengthOverflow)
}
pub fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, ConvertErrorOf<D, E, DH, EH>> {
self.lifecycle.on_transcode();
TranscodeError::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 = D::MIN_UNITS_PER_VALUE;
while state.has_input() {
let available = state.available_input();
if available < min_input_units.get() {
return Ok(
state.need_input_progress(min_input_units, available)
);
}
let previous_read = state.read();
if let Some(progress) = self.convert_next(&mut state)? {
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, ConvertErrorOf<D, E, DH, EH>>
where
D::Value: Default,
{
self.lifecycle.on_finish_attempt();
let required = self.max_finish_output_len()?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let empty_input: &[D::Unit] = &[];
let mut state = ConvertState::new(empty_input, 0, output, output_index);
if self.drain_pending(&mut state)?.is_some() {
unreachable!(
"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)
.map_err(|error| {
error.map_domain(TranscodeConvertEngineError::encode)
})?;
state.advance_output(written);
self.lifecycle.on_finish_success();
Ok(state.written())
}
pub fn reset(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, ConvertErrorOf<D, E, DH, EH>>
where
D::Value: Default,
{
self.lifecycle.on_reset();
let required = self.max_reset_output_len()?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.pending.clear();
let empty_input: &[D::Unit] = &[];
let mut state = ConvertState::new(empty_input, 0, output, output_index);
self.drain_decoder_reset(&mut state)?;
let output_cursor = state.output_cursor();
let encoder_written = self
.encode_engine
.reset(state.output_mut(), output_cursor)
.map_err(|error| {
error.map_domain(TranscodeConvertEngineError::encode)
})?;
state.advance_output(encoder_written);
Ok(state.written())
}
fn drain_decoder_reset(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<(), ConvertErrorOf<D, E, DH, EH>>
where
D::Value: Default,
{
let value_count = D::MAX_DECODE_RESET_VALUES;
if value_count == 0 {
self.decode_engine.reset(&mut [], 0).map_err(|error| {
error.map_domain(TranscodeConvertEngineError::decode)
})?;
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).map_err(
|error| error.map_domain(TranscodeConvertEngineError::decode),
)?;
for value in reset_values.into_iter().take(written) {
let pending = PendingValue::new(value, 0);
if self.encode_pending(pending, state)?.is_some() {
unreachable!(
"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>,
) -> Result<Option<TranscodeProgress>, ConvertErrorOf<D, E, DH, EH>> {
let step = self
.decode_engine
.decode_step(state.input(), state.decode_context())
.map_err(|error| {
error.map_domain(TranscodeConvertEngineError::decode)
})?;
state.apply_decode_step(step, |pending, state| {
self.encode_pending(pending, state)
})
}
#[inline(always)]
fn pending_output_len(&self) -> Result<usize, CapacityError> {
self.pending.max_output_len(&self.encode_engine)
}
#[inline(always)]
fn drain_pending(
&mut self,
state: &mut ConvertState<'_, D::Unit, E::Unit>,
) -> Result<Option<TranscodeProgress>, ConvertErrorOf<D, E, DH, EH>> {
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<(), ConvertErrorOf<D, E, DH, EH>>
where
D::Value: Default,
{
let value_count = self.decode_engine.max_finish_output_len()?;
if value_count == 0 {
self.decode_engine.finish(&mut [], 0).map_err(|error| {
error.map_domain(TranscodeConvertEngineError::decode)
})?;
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)
.map_err(|error| {
error.map_domain(TranscodeConvertEngineError::decode)
})?;
for value in decoded.into_iter().take(written) {
let pending = PendingValue::new(value, 0);
if self.encode_pending(pending, state)?.is_some() {
unreachable!(
"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>, ConvertErrorOf<D, E, DH, EH>> {
let input_index = pending.input_index();
let output_index = state.output_cursor();
let context = EncodeContext::new(
pending.value(),
input_index,
state.output_mut(),
output_index,
);
let outcome =
self.encode_engine.encode_one(context).map_err(|error| {
TranscodeError::domain(TranscodeConvertEngineError::encode(
TranscodeEncodeEngineError::hook(error),
))
})?;
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<D::Unit, E::Unit>
for TranscodeConvertEngine<D, E, DH, EH>
where
D: Codec,
E: Codec<Value = D::Value>,
D::Value: Default,
DH: TranscodeDecodeHooks<D>,
EH: TranscodeEncodeHooks<E>,
{
type Error = TranscodeConvertEngineError<
TranscodeDecodeEngineError<D::DecodeError, DH::Error>,
TranscodeEncodeEngineError<E::EncodeError, EH::Error>,
>;
#[inline(always)]
fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeConvertEngine::max_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, TranscodeError<Self::Error>> {
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, TranscodeError<Self::Error>> {
TranscodeConvertEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<Self::Error>> {
TranscodeConvertEngine::finish(self, output, output_index)
}
}