use std::{
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
str,
};
#[derive(Default)]
#[doc(hidden)]
pub struct Buffer(usize, Vec<u8>);
impl Debug for Buffer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let slice = self.as_slice();
if let Ok(str) = str::from_utf8(slice) {
str.fmt(f)
} else {
slice.fmt(f)
}
}
}
impl From<Buffer> for Vec<u8> {
fn from(Buffer(offset, mut vec): Buffer) -> Self {
vec.copy_within(offset.., 0);
vec.truncate(vec.len() - offset);
vec
}
}
impl From<Vec<u8>> for Buffer {
fn from(value: Vec<u8>) -> Self {
Self(0, value)
}
}
impl Deref for Buffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl DerefMut for Buffer {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
#[doc(hidden)]
impl Buffer {
pub fn truncate(&mut self, n: usize) {
if n == 0 {
self.0 = 0;
self.1.clear();
} else {
self.1.truncate(self.0 + n);
}
}
pub fn extend_from_slice(&mut self, slice: &[u8]) {
self.1.extend_from_slice(slice);
}
pub fn prepend(&mut self, slice: &[u8]) {
if slice.len() <= self.0 {
self.0 -= slice.len();
self.1[self.0..self.0 + slice.len()].copy_from_slice(slice);
} else {
let active_len = self.len();
let new_total = slice.len() + active_len;
self.1.resize(new_total, 0);
if active_len > 0 {
self.1.copy_within(self.0..self.0 + active_len, slice.len());
}
self.1[..slice.len()].copy_from_slice(slice);
self.0 = 0;
}
}
pub fn ignore_front(&mut self, n: usize) {
self.0 += n;
if self.0 >= self.1.len() {
self.1.clear();
self.0 = 0;
}
}
pub fn len(&self) -> usize {
self.1.len() - self.0
}
pub fn is_empty(&self) -> bool {
self.1.len() == self.0
}
pub fn fill_capacity(&mut self) {
self.1.resize(self.1.capacity(), 0);
}
pub fn with_capacity(capacity: usize) -> Self {
Self(0, Vec::with_capacity(capacity))
}
pub fn expand(&mut self) {
if self.1.len() == self.1.capacity() {
self.1.reserve(32);
}
self.fill_capacity();
}
pub fn as_slice(&self) -> &[u8] {
&self.1[self.0..]
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.1[self.0..]
}
}
#[cfg(test)]
mod tests {
use super::Buffer;
#[test]
fn prepend_on_empty_default() {
let mut buf = Buffer::default();
buf.prepend(b"hello");
assert_eq!(&*buf, b"hello");
assert_eq!(buf.len(), 5);
assert_eq!(buf.0, 0);
}
#[test]
fn prepend_empty_slice_is_noop() {
let mut buf = Buffer::default();
buf.prepend(b"");
assert!(buf.is_empty());
let mut buf = Buffer::from(b"abc".to_vec());
buf.ignore_front(1);
let offset_before = buf.0;
buf.prepend(b"");
assert_eq!(&*buf, b"bc");
assert_eq!(buf.0, offset_before);
}
#[test]
fn prepend_fast_path_uses_existing_room() {
let mut buf = Buffer::from(b"abcdef".to_vec());
buf.ignore_front(4); assert_eq!(buf.0, 4);
let ptr_before = buf.1.as_ptr();
let len_before = buf.1.len();
buf.prepend(b"XYZ"); assert_eq!(&*buf, b"XYZef");
assert_eq!(buf.0, 1);
assert_eq!(buf.1.len(), len_before, "underlying vec length unchanged");
assert_eq!(buf.1.as_ptr(), ptr_before, "no reallocation on fast path");
}
#[test]
fn prepend_fast_path_exact_offset() {
let mut buf = Buffer::from(b"abcdef".to_vec());
buf.ignore_front(3); buf.prepend(b"XYZ"); assert_eq!(&*buf, b"XYZdef");
assert_eq!(buf.0, 0);
}
#[test]
fn prepend_slow_path_zero_offset_with_content() {
let mut buf = Buffer::from(b"world".to_vec());
assert_eq!(buf.0, 0);
buf.prepend(b"hello ");
assert_eq!(&*buf, b"hello world");
assert_eq!(buf.0, 0);
}
#[test]
fn prepend_slow_path_insufficient_offset() {
let mut buf = Buffer::from(b"abcdef".to_vec());
buf.ignore_front(2); buf.prepend(b"WXYZ"); assert_eq!(&*buf, b"WXYZcdef");
assert_eq!(buf.0, 0);
}
#[test]
fn prepend_preserves_order_after_extend() {
let mut buf = Buffer::default();
buf.extend_from_slice(b"later"); buf.prepend(b"earlier "); assert_eq!(&*buf, b"earlier later");
}
#[test]
fn prepend_then_ignore_front_then_prepend() {
let mut buf = Buffer::from(b"cccc".to_vec());
buf.ignore_front(4); assert!(buf.is_empty());
buf.prepend(b"bb"); assert_eq!(&*buf, b"bb");
buf.ignore_front(1);
buf.prepend(b"a"); assert_eq!(&*buf, b"ab");
}
}