use core::num::NonZeroUsize;
use qubit_codec::{
BufferedDecodeHooks,
CapacityError,
DecodeAction,
DecodeContext,
};
use crate::{
CharsetCodec,
CharsetDecodeError,
CharsetDecodeErrorKind,
MalformedAction,
};
use super::charset_decode_policy::CharsetDecodePolicy;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) struct CharsetDecodeHooks {
pub(super) malformed_action: MalformedAction,
pub(super) replacement: char,
}
impl CharsetDecodeHooks {
#[must_use]
#[inline(always)]
pub(crate) const fn new(
malformed_action: MalformedAction,
replacement: char,
) -> Self {
Self {
malformed_action,
replacement,
}
}
#[must_use]
#[inline(always)]
pub(crate) const fn from_policy(policy: CharsetDecodePolicy) -> Self {
Self::new(policy.malformed_action(), policy.replacement())
}
#[must_use]
#[inline]
fn malformed_consumed(
reported: Option<usize>,
available: usize,
) -> NonZeroUsize {
let consumed = reported.unwrap_or(1).min(available).max(1);
NonZeroUsize::new(consumed)
.expect("malformed input consumption is non-zero")
}
}
impl<C> BufferedDecodeHooks<C> for CharsetDecodeHooks
where
C: CharsetCodec,
{
type Error = CharsetDecodeError;
#[inline(always)]
fn max_output_len(
&self,
_codec: &C,
input_len: usize,
) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn handle_decode_error(
&mut self,
_codec: &C,
error: CharsetDecodeError,
context: DecodeContext,
) -> Result<DecodeAction<char>, Self::Error> {
if let Some((required, available)) = error.kind().incomplete() {
debug_assert!(
required > available,
"incomplete error did not require more input"
);
return Ok(DecodeAction::NeedInput {
required_total: required,
});
}
if error.kind().is_malformed_input() {
let consumed = CharsetDecodeHooks::malformed_consumed(
error.consumed(),
context.available,
);
return match self.malformed_action {
MalformedAction::Report => Err(error),
MalformedAction::Ignore => Ok(DecodeAction::Skip { consumed }),
MalformedAction::Replace => Ok(DecodeAction::Emit {
value: self.replacement,
consumed,
}),
};
}
Err(error)
}
#[inline]
fn invalid_input_index(
&mut self,
codec: &C,
index: usize,
input_len: usize,
) -> Self::Error {
let kind = CharsetDecodeErrorKind::InvalidInputIndex { input_len };
CharsetDecodeError::new(codec.charset(), kind, index)
}
#[inline]
fn invalid_output_index(
&mut self,
codec: &C,
index: usize,
output_len: usize,
) -> Self::Error {
let kind = CharsetDecodeErrorKind::InvalidOutputIndex { output_len };
CharsetDecodeError::new(codec.charset(), kind, index)
}
}