use super::{
charset_codec::CharsetCodec,
charset_convert_error::CharsetConvertError,
charset_decoder::CharsetDecoder,
charset_encoder::CharsetEncoder,
};
use crate::{
Coder,
CoderProgress,
CoderStatus,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetCodec,
{
decoder: CharsetDecoder<D>,
encoder: CharsetEncoder<E>,
pending: Option<char>,
}
impl<D, E> CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetCodec,
{
#[must_use]
#[inline]
pub fn from_codecs(source: D, target: E) -> Self {
Self::new(CharsetDecoder::new(source), CharsetEncoder::new(target))
}
#[must_use]
#[inline]
pub fn new(decoder: CharsetDecoder<D>, encoder: CharsetEncoder<E>) -> Self {
Self {
decoder,
encoder,
pending: None,
}
}
#[must_use]
#[inline]
pub const fn decoder(&self) -> &CharsetDecoder<D> {
&self.decoder
}
#[must_use]
#[inline]
pub const fn encoder(&self) -> &CharsetEncoder<E> {
&self.encoder
}
#[must_use]
#[inline]
pub fn decoder_mut(&mut self) -> &mut CharsetDecoder<D> {
&mut self.decoder
}
#[must_use]
#[inline]
pub fn encoder_mut(&mut self) -> &mut CharsetEncoder<E> {
&mut self.encoder
}
#[inline]
fn write_pending(
&mut self,
ch: char,
output: &mut [E::Unit],
output_index: usize,
written: &mut usize,
) -> Result<CoderProgress, CharsetConvertError> {
let single = [ch];
let encode_progress = self.encoder.convert(&single, 0, output, output_index + *written)?;
if encode_progress.status() == CoderStatus::Complete && encode_progress.read() == 1 {
self.pending = None;
}
*written += encode_progress.written();
Ok(encode_progress)
}
}
impl<D, E> Coder<D::Unit, E::Unit> for CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetCodec,
{
type Error = CharsetConvertError;
#[inline]
fn max_output_len(&self, input_len: usize) -> Option<usize> {
input_len.checked_mul(self.encoder.codec().max_units_per_char())
}
#[inline]
fn reset(&mut self) {
self.pending = None;
self.decoder.reset();
self.encoder.reset();
}
fn convert(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<CoderProgress, Self::Error> {
let mut read = 0;
let mut written = 0;
if let Some(ch) = self.pending {
let status = self.write_pending(ch, output, output_index, &mut written)?;
if matches!(status.status(), CoderStatus::NeedOutput { .. }) {
let status = CoderStatus::NeedOutput {
output_index: output_index + written,
required: status.required(),
available: status.available(),
};
return Ok(CoderProgress::new(status, read, written));
}
}
while input_index + read < input.len() {
let mut decoded = ['\0'; 4];
let decode_progress = self.decoder.convert(input, input_index + read, &mut decoded, 0)?;
let decode_status = decode_progress.status();
let decode_read = decode_progress.read();
read += decode_read;
if decode_progress.written() > 0 {
for &ch in decoded.iter().take(decode_progress.written()) {
self.pending = Some(ch);
let status = self.write_pending(ch, output, output_index, &mut written)?;
if matches!(status.status(), CoderStatus::NeedOutput { .. }) {
let status = CoderStatus::NeedOutput {
output_index: output_index + written,
required: status.required(),
available: status.available(),
};
return Ok(CoderProgress::new(status, read, written));
}
}
}
match decode_status {
CoderStatus::Complete if input_index + read >= input.len() || decode_read == 0 => {
return Ok(CoderProgress::complete(read, written));
}
CoderStatus::Complete => {}
CoderStatus::NeedInput { .. } => {
let status = CoderStatus::NeedInput {
input_index: input_index + read,
required: decode_progress.required(),
available: decode_progress.available(),
};
return Ok(CoderProgress::new(status, read, written));
}
CoderStatus::NeedOutput { .. } => {
debug_assert!(
decode_read > 0,
"Decoder must consume at least one input unit when reporting NeedOutput"
);
}
}
}
Ok(CoderProgress::complete(read, written))
}
#[inline]
fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<CoderProgress, Self::Error> {
if let Some(ch) = self.pending {
let mut written = 0;
let status = self.write_pending(ch, output, output_index, &mut written)?;
if matches!(status.status(), CoderStatus::NeedOutput { .. }) {
let status = CoderStatus::NeedOutput {
output_index: output_index + written,
required: status.required(),
available: status.available(),
};
return Ok(CoderProgress::new(status, 0, written));
}
return Ok(CoderProgress::complete(0, written));
}
Ok(CoderProgress::complete(0, 0))
}
}