#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use crate::DecodeError;
#[cfg(feature = "alloc")]
use super::{contains_nonascii, Utf8Writer, USIZE_SIZE};
use super::Entry;
pub(crate) type Table = [Option<Entry>; 256];
const _: () = assert!(core::mem::size_of::<Option<Entry>>() == 4);
#[cfg(feature = "alloc")]
#[inline(always)]
pub(crate) fn decode_helper<'a>(
table: &Table,
bytes: &'a [u8],
fallback: Option<char>,
) -> Result<Cow<'a, str>, DecodeError> {
let fallback: Option<Entry> = fallback.map(Entry::from_char);
if bytes.is_ascii() {
let s = unsafe { core::str::from_utf8_unchecked(bytes) };
return Ok(s.into());
}
let mut writer = Utf8Writer::for_input_len(bytes.len());
unsafe {
let mut iter = bytes.chunks_exact(USIZE_SIZE);
for (i, chunk) in (&mut iter).enumerate() {
let chunk: &[u8; USIZE_SIZE] = chunk.try_into().unwrap();
let word = usize::from_ne_bytes(*chunk);
if contains_nonascii(word) {
decode_slice(table, chunk, &mut writer, fallback).map_err(|mut e| {
e.position += i * USIZE_SIZE;
e
})?;
} else {
writer.push_ascii_word(word);
}
}
decode_slice(table, iter.remainder(), &mut writer, fallback).map_err(|mut e| {
e.position += bytes.len() - iter.remainder().len();
e
})?;
Ok(writer.finish().into())
}
}
#[cfg(feature = "alloc")]
#[inline(always)]
pub(crate) fn decode_helper_non_ascii<'a>(
table: &Table,
bytes: &'a [u8],
fallback: Option<char>,
) -> Result<Cow<'a, str>, DecodeError> {
let mut writer = Utf8Writer::for_input_len(bytes.len());
let fallback: Option<Entry> = fallback.map(Entry::from_char);
unsafe { decode_slice(table, bytes, &mut writer, fallback) }?;
Ok(unsafe { writer.finish() }.into())
}
#[cfg(feature = "alloc")]
#[inline]
unsafe fn decode_slice(
table: &Table,
src: &[u8],
writer: &mut Utf8Writer,
fallback: Option<Entry>,
) -> Result<(), DecodeError> {
for (i, &b) in src.iter().enumerate() {
match table[b as usize].or(fallback) {
Some(entry) => writer.push_entry(entry),
None => {
return Err(DecodeError {
position: i,
value: b,
})
}
}
}
Ok(())
}