#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
mod error;
use core::cmp::Ordering;
use alloc::string::String;
use alloc::vec::Vec;
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 len(&self) -> usize {
self.buffer.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}
impl Utf8Builder {
#[inline]
pub 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)
}
}
}
impl Utf8Builder {
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 if self.sl + 1 == self.sel {
self.buffer.push(b);
self.sl = 0;
} else {
self.buffer.push(b);
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)
}
}
pub fn push_char(&mut self, c: char) -> Result<(), Utf8Error> {
if self.sl == 0 {
self.buffer.reserve(4);
let len = self.buffer.len();
unsafe {
self.buffer.set_len(len + 4);
}
let c = c.encode_utf8(&mut self.buffer[len..]).len();
unsafe {
self.buffer.set_len(len + c);
}
Ok(())
} else {
Err(Utf8Error)
}
}
pub fn push_chunk(&mut self, chunk: &[u8]) -> Result<(), Utf8Error> {
let chunk_size = chunk.len();
if chunk_size == 0 {
return Ok(());
}
let mut e = if self.sl > 0 {
let r = (self.sel - self.sl) as usize;
match r.cmp(&chunk_size) {
Ordering::Greater => {
let sl = self.sl as usize;
let nsl = sl + chunk_size;
self.buffer.extend_from_slice(chunk);
self.sl = nsl as u8;
return Ok(());
}
Ordering::Equal => {
self.buffer.extend_from_slice(chunk);
self.sl = 0;
return Ok(());
}
Ordering::Less => {
self.buffer.extend_from_slice(&chunk[..r]);
self.sl = 0;
r
}
}
} else {
0usize
};
loop {
let w = utf8_width::get_width(chunk[e]);
if w == 0 {
return Err(Utf8Error);
}
let r = chunk_size - e;
if r >= w {
self.buffer.extend_from_slice(&chunk[e..e + w]);
e += w;
if e == chunk_size {
break;
}
} else {
self.buffer.extend_from_slice(&chunk[e..]);
self.sl = r as u8;
self.sel = w as u8;
break;
}
}
Ok(())
}
}
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,
}
}
}