use crate::io::{Error, Read};
use super::buffer_backend::{BufferBackend, WILDCOPY_OVERLENGTH};
pub(crate) struct UserSliceBackend<'a> {
slice: &'a mut [u8],
head: usize,
tail: usize,
}
impl<'a> UserSliceBackend<'a> {
pub(crate) fn from_slice(slice: &'a mut [u8]) -> Self {
Self {
slice,
head: 0,
tail: 0,
}
}
}
impl<'a> BufferBackend for UserSliceBackend<'a> {
fn new() -> Self {
Self {
slice: &mut [],
head: 0,
tail: 0,
}
}
#[inline]
fn clear(&mut self) {
self.head = 0;
self.tail = 0;
}
#[inline]
fn reserve(&mut self, _n: usize) {
}
#[inline]
fn len(&self) -> usize {
self.tail - self.head
}
#[inline]
fn cap(&self) -> usize {
self.slice.len()
}
#[inline]
fn tail(&self) -> usize {
self.tail
}
#[inline]
unsafe fn set_tail(&mut self, new_tail: usize) {
debug_assert!(new_tail >= self.head);
debug_assert!(new_tail <= self.slice.len());
self.tail = new_tail;
}
#[inline]
fn extend(&mut self, data: &[u8]) {
let len = data.len();
let new_tail = self.tail + len;
assert!(
new_tail <= self.slice.len(),
"UserSliceBackend::extend overflows slice (tail+={}, cap={}) — corrupt frame",
len,
self.slice.len()
);
let total_writable = self.slice.len() - self.tail;
unsafe {
super::simd_copy::copy_bytes_overshooting(
(data.as_ptr(), len),
(self.slice.as_mut_ptr().add(self.tail), total_writable),
len,
);
}
self.tail = new_tail;
}
#[inline]
fn extend_and_fill(&mut self, fill_with: u8, fill_length: usize) {
let new_tail = self.tail + fill_length;
assert!(
new_tail <= self.slice.len(),
"UserSliceBackend::extend_and_fill overflows slice (tail+={}, cap={}) — corrupt frame",
fill_length,
self.slice.len()
);
self.slice[self.tail..new_tail].fill(fill_with);
self.tail = new_tail;
}
fn extend_from_reader<R: Read>(
&mut self,
mut read: R,
fill_length: usize,
) -> Result<(), Error> {
let old = self.tail;
let new_tail = old + fill_length;
if new_tail > self.slice.len() {
return Err(Error::other(
"UserSliceBackend: raw block exceeds caller-provided output capacity",
));
}
match read.read_exact(&mut self.slice[old..new_tail]) {
Ok(()) => {
self.tail = new_tail;
Ok(())
}
Err(e) => Err(e),
}
}
#[inline]
unsafe fn extend_from_within_unchecked(&mut self, start: usize, len: usize) {
let dst_off = self.tail;
let src_off = self.head + start;
debug_assert!(src_off + len <= dst_off);
assert!(
dst_off + len <= self.slice.len(),
"UserSliceBackend: match write past slice capacity (corrupt frame)"
);
let total_readable = self.tail - src_off;
let total_writable = self.slice.len() - dst_off;
unsafe {
let base = self.slice.as_mut_ptr();
super::simd_copy::copy_bytes_overshooting(
(base.add(src_off), total_readable),
(base.add(dst_off), total_writable),
len,
);
}
self.tail = 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.slice[self.head..self.tail], &[])
}
#[inline]
fn drop_first_n(&mut self, n: usize) {
self.head += n;
debug_assert!(self.head <= self.tail);
}
}
const _: () = {
let _: usize = WILDCOPY_OVERLENGTH;
};
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::vec;
#[test]
fn extend_writes_at_tail() {
let mut buf = vec![0u8; 32];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[1, 2, 3, 4]);
assert_eq!(b.len(), 4);
assert_eq!(b.tail(), 4);
b.extend(&[5, 6]);
let (s, t) = b.as_slices();
assert_eq!(s, &[1, 2, 3, 4, 5, 6]);
assert!(t.is_empty());
}
#[test]
fn extend_and_fill_repeats_byte() {
let mut buf = vec![0u8; 16];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[0xAA]);
b.extend_and_fill(0xBB, 4);
let (s, _) = b.as_slices();
assert_eq!(s, &[0xAA, 0xBB, 0xBB, 0xBB, 0xBB]);
}
#[test]
fn extend_from_within_unchecked_copies_non_overlapping() {
let mut buf = vec![0u8; 32];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[10, 20, 30, 40, 50]);
unsafe { b.extend_from_within_unchecked(0, 3) };
let (s, _) = b.as_slices();
assert_eq!(s, &[10, 20, 30, 40, 50, 10, 20, 30]);
}
#[test]
fn drop_first_n_advances_head_keeps_history() {
let mut buf = vec![0u8; 32];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[1, 2, 3, 4, 5]);
b.drop_first_n(2);
assert_eq!(b.len(), 3);
let (s, _) = b.as_slices();
assert_eq!(s, &[3, 4, 5]);
unsafe { b.extend_from_within_unchecked(0, 3) };
let (s, _) = b.as_slices();
assert_eq!(s, &[3, 4, 5, 3, 4, 5]);
}
#[test]
fn set_tail_rollback() {
let mut buf = vec![0u8; 32];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[1, 2, 3]);
let saved = b.tail();
b.extend(&[4, 5, 6, 7]);
assert_eq!(b.len(), 7);
unsafe { b.set_tail(saved) };
assert_eq!(b.len(), 3);
let (s, _) = b.as_slices();
assert_eq!(s, &[1, 2, 3]);
}
#[test]
fn clear_resets_cursors() {
let mut buf = vec![0u8; 32];
let mut b = UserSliceBackend::from_slice(&mut buf);
b.extend(&[1, 2, 3]);
b.drop_first_n(1);
b.clear();
assert_eq!(b.len(), 0);
assert_eq!(b.tail(), 0);
}
#[test]
fn extend_from_reader_into_slice() {
let mut buf = vec![0u8; 16];
let mut b = UserSliceBackend::from_slice(&mut buf);
let src = [9u8, 8, 7, 6, 5];
b.extend_from_reader(&src[..], 5).unwrap();
let (s, _) = b.as_slices();
assert_eq!(s, &[9, 8, 7, 6, 5]);
}
#[test]
fn extend_from_reader_over_capacity_errors() {
let mut buf = vec![0u8; 4];
let mut b = UserSliceBackend::from_slice(&mut buf);
let src = [9u8, 8, 7, 6, 5];
assert!(b.extend_from_reader(&src[..], 5).is_err());
assert_eq!(b.tail(), 0);
}
}