#![no_std]
extern crate alloc;
mod error;
use alloc::{string::String, vec::Vec};
use core::{fmt, str::from_utf8};
pub use error::Utf8Error;
#[derive(Debug, Clone, Default)]
pub struct Utf8Builder {
buffer: Vec<u8>,
sl: u8,
sel: u8,
}
impl Utf8Builder {
#[inline]
pub const fn new() -> Self {
Utf8Builder {
buffer: Vec::new(), sl: 0, sel: 0
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Utf8Builder {
buffer: Vec::with_capacity(capacity), sl: 0, sel: 0
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.buffer.reserve(additional);
}
#[inline]
pub fn clear(&mut self) {
self.buffer.clear();
self.sl = 0;
}
#[inline]
pub fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.buffer
}
}
impl Utf8Builder {
#[inline]
pub const fn is_valid(&self) -> bool {
self.sl == 0
}
#[inline]
pub fn finalize(self) -> Result<String, Utf8Error> {
if self.is_valid() {
let s = unsafe { String::from_utf8_unchecked(self.buffer) };
Ok(s)
} else {
Err(Utf8Error)
}
}
#[inline]
pub fn into_bytes(self) -> Vec<u8> {
self.buffer
}
}
#[inline]
const fn is_valid_continuation(lead: u8, index: u8, b: u8) -> bool {
if index == 1 {
match lead {
0xE0 => matches!(b, 0xA0..=0xBF),
0xED => matches!(b, 0x80..=0x9F),
0xF0 => matches!(b, 0x90..=0xBF),
0xF4 => matches!(b, 0x80..=0x8F),
_ => matches!(b, 0x80..=0xBF),
}
} else {
matches!(b, 0x80..=0xBF)
}
}
impl Utf8Builder {
#[inline]
pub fn push(&mut self, b: u8) -> Result<(), Utf8Error> {
if self.sl == 0 {
let w = utf8_width::get_width(b);
match w {
0 => return Err(Utf8Error),
1 => {
self.buffer.push(b);
},
_ => {
self.buffer.push(b);
self.sl = 1;
self.sel = w as u8;
},
}
} else {
let lead = self.buffer[self.buffer.len() - self.sl as usize];
if !is_valid_continuation(lead, self.sl, b) {
return Err(Utf8Error);
}
self.buffer.push(b);
if self.sl + 1 == self.sel {
self.sl = 0;
} else {
self.sl += 1;
}
}
Ok(())
}
#[inline]
pub fn push_str(&mut self, s: &str) -> Result<(), Utf8Error> {
if self.sl == 0 {
self.buffer.extend_from_slice(s.as_bytes());
Ok(())
} else {
Err(Utf8Error)
}
}
#[inline]
pub fn push_char(&mut self, c: char) -> Result<(), Utf8Error> {
if self.sl == 0 {
let mut tmp = [0u8; 4];
self.buffer.extend_from_slice(c.encode_utf8(&mut tmp).as_bytes());
Ok(())
} else {
Err(Utf8Error)
}
}
#[inline]
pub fn push_chunk(&mut self, chunk: &[u8]) -> Result<(), Utf8Error> {
let chunk_size = chunk.len();
if chunk_size == 0 {
return Ok(());
}
let e = if self.sl > 0 {
let lead = self.buffer[self.buffer.len() - self.sl as usize];
let r = (self.sel - self.sl) as usize;
let take = r.min(chunk_size);
for (i, &b) in chunk[..take].iter().enumerate() {
if !is_valid_continuation(lead, self.sl + i as u8, b) {
return Err(Utf8Error);
}
}
self.buffer.extend_from_slice(&chunk[..take]);
if take < r {
self.sl += take as u8;
return Ok(());
}
self.sl = 0;
if take == chunk_size {
return Ok(());
}
take
} else {
0usize
};
let rest = &chunk[e..];
match from_utf8(rest) {
Ok(s) => {
self.buffer.extend_from_slice(s.as_bytes());
},
Err(error) => {
let valid_up_to = error.valid_up_to();
if error.error_len().is_some() {
self.buffer.extend_from_slice(&rest[..valid_up_to]);
return Err(Utf8Error);
}
self.buffer.extend_from_slice(rest);
self.sl = (rest.len() - valid_up_to) as u8;
self.sel = utf8_width::get_width(rest[valid_up_to]) as u8;
},
}
Ok(())
}
}
impl fmt::Write for Utf8Builder {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s).map_err(|_| fmt::Error)
}
#[inline]
fn write_char(&mut self, c: char) -> fmt::Result {
self.push_char(c).map_err(|_| fmt::Error)
}
}
impl From<&str> for Utf8Builder {
#[inline]
fn from(s: &str) -> Self {
Utf8Builder {
buffer: s.as_bytes().to_vec(), sl: 0, sel: 0
}
}
}
impl From<String> for Utf8Builder {
#[inline]
fn from(s: String) -> Self {
Utf8Builder {
buffer: s.into_bytes(), sl: 0, sel: 0
}
}
}