use crate::io::{Error, Read};
use alloc::vec::Vec;
use core::ptr;
use super::buffer_backend::{BufferBackend, WILDCOPY_OVERLENGTH};
pub(crate) struct FlatBuf {
buf: Vec<u8>,
head: usize,
}
impl FlatBuf {
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: Vec::with_capacity(cap + WILDCOPY_OVERLENGTH),
head: 0,
}
}
}
impl BufferBackend for FlatBuf {
fn new() -> Self {
Self {
buf: Vec::new(),
head: 0,
}
}
#[inline]
fn clear(&mut self) {
self.buf.clear();
self.head = 0;
}
#[inline]
fn reserve(&mut self, n: usize) {
self.buf.reserve(n.saturating_add(WILDCOPY_OVERLENGTH));
}
#[inline]
fn len(&self) -> usize {
self.buf.len() - self.head
}
#[inline]
fn cap(&self) -> usize {
self.buf.capacity()
}
#[inline]
fn tail(&self) -> usize {
self.buf.len()
}
#[inline]
unsafe fn set_tail(&mut self, new_tail: usize) {
debug_assert!(new_tail >= self.head);
debug_assert!(new_tail <= self.buf.len());
unsafe { self.buf.set_len(new_tail) };
}
#[inline]
fn extend(&mut self, data: &[u8]) {
self.buf.extend_from_slice(data);
}
#[inline]
fn extend_and_fill(&mut self, fill_with: u8, fill_length: usize) {
let new_len = self.buf.len() + fill_length;
self.buf.resize(new_len, fill_with);
}
fn extend_from_reader<R: Read>(
&mut self,
mut read: R,
fill_length: usize,
) -> Result<(), Error> {
let old = self.buf.len();
let new_len = old + fill_length;
self.reserve(fill_length);
self.buf.resize(new_len, 0);
let read_slot = &mut self.buf[old..new_len];
match read.read_exact(read_slot) {
Ok(()) => Ok(()),
Err(e) => {
self.buf.truncate(old);
Err(e)
}
}
}
#[inline]
unsafe fn extend_from_within_unchecked(&mut self, start: usize, len: usize) {
let dst_off = self.buf.len();
let src_off = self.head + start;
debug_assert!(src_off + len <= dst_off);
debug_assert!(dst_off + len <= self.buf.capacity());
unsafe {
let ptr = self.buf.as_mut_ptr();
ptr::copy_nonoverlapping(ptr.add(src_off), ptr.add(dst_off), len);
self.buf.set_len(dst_off + len);
}
}
#[inline]
unsafe fn extend_from_within_unchecked_branchless(&mut self, start: usize, len: usize) {
unsafe { self.extend_from_within_unchecked(start, len) }
}
#[inline]
fn as_slices(&self) -> (&[u8], &[u8]) {
(&self.buf[self.head..], &[])
}
#[inline]
fn drop_first_n(&mut self, n: usize) {
self.head += n;
debug_assert!(self.head <= self.buf.len());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_capacity_starts_empty() {
let f = FlatBuf::with_capacity(1024);
assert_eq!(f.len(), 0);
assert_eq!(f.tail(), 0);
assert!(f.cap() >= 1024 + WILDCOPY_OVERLENGTH);
}
#[test]
fn extend_appends_then_len_matches() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[1, 2, 3, 4]);
assert_eq!(f.len(), 4);
f.extend(&[5, 6]);
assert_eq!(f.len(), 6);
let (s1, s2) = f.as_slices();
assert_eq!(s1, &[1, 2, 3, 4, 5, 6]);
assert!(s2.is_empty(), "flat layout never wraps");
}
#[test]
fn extend_and_fill_appends_repeated_byte() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[0xAA]);
f.extend_and_fill(0xBB, 5);
let (s1, _) = f.as_slices();
assert_eq!(s1, &[0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB]);
}
#[test]
fn extend_from_within_unchecked_copies_non_overlapping() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[10, 20, 30, 40, 50]);
unsafe { f.extend_from_within_unchecked(0, 3) };
let (s1, _) = f.as_slices();
assert_eq!(s1, &[10, 20, 30, 40, 50, 10, 20, 30]);
}
#[test]
fn drop_first_n_advances_head() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[1, 2, 3, 4, 5]);
f.drop_first_n(2);
assert_eq!(f.len(), 3);
let (s1, _) = f.as_slices();
assert_eq!(s1, &[3, 4, 5]);
unsafe { f.extend_from_within_unchecked(0, 3) };
let (s1, _) = f.as_slices();
assert_eq!(s1, &[3, 4, 5, 3, 4, 5]);
}
#[test]
fn set_tail_rolls_back() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[1, 2, 3]);
let saved_tail = f.tail();
let saved_cap = f.cap();
f.extend(&[4, 5, 6, 7]);
assert_eq!(f.len(), 7);
assert_eq!(f.cap(), saved_cap, "with_capacity sized to avoid realloc");
unsafe { f.set_tail(saved_tail) };
assert_eq!(f.len(), 3);
let (s1, _) = f.as_slices();
assert_eq!(s1, &[1, 2, 3]);
}
#[test]
fn clear_resets() {
let mut f = FlatBuf::with_capacity(64);
f.extend(&[1, 2, 3]);
f.drop_first_n(1);
assert_eq!(f.len(), 2);
f.clear();
assert_eq!(f.len(), 0);
assert_eq!(f.tail(), 0);
}
}