use super::super::encode_context::EncodeContext;
use super::super::internal::{
encode_state::EncodeState,
lifecycle::LifecycleGuard,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
CodecEncodeError,
EncodeOutcome,
TranscodeEncodeEngineError,
TranscodeEncodeHooks,
TranscodeError,
TranscodeProgress,
Transcoder,
};
type EncodeEngineErrorOf<C, H> = TranscodeEncodeEngineError<
<C as Codec>::EncodeError,
<H as TranscodeEncodeHooks<C>>::Error,
>;
#[derive(Debug)]
pub struct TranscodeEncodeEngine<C, H> {
codec: C,
hooks: H,
lifecycle: LifecycleGuard,
}
impl<C, H> TranscodeEncodeEngine<C, H>
where
C: Codec,
H: TranscodeEncodeHooks<C>,
{
#[inline]
#[must_use]
pub fn new(codec: C, hooks: H) -> Self {
assert_unit_bounds::<C>();
Self {
codec,
hooks,
lifecycle: LifecycleGuard::new(),
}
}
#[inline(always)]
pub(crate) fn encode_one(
&mut self,
context: EncodeContext<'_, C::Value, C::Unit>,
) -> Result<EncodeOutcome, H::Error> {
self.hooks.encode_value(&mut self.codec, context)
}
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
self.hooks.max_output_len(&self.codec, input_len)
}
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(C::MAX_ENCODE_RESET_UNITS)
}
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
C::MAX_ENCODE_FLUSH_UNITS
.checked_add(self.hooks.max_finish_output_len(&self.codec))
.ok_or(CapacityError::OutputLengthOverflow)
}
pub fn reset(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<EncodeEngineErrorOf<C, H>>> {
self.lifecycle.on_reset();
let required = self.max_reset_output_len()?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.hooks.reset_hooks(&mut self.codec);
let written = unsafe {
self.codec.encode_reset(output, output_index)
}
.map_err(|error| {
TranscodeError::domain(TranscodeEncodeEngineError::codec(
CodecEncodeError::encode_reset(error),
))
})?;
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<EncodeEngineErrorOf<C, H>>>
{
self.lifecycle.on_transcode();
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 outcome = self
.hooks
.encode_value(&mut self.codec, context)
.map_err(|error| {
TranscodeError::domain(TranscodeEncodeEngineError::hook(
error,
))
})?;
if let Some(progress) = state.apply_encode_outcome(outcome) {
return Ok(progress);
}
}
Ok(state.complete_progress())
}
pub fn finish(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<EncodeEngineErrorOf<C, H>>> {
self.lifecycle.on_finish_attempt();
let required = self.max_finish_output_len()?;
TranscodeError::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
let flushed = unsafe {
self.codec.encode_flush(output, output_index)
}
.map_err(|error| {
TranscodeError::domain(TranscodeEncodeEngineError::codec(
CodecEncodeError::encode_flush(error),
))
})?;
assert!(
flushed <= C::MAX_ENCODE_FLUSH_UNITS,
"Codec::encode_flush wrote beyond its flush bound",
);
let written = self
.hooks
.finish_hooks(&mut self.codec, output, output_index + flushed)
.map_err(|error| {
TranscodeError::domain(TranscodeEncodeEngineError::hook(error))
})?;
assert!(
flushed + written <= required,
"TranscodeEncodeEngine hook wrote beyond its finish bound",
);
self.lifecycle.on_finish_success();
Ok(flushed + 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())
}
}
impl<C, H> Transcoder<C::Value, C::Unit> for TranscodeEncodeEngine<C, H>
where
C: Codec,
H: TranscodeEncodeHooks<C>,
{
type Error = EncodeEngineErrorOf<C, H>;
#[inline(always)]
fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_output_len(self, input_len)
}
#[inline(always)]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_reset_output_len(self)
}
#[inline(always)]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_finish_output_len(self)
}
#[inline(always)]
fn reset(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<Self::Error>> {
TranscodeEncodeEngine::reset(self, output, output_index)
}
#[inline(always)]
fn transcode(
&mut self,
input: &[C::Value],
input_index: usize,
output: &mut [C::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeError<Self::Error>> {
TranscodeEncodeEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeError<Self::Error>> {
TranscodeEncodeEngine::finish(self, output, output_index)
}
}