pub(crate) mod complete;
pub(crate) mod incomplete;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::mem;
use core::num::NonZeroU32;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub(crate) struct Entry(NonZeroU32);
impl Entry {
pub const fn new(buf: [u8; 3], len: u8) -> Entry {
match NonZeroU32::new(u32::from_le_bytes([buf[0], buf[1], buf[2], len])) {
Some(packed) => Entry(packed),
None => panic!("entry len must be 1..=3"),
}
}
#[cfg(feature = "alloc")]
pub fn from_char(c: char) -> Self {
let len = c.len_utf8();
assert!(len < 4);
let mut buf = [0; 3];
c.encode_utf8(&mut buf);
Entry::new(buf, len as u8)
}
#[cfg(feature = "alloc")]
#[inline]
pub const fn len(self) -> usize {
(self.0.get() >> 24) as usize
}
#[inline]
pub const fn to_char(self) -> char {
let [b0, b1, b2, len] = self.0.get().to_le_bytes();
let cp = match len {
1 => b0 as u32,
2 => ((b0 as u32 & 0x1F) << 6) | (b1 as u32 & 0x3F),
_ => ((b0 as u32 & 0x0F) << 12) | ((b1 as u32 & 0x3F) << 6) | (b2 as u32 & 0x3F),
};
unsafe { char::from_u32_unchecked(cp) }
}
#[cfg(feature = "alloc")]
#[inline]
pub const fn utf8_word(self) -> [u8; 4] {
self.0.get().to_le_bytes()
}
}
#[cfg(feature = "alloc")]
const USIZE_SIZE: usize = mem::size_of::<usize>();
#[cfg(feature = "alloc")]
pub(crate) struct Utf8Writer {
buf: Vec<u8>,
dst: *mut u8,
end: *mut u8,
}
#[cfg(feature = "alloc")]
impl Utf8Writer {
#[inline]
pub fn for_input_len(input_len: usize) -> Self {
let cap = input_len * 3 + 1;
let mut buf: Vec<u8> = Vec::with_capacity(cap);
let dst = buf.as_mut_ptr();
let end = unsafe { dst.add(cap) };
Self { buf, dst, end }
}
#[inline]
fn remaining(&self) -> usize {
self.end as usize - self.dst as usize
}
#[inline]
pub unsafe fn push_entry(&mut self, entry: Entry) {
debug_assert!(self.remaining() >= 4, "push_entry overran buffer");
self.dst
.cast::<[u8; 4]>()
.write_unaligned(entry.utf8_word());
self.dst = self.dst.add(entry.len());
}
#[inline]
pub unsafe fn push_ascii_word(&mut self, word: usize) {
debug_assert!(
self.remaining() >= USIZE_SIZE,
"push_ascii_word overran buffer"
);
self.dst
.cast::<[u8; USIZE_SIZE]>()
.write_unaligned(word.to_ne_bytes());
self.dst = self.dst.add(USIZE_SIZE);
}
#[inline]
pub unsafe fn finish(mut self) -> String {
let length = self.dst.offset_from(self.buf.as_ptr()) as usize;
self.buf.set_len(length);
self.buf.shrink_to_fit();
String::from_utf8_unchecked(self.buf)
}
}
#[cfg(feature = "alloc")]
#[inline]
fn contains_nonascii(v: usize) -> bool {
const NONASCII_MASK: usize = 0x8080_8080_8080_8080_u64 as usize;
(NONASCII_MASK & v) != 0
}