use core::cmp::Ordering;
use core::fmt;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[derive(Debug, Clone, Copy)]
pub(crate) struct BufferTooSmallError(());
impl BufferTooSmallError {
#[inline]
#[must_use]
fn new() -> Self {
Self(())
}
}
impl fmt::Display for BufferTooSmallError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("destination buffer does not have enough capacity")
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for BufferTooSmallError {}
pub(crate) trait Buffer<'a> {
type ExtendError;
#[must_use]
fn as_bytes(&self) -> &[u8];
fn into_bytes(self) -> &'a [u8];
fn push_str(&mut self, s: &str) -> Result<(), Self::ExtendError>;
fn truncate(&mut self, new_len: usize);
fn push_optional_with_prefix(
&mut self,
prefix: &str,
body: Option<&str>,
) -> Result<(), Self::ExtendError> {
if let Some(body) = body {
self.push_str(prefix)?;
self.push_str(body)?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<'a> Buffer<'a> for &'a mut String {
type ExtendError = alloc::collections::TryReserveError;
#[inline]
fn as_bytes(&self) -> &[u8] {
(**self).as_bytes()
}
#[inline]
fn into_bytes(self) -> &'a [u8] {
(*self).as_bytes()
}
fn push_str(&mut self, s: &str) -> Result<(), Self::ExtendError> {
(**self).try_reserve(s.len())?;
(**self).push_str(s);
Ok(())
}
fn truncate(&mut self, new_len: usize) {
if (**self).len() < new_len {
panic!("[precondition] truncation should make the content shorter")
}
(**self).truncate(new_len);
}
fn push_optional_with_prefix(
&mut self,
prefix: &str,
body: Option<&str>,
) -> Result<(), Self::ExtendError> {
if let Some(body) = body {
(**self).try_reserve(prefix.len() + body.len())?;
(**self).push_str(prefix);
(**self).push_str(body);
}
Ok(())
}
}
#[derive(Debug)]
pub(super) struct ByteSliceBuf<'a> {
buf: &'a mut [u8],
len: usize,
}
impl<'a> ByteSliceBuf<'a> {
#[inline]
#[must_use]
pub(super) fn new(buf: &'a mut [u8]) -> Self {
Self { buf, len: 0 }
}
}
impl<'a> Buffer<'a> for ByteSliceBuf<'a> {
type ExtendError = BufferTooSmallError;
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.buf[..self.len]
}
#[inline]
fn into_bytes(self) -> &'a [u8] {
&self.buf[..self.len]
}
fn push_str(&mut self, s: &str) -> Result<(), Self::ExtendError> {
let s_end = self.len + s.len();
if self.buf.len() < s_end {
return Err(BufferTooSmallError::new());
}
self.buf[self.len..s_end].copy_from_slice(s.as_bytes());
self.len = s_end;
Ok(())
}
fn truncate(&mut self, new_len: usize) {
let last_byte = match new_len.cmp(&self.len) {
Ordering::Greater => {
panic!("[precondition] truncation should make the content shorter")
}
Ordering::Equal => return,
Ordering::Less => self.buf[new_len],
};
let is_byte_continue = (last_byte as i8) < -64;
if is_byte_continue {
panic!("[precondition] `new_len` should lie on a `char` boundary");
}
self.len = new_len;
}
fn push_optional_with_prefix(
&mut self,
prefix: &str,
body: Option<&str>,
) -> Result<(), Self::ExtendError> {
if let Some(body) = body {
let prefix_end = self.len + prefix.len();
let body_end = prefix_end + body.len();
if self.buf.len() < body_end {
return Err(BufferTooSmallError::new());
}
self.buf[self.len..prefix_end].copy_from_slice(prefix.as_bytes());
self.buf[prefix_end..body_end].copy_from_slice(body.as_bytes());
self.len = body_end;
}
Ok(())
}
}