#[cfg(feature = "alloc")]
use alloc::string::String;
use crate::Encoding;
use crate::encodings::{UTF_8, UTF_16BE, UTF_16LE};
use crate::options::{Bom, DecodeOptions, Malformed, Tally};
use crate::result::DecoderResult;
use crate::sink::ByteSink;
use crate::variant::VariantDecoder;
pub const DECODER_MIN_BUFFER: usize = 4;
const BOMS: [(&[u8], &Encoding); 3] = [
(&[0xEF, 0xBB, 0xBF], UTF_8),
(&[0xFE, 0xFF], UTF_16BE),
(&[0xFF, 0xFE], UTF_16LE),
];
enum Decision {
NeedMore,
Bom(&'static Encoding),
NoBom,
}
#[derive(Debug, Clone)]
pub struct Decoder {
encoding: &'static Encoding,
variant: VariantDecoder,
options: DecodeOptions,
sniffing: bool,
bom_buf: [u8; 3],
bom_len: u8,
prefix: [u8; 3],
prefix_pos: u8,
prefix_len: u8,
pending: [u8; 4],
pending_pos: u8,
pending_len: u8,
tally: Tally,
}
impl Decoder {
pub(crate) fn new(encoding: &'static Encoding, options: DecodeOptions) -> Decoder {
let sniffing = match options.bom {
Bom::Sniff => true,
Bom::Remove => encoding.bom().is_some(),
Bom::Ignore => false,
};
Decoder {
encoding,
variant: encoding.variant().new_decoder(),
options,
sniffing,
bom_buf: [0; 3],
bom_len: 0,
prefix: [0; 3],
prefix_pos: 0,
prefix_len: 0,
pending: [0; 4],
pending_pos: 0,
pending_len: 0,
tally: Tally::default(),
}
}
pub fn encoding(&self) -> &'static Encoding {
self.encoding
}
pub fn max_utf8_buffer_length(&self, byte_length: usize) -> Option<usize> {
self.variant.max_utf8_buffer_length(byte_length)
}
pub fn decode_to_utf8(
&mut self,
src: &[u8],
dst: &mut [u8],
last: bool,
) -> (DecoderResult, usize, usize) {
let mut total_read = 0usize;
let mut total_written = 0usize;
loop {
{
let mut sink = ByteSink::new(&mut dst[total_written..]);
if !self.drain_pending(&mut sink) {
return (
DecoderResult::OutputFull,
total_read,
total_written + sink.written(),
);
}
total_written += sink.written();
}
let (result, read, written) =
self.decode_step(&src[total_read..], &mut dst[total_written..], last);
total_read += read;
total_written += written;
match result {
DecoderResult::Malformed(len) => match self.options.malformed {
Malformed::Fail => {
return (DecoderResult::Malformed(len), total_read, total_written);
}
Malformed::Omit => {
self.tally.errors += 1;
}
Malformed::Replace(c) => {
self.tally.errors += 1;
let mut buf = [0u8; 4];
let encoded = c.encode_utf8(&mut buf).as_bytes();
self.pending[..encoded.len()].copy_from_slice(encoded);
self.pending_pos = 0;
self.pending_len = encoded.len() as u8;
}
},
other => return (other, total_read, total_written),
}
}
}
pub fn tally(&self) -> Tally {
self.tally
}
fn drain_pending(&mut self, sink: &mut ByteSink) -> bool {
if self.pending_pos == self.pending_len {
return true;
}
let (start, end) = (usize::from(self.pending_pos), usize::from(self.pending_len));
let written = sink.write_slice_partial(&self.pending[start..end]);
self.pending_pos += written as u8;
if self.pending_pos == self.pending_len {
self.pending_pos = 0;
self.pending_len = 0;
true
} else {
false
}
}
#[cfg(feature = "alloc")]
pub fn decode_to_string(
&mut self,
src: &[u8],
dst: &mut String,
last: bool,
) -> Result<(), MalformedError> {
let mut buffer = [0u8; 1024];
let mut read = 0usize;
loop {
let (result, n, written) = self.decode_to_utf8(&src[read..], &mut buffer, last);
read += n;
dst.push_str(
core::str::from_utf8(&buffer[..written]).expect("decoders emit valid UTF-8"),
);
match result {
DecoderResult::InputEmpty => return Ok(()),
DecoderResult::OutputFull => {}
DecoderResult::Malformed(len) => {
return Err(MalformedError { offset: read, len });
}
}
}
}
fn decode_step(
&mut self,
src: &[u8],
dst: &mut [u8],
last: bool,
) -> (DecoderResult, usize, usize) {
let mut sink = ByteSink::new(dst);
let mut read = 0usize;
if self.sniffing {
match self.consume_bom(src, last) {
Some(consumed) => read = consumed,
None => return (DecoderResult::InputEmpty, src.len(), 0),
}
}
while self.prefix_pos < self.prefix_len {
let prefix = self.prefix;
let (start, end) = (usize::from(self.prefix_pos), usize::from(self.prefix_len));
let prefix_is_last = last && read == src.len();
let (result, n) = self
.variant
.decode(&prefix[start..end], &mut sink, prefix_is_last);
self.prefix_pos += n as u8;
if result != DecoderResult::InputEmpty {
return (result, read, sink.written());
}
}
let (result, n) = self.variant.decode(&src[read..], &mut sink, last);
(result, read + n, sink.written())
}
fn consume_bom(&mut self, src: &[u8], last: bool) -> Option<usize> {
let mut consumed = 0usize;
loop {
match self.bom_decision() {
Decision::NeedMore => {
if consumed == src.len() {
if !last {
return None;
}
self.buffered_bytes_are_content();
return Some(consumed);
}
self.bom_buf[usize::from(self.bom_len)] = src[consumed];
self.bom_len += 1;
consumed += 1;
}
Decision::Bom(encoding) => {
self.bom_len = 0;
self.sniffing = false;
if encoding != self.encoding {
self.encoding = encoding;
self.variant = encoding.variant().new_decoder();
}
return Some(consumed);
}
Decision::NoBom => {
self.buffered_bytes_are_content();
return Some(consumed);
}
}
}
}
fn bom_decision(&self) -> Decision {
let seen = &self.bom_buf[..usize::from(self.bom_len)];
match self.options.bom {
Bom::Sniff => prefix_decision(&BOMS, seen),
Bom::Remove => match self.encoding.bom() {
Some(bom) => prefix_decision(&[(bom, self.encoding)], seen),
None => Decision::NoBom,
},
Bom::Ignore => Decision::NoBom,
}
}
fn buffered_bytes_are_content(&mut self) {
self.prefix = self.bom_buf;
self.prefix_pos = 0;
self.prefix_len = self.bom_len;
self.bom_len = 0;
self.sniffing = false;
}
}
fn prefix_decision(candidates: &[(&[u8], &'static Encoding)], seen: &[u8]) -> Decision {
let mut partial = false;
for &(bom, encoding) in candidates {
if seen.len() >= bom.len() {
if &seen[..bom.len()] == bom {
return Decision::Bom(encoding);
}
} else if bom.starts_with(seen) {
partial = true;
}
}
if partial {
Decision::NeedMore
} else {
Decision::NoBom
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MalformedError {
pub offset: usize,
pub len: u8,
}
impl core::fmt::Display for MalformedError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"malformed byte sequence of {} byte(s) ending at offset {}",
self.len, self.offset
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for MalformedError {}