use core::num::NonZeroUsize;
use super::super::internal::encode_attempt::EncodeAttempt;
use super::super::internal::encode_state::EncodeState;
use super::super::internal::lifecycle_guard::LifecycleGuard;
use super::EncodeOutcome;
use super::EncodeUnencodableAction;
use super::TranscodeEncodeHooks;
use crate::CapacityError;
use crate::Codec;
use crate::TranscodeEncodeError;
use crate::TranscodeEncodeErrorOf;
use crate::TranscodeEncoder;
use crate::TranscodeFailure;
use crate::TranscodeProgress;
use crate::Transcoder;
use crate::codec::assert_unit_bounds;
#[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]
#[must_use = "use the codec reference to inspect the wrapped codec"]
pub const fn codec(&self) -> &C {
&self.codec
}
#[inline]
#[must_use = "use the codec reference to configure the wrapped codec"]
pub fn codec_mut(&mut self) -> &mut C {
&mut self.codec
}
#[inline]
#[must_use = "use the hook reference to inspect the encode policy"]
pub const fn hooks(&self) -> &H {
&self.hooks
}
#[inline]
#[must_use = "use the hook reference to configure the encode policy"]
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]
pub fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
self.hooks.max_transcode_output_len(&self.codec, input_len)
}
#[inline]
pub fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(C::MAX_ENCODE_RESET_UNITS)
}
#[inline]
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]
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 hook_required = self.hooks.max_finish_output_len(&self.codec);
let required = C::MAX_ENCODE_FINISH_UNITS
.checked_add(hook_required)
.ok_or(CapacityError::OutputLengthOverflow)?;
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!(
written <= hook_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 required = C::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 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]
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]
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]
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_transcode_output_len(self, input_len)
}
#[inline]
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_reset_output_len(self)
}
#[inline]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
TranscodeEncodeEngine::max_finish_output_len(self)
}
#[inline]
fn reset(&mut self, output: &mut [C::Unit], output_index: usize) -> Result<usize, TranscodeEncodeErrorOf<C>> {
TranscodeEncodeEngine::reset(self, output, output_index)
}
#[inline]
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]
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;
}