use super::{
buffered_encode_hooks::BufferedEncodeHooks,
encode_context::EncodeContext,
encode_plan::EncodePlan,
encode_state::EncodeState,
encode_step::EncodeStep,
finish_error::FinishError,
transcode_progress::TranscodeProgress,
};
use crate::{
CapacityError,
Codec,
codec::assert_unit_bounds,
};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BufferedEncodeEngine<C, H> {
pub(super) codec: C,
pub(super) hooks: H,
}
impl<C, H> BufferedEncodeEngine<C, H>
where
C: Codec,
H: BufferedEncodeHooks<C>,
{
#[must_use]
#[inline(always)]
pub const fn new(codec: C, hooks: H) -> Self {
Self { codec, hooks }
}
#[inline(always)]
pub(crate) fn prepare_value(
&mut self,
input_value: &C::Value,
input_index: usize,
) -> Result<EncodePlan<H::PlanAction>, H::Error> {
self.hooks.prepare_encode(&self.codec, input_value, input_index)
}
#[inline(always)]
pub(crate) unsafe fn write_prepared_value(
&mut self,
context: EncodeContext<'_, C::Value, C::Unit>,
plan: EncodePlan<H::PlanAction>,
) -> Result<usize, H::Error> {
unsafe { self.hooks.write_encode(&self.codec, context, plan) }
}
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
assert_unit_bounds::<C>(&self.codec);
self.hooks.max_output_len(&self.codec, input_len)
}
#[must_use]
#[inline(always)]
pub fn max_finish_output_len(&self) -> usize {
self.hooks.max_finish_output_len(&self.codec)
}
#[inline(always)]
pub fn reset(&mut self) {
self.hooks.reset(&self.codec);
}
pub fn transcode(
&mut self,
input: &[C::Value],
input_index: usize,
output: &mut [C::Unit],
output_index: usize,
) -> Result<TranscodeProgress, H::Error> {
if input_index > input.len() {
return Err(self.hooks.invalid_input_index(&self.codec, input_index, input.len()));
}
if output_index > output.len() {
return Err(self.hooks.invalid_output_index(&self.codec, output_index, output.len()));
}
assert_unit_bounds::<C>(&self.codec);
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())
}
#[inline(always)]
pub(crate) fn invalid_output_index(&mut self, index: usize, output_len: usize) -> H::Error {
self.hooks.invalid_output_index(&self.codec, index, output_len)
}
pub fn finish(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, FinishError<H::Error>> {
let required = self.max_finish_output_len();
FinishError::ensure_output_capacity(output.len(), output_index, required)?;
let output_end = output_index + required;
let output = &mut output[..output_end];
let written = self
.hooks
.finish(&self.codec, output, output_index)
.map_err(FinishError::source)?;
assert!(
written <= required,
"BufferedEncodeEngine hook wrote beyond its finish bound",
);
Ok(written)
}
#[inline]
pub(super) fn encode_step(
&mut self,
context: EncodeContext<'_, C::Value, C::Unit>,
) -> Result<EncodeStep, H::Error> {
let plan = self.prepare_value(context.input_value, context.input_index)?;
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 = unsafe { self.write_prepared_value(context, plan) }?;
assert!(
written <= max_output_units,
"BufferedEncodeEngine hook wrote beyond its prepared capacity bound",
);
Ok(EncodeStep::written(written))
}
}