use core::num::NonZeroUsize;
use super::super::internal::{
encode_attempt::EncodeAttempt,
encode_state::EncodeState,
lifecycle_guard::LifecycleGuard,
};
use super::{
EncodeOutcome,
EncodeUnencodableAction,
TranscodeEncodeHooks,
};
use crate::codec::assert_unit_bounds;
use crate::{
CapacityError,
Codec,
TranscodeEncodeError,
TranscodeEncodeErrorOf,
TranscodeEncoder,
TranscodeFailure,
TranscodeProgress,
Transcoder,
};
#[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)]
#[must_use]
pub const fn codec(&self) -> &C {
&self.codec
}
#[inline(always)]
#[must_use]
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
#[inline(always)]
#[must_use]
pub const fn hooks(&self) -> &H {
&self.hooks
}
#[inline(always)]
#[must_use]
pub fn hooks_mut(&mut self) -> &mut H {
&mut self.hooks
}
#[inline]
#[must_use]
pub fn into_parts(self) -> (C, H) {
let Self { codec, hooks, .. } = self;
(codec, hooks)
}
#[inline(always)]
#[must_use = "capacity planning can fail on overflow"]
pub fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
self.hooks.max_transcode_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_FINISH_UNITS
.checked_add(self.hooks.max_finish_output_len(&self.codec))
.ok_or(CapacityError::OutputLengthOverflow)
}
#[inline(never)]
#[must_use = "capacity planning can fail on overflow"]
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()?;
reset
.checked_add(transcode)
.and_then(|len| len.checked_add(finish))
.ok_or(CapacityError::OutputLengthOverflow)
}
pub fn reset(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeEncodeErrorOf<C>> {
let required = self.max_reset_output_len()?;
TranscodeFailure::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.lifecycle.on_reset_start();
self.hooks.reset_hooks(&mut self.codec);
let written = unsafe {
self.codec.encode_reset(output, output_index)
}
.map_err(TranscodeEncodeError::domain_reset)?;
assert!(
written <= required,
"Codec::encode_reset wrote beyond its reset bound",
);
self.lifecycle.on_reset_success();
Ok(written)
}
pub fn transcode(
&mut self,
input: &[C::Value],
input_index: usize,
output: &mut [C::Unit],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeErrorOf<C>> {
self.lifecycle.on_transcode()?;
TranscodeFailure::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 attempt = unsafe { state.context_unchecked() };
let outcome = self.encode_one(attempt)?;
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, TranscodeEncodeErrorOf<C>> {
self.lifecycle.on_finish_attempt()?;
let required = self.max_finish_output_len()?;
TranscodeFailure::ensure_output_capacity(
output.len(),
output_index,
required,
)?;
self.lifecycle.on_finish_start();
let finished = unsafe {
self.codec.encode_finish(output, output_index)
}
.map_err(TranscodeEncodeError::domain_finish)?;
assert!(
finished <= C::MAX_ENCODE_FINISH_UNITS,
"Codec::encode_finish wrote beyond its finish bound",
);
let written = self.hooks.finish_hooks(
&mut self.codec,
output,
output_index + finished,
)?;
assert!(
finished + written <= required,
"TranscodeEncodeEngine hook wrote beyond its finish bound",
);
self.lifecycle.on_finish_success();
Ok(finished + written)
}
#[inline]
pub fn transcode_complete_into(
&mut self,
input: &[C::Value],
output: &mut [C::Unit],
) -> Result<usize, TranscodeEncodeErrorOf<C>> {
<Self as Transcoder>::transcode_complete_into(self, input, output)
}
pub(in crate::transcode) fn encode_one(
&mut self,
attempt: EncodeAttempt<'_, C::Value, C::Unit>,
) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
let encodable = {
let context = attempt.context();
self.codec.can_encode_value(context.input_value())
};
if encodable {
return self.encode_encodable_value(attempt);
}
let action = {
let context = attempt.context();
self.hooks
.handle_unencodable_encode(&mut self.codec, &context)?
};
self.apply_unencodable_action(action, attempt)
}
fn encode_encodable_value(
&mut self,
attempt: EncodeAttempt<'_, C::Value, C::Unit>,
) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
let required = self.codec.encode_len(attempt.value());
assert!(
required <= C::MAX_ENCODE_UNITS_PER_VALUE,
"Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE",
);
if attempt.available_output() < required {
return Ok(EncodeOutcome::need_output(
NonZeroUsize::new(required).expect(
"required output is non-zero when capacity is insufficient",
),
));
}
let (value, input_index, output, output_index) = attempt.into_parts();
let written = unsafe {
self.codec.encode(value, output, output_index)
}
.map_err(|error| {
TranscodeEncodeError::domain_main(error, input_index)
})?;
assert!(
written == required,
"Codec::encode wrote a different length than Codec::encode_len",
);
Ok(EncodeOutcome::consumed(written))
}
#[inline(always)]
fn apply_unencodable_action(
&mut self,
action: EncodeUnencodableAction<C::Value>,
attempt: EncodeAttempt<'_, C::Value, C::Unit>,
) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
match action {
EncodeUnencodableAction::Reject => {
Err(TranscodeEncodeError::unencodable_without_context(
attempt.input_index(),
))
}
EncodeUnencodableAction::Skip => Ok(EncodeOutcome::consumed(0)),
EncodeUnencodableAction::Replace { value } => {
self.encode_replacement_value(value, attempt)
}
}
}
fn encode_replacement_value(
&mut self,
value: C::Value,
attempt: EncodeAttempt<'_, C::Value, C::Unit>,
) -> Result<EncodeOutcome, TranscodeEncodeErrorOf<C>> {
assert!(
self.codec.can_encode_value(&value),
"EncodeUnencodableAction::Replace returned an unencodable replacement value",
);
let required = self.codec.encode_len(&value);
assert!(
required <= C::MAX_ENCODE_UNITS_PER_VALUE,
"Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE",
);
if attempt.available_output() < required {
return Ok(EncodeOutcome::need_output(
NonZeroUsize::new(required).expect(
"required output is non-zero when capacity is insufficient",
),
));
}
let (_, input_index, output, output_index) = attempt.into_parts();
let written = unsafe {
self.codec.encode(&value, output, output_index)
}
.map_err(|error| {
TranscodeEncodeError::domain_main(error, input_index)
})?;
assert!(
written == required,
"Codec::encode wrote a different length than Codec::encode_len",
);
Ok(EncodeOutcome::consumed(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 for TranscodeEncodeEngine<C, H>
where
C: Codec,
H: TranscodeEncodeHooks<C>,
{
type Input = C::Value;
type Output = C::Unit;
type Error = TranscodeEncodeErrorOf<C>;
#[inline(always)]
fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_transcode_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, TranscodeEncodeErrorOf<C>> {
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, TranscodeEncodeErrorOf<C>> {
TranscodeEncodeEngine::transcode(
self,
input,
input_index,
output,
output_index,
)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, TranscodeEncodeErrorOf<C>> {
TranscodeEncodeEngine::finish(self, output, output_index)
}
}
impl<C, H> TranscodeEncoder for TranscodeEncodeEngine<C, H>
where
C: Codec,
H: TranscodeEncodeHooks<C>,
{
type EncodeError = C::EncodeError;
}