use alloc::string::String;
use alloc::vec::Vec;
pub(crate) struct Buffer {
bytes: Vec<u8>,
}
impl Buffer {
pub(crate) fn with_capacity(capacity: usize) -> Buffer {
Buffer {
bytes: Vec::with_capacity(capacity),
}
}
#[inline(always)]
pub(crate) fn reserve(&mut self, additional: usize) {
if self.bytes.capacity() - self.bytes.len() < additional {
self.grow(additional);
}
}
#[cold]
#[inline(never)]
fn grow(&mut self, additional: usize) {
self.bytes.reserve(additional);
}
#[inline(always)]
pub(crate) fn push(&mut self, byte: u8) {
assert!(byte.is_ascii(), "only ASCII bytes can be pushed");
self.reserve(1);
unsafe { self.push_unchecked(byte) };
}
#[inline(always)]
pub(crate) unsafe fn push_unchecked(&mut self, byte: u8) {
debug_assert!(byte.is_ascii());
unsafe {
let len = self.bytes.len();
self.bytes.as_mut_ptr().add(len).write(byte);
self.bytes.set_len(len + 1);
}
}
#[inline(always)]
pub(crate) fn push_str(&mut self, s: &str) {
self.reserve(s.len());
unsafe { self.push_str_unchecked(s) };
}
#[inline(always)]
pub(crate) unsafe fn push_str_unchecked(&mut self, s: &str) {
unsafe {
let len = self.bytes.len();
crate::copy::copy_small(s.as_ptr(), self.bytes.as_mut_ptr().add(len), s.len());
self.bytes.set_len(len + s.len());
}
}
#[inline(always)]
pub(crate) fn len(&self) -> usize {
self.bytes.len()
}
pub(crate) fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.bytes) }
}
pub(crate) fn truncate(&mut self, len: usize) {
assert!(self.as_str().is_char_boundary(len));
self.bytes.truncate(len);
}
pub(crate) fn from_vec(bytes: Vec<u8>) -> Buffer {
debug_assert!(core::str::from_utf8(&bytes).is_ok());
Buffer { bytes }
}
pub(crate) fn take(&mut self) -> Vec<u8> {
core::mem::take(&mut self.bytes)
}
pub(crate) fn into_string(self) -> String {
unsafe { String::from_utf8_unchecked(self.bytes) }
}
}
#[test]
fn test_copy_small() {
let source: Vec<u8> = (0..100u8).collect();
for start in 0..4 {
for len in 0..(100 - start) {
let mut buffer = Buffer::with_capacity(0);
buffer.push(b'x');
let s = core::str::from_utf8(&source[start..start + len]).unwrap();
buffer.push_str(s);
buffer.push(b'y');
let out = buffer.into_string();
assert_eq!(&out[1..out.len() - 1], s);
assert!(out.starts_with('x') && out.ends_with('y'));
}
}
}
#[test]
#[should_panic = "only ASCII bytes can be pushed"]
fn test_push_non_ascii() {
Buffer::with_capacity(0).push(0xc3);
}