pub(crate) struct Utf8Error {
bytes: [u8; 3],
len: u8,
}
impl Utf8Error {
#[cold]
#[inline(never)]
fn new(original_bytes: &[u8], err: core::str::Utf8Error) -> Utf8Error {
let len = err.error_len().unwrap_or_else(|| original_bytes.len());
debug_assert!(1 <= len && len <= 3);
let mut bytes = [0; 3];
bytes[..len].copy_from_slice(&original_bytes[..len]);
Utf8Error {
bytes,
len: u8::try_from(len).unwrap(),
}
}
pub(crate) fn as_slice(&self) -> &[u8] {
&self.bytes[..self.len()]
}
pub(crate) fn len(&self) -> usize {
usize::from(self.len)
}
}
impl core::fmt::Display for Utf8Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"found invalid UTF-8 byte {errant_bytes:?} in format \
string (format strings must be valid UTF-8)",
errant_bytes = crate::shared::util::escape::Bytes(self.as_slice()),
)
}
}
pub(crate) fn decode(bytes: &[u8]) -> Option<Result<char, Utf8Error>> {
if bytes.is_empty() {
return None;
}
let string = match core::str::from_utf8(&bytes[..bytes.len().min(4)]) {
Ok(s) => s,
Err(ref err) if err.valid_up_to() > 0 => {
core::str::from_utf8(&bytes[..err.valid_up_to()]).unwrap()
}
Err(err) => return Some(Err(Utf8Error::new(bytes, err))),
};
Some(Ok(string.chars().next().unwrap()))
}