use super::super::internal::{
encode_state::EncodeState,
encode_step::EncodeStep,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
EncodeContext,
TranscodeEncodeHooks,
TranscodeError,
TranscodeProgress,
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TranscodeEncodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
}
impl<C, H> TranscodeEncodeEngine<C, H>
where
C: Codec,
H: TranscodeEncodeHooks<C>,
{
#[must_use]
#[inline]
pub fn new(codec: C, hooks: H) -> Self {
assert_unit_bounds::<C>(&codec);
Self { codec, hooks }
}
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
self.hooks.max_output_len(&self.codec, input_len)
}
#[must_use]
#[inline(always)]
pub fn max_reset_output_len(&self) -> usize {
self.codec.max_encode_reset_units()
}
#[must_use]
#[inline(always)]
pub fn max_finish_output_len(&self) -> usize {
self.hooks.max_finish_output_len(&self.codec)
}
pub fn reset(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<H::Error>> {
let required = self.max_reset_output_len();
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.hooks.reset(&mut self.codec);
let written = unsafe {
self.hooks
.write_encode_reset(&mut self.codec, output, output_index)
}
.map_err(TranscodeError::domain)?;
assert!(
written <= required,
"Codec::encode_reset wrote beyond its reset bound",
);
Ok(written)
}
pub fn transcode(
&mut self,
input: &[C::Value],
input_index: usize,
output: &mut [C::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeError<H::Error>> {
TranscodeError::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let mut state =
EncodeState::new(input, input_index, output, output_index);
while state.has_input() {
let context = unsafe { state.context_unchecked() };
let step = self.encode_step(context)?;
if let Some(progress) = step.apply_to_state(&mut state) {
return Ok(progress);
}
}
Ok(state.complete_progress())
}
pub fn finish(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<H::Error>> {
let required = self.max_finish_output_len();
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let written = self
.hooks
.finish(&mut self.codec, output, output_index)
.map_err(TranscodeError::domain)?;
assert!(
written <= required,
"TranscodeEncodeEngine hook wrote beyond its finish bound",
);
Ok(written)
}
pub(super) fn encode_step(
&mut self,
context: EncodeContext<'_, C::Value, C::Unit>,
) -> Result<EncodeStep, TranscodeError<H::Error>> {
let plan = self
.hooks
.prepare_encode(
&mut self.codec,
context.input_value,
context.input_index,
)
.map_err(TranscodeError::domain)?;
let max_output_units = plan.max_output_units;
let available = context.available_output();
if available < max_output_units {
return Ok(EncodeStep::need_output(max_output_units, available));
}
let written = match unsafe {
self.hooks.write_encode(&mut self.codec, context, plan)
} {
Ok(written) => written,
Err(error) => return Err(TranscodeError::domain(error)),
};
assert!(
written <= max_output_units,
"TranscodeEncodeEngine hook wrote beyond its prepared capacity bound",
);
Ok(EncodeStep::written(written))
}
}
impl<C, H> Default for TranscodeEncodeEngine<C, H>
where
C: Codec + Default,
H: TranscodeEncodeHooks<C> + Default,
{
#[inline(always)]
fn default() -> Self {
Self::new(C::default(), H::default())
}
}