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,
}
}
#[cfg(feature = "hash")]
pub(crate) fn written_since(&self, from: usize) -> &[u8] {
&self.slice[from..self.tail]
}
#[inline]
unsafe fn exec_sequence_bounded(
&mut self,
lit_src: *const u8,
lit_length: usize,
offset: usize,
match_length: usize,
) {
unsafe {
super::exec_sequence_inline::exec_sequence_bounded_copy(
self.slice.as_mut_ptr(),
self.tail,
lit_src,
lit_length,
offset,
match_length,
);
}
self.tail += lit_length + match_length;
}
}
impl<'a> BufferBackend for UserSliceBackend<'a> {
const SUPPORTS_INLINE_SEQUENCE_EXEC: bool = true;
const INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER: bool = false;
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn exec_sequence_inline(
&mut self,
lit_src: *const u8,
lit_length: usize,
offset: usize,
match_length: usize,
) -> Result<(), super::errors::ExecuteSequencesError> {
use super::buffer_backend::sequence_output_fits;
use super::exec_sequence_inline::x86::{
copy16, overlap_copy8, wildcopy_no_overlap, wildcopy_overlap_8byte_stride,
};
const MAX_WILDCOPY_OVERSHOOT: usize = 15;
let cap = self.slice.len();
let total = sequence_output_fits(lit_length, match_length, self.tail, cap, 0)?;
let new_tail = self.tail + total;
debug_assert!(offset >= 1);
debug_assert!(match_length >= 1);
let live_len = self.tail - self.head;
debug_assert!(
live_len
.checked_add(lit_length)
.is_some_and(|end| offset <= end),
"exec_sequence_inline: offset ({}) exceeds live window (len={} + lit={}, head={}, tail={})",
offset,
live_len,
lit_length,
self.head,
self.tail,
);
if total + MAX_WILDCOPY_OVERSHOOT > cap - self.tail {
unsafe { self.exec_sequence_bounded(lit_src, lit_length, offset, match_length) };
return Ok(());
}
unsafe {
let base_mut = self.slice.as_mut_ptr();
let op_lit = base_mut.add(self.tail);
copy16(op_lit, lit_src);
if lit_length > 16 {
wildcopy_no_overlap(op_lit.add(16), lit_src.add(16), lit_length - 16);
}
let op_match = base_mut.add(self.tail + lit_length);
let match_src = base_mut.cast_const().add(self.tail + lit_length - offset);
if offset >= 16 {
wildcopy_no_overlap(op_match, match_src, match_length);
} else {
let (op2, ip2) = overlap_copy8(op_match, match_src, offset);
if match_length > 8 {
wildcopy_overlap_8byte_stride(op2, ip2, match_length - 8);
}
}
}
self.tail = new_tail;
Ok(())
}
#[cfg(not(target_arch = "x86_64"))]
#[inline(always)]
unsafe fn exec_sequence_inline(
&mut self,
lit_src: *const u8,
lit_length: usize,
offset: usize,
match_length: usize,
) -> Result<(), super::errors::ExecuteSequencesError> {
use super::buffer_backend::sequence_output_fits;
use super::exec_sequence_inline::portable::{
copy16, overlap_copy8, wildcopy_no_overlap, wildcopy_overlap_8byte_stride,
};
const MAX_WILDCOPY_OVERSHOOT: usize = 15;
let cap = self.slice.len();
let total = sequence_output_fits(lit_length, match_length, self.tail, cap, 0)?;
let new_tail = self.tail + total;
debug_assert!(offset >= 1);
debug_assert!(match_length >= 1);
let live_len = self.tail - self.head;
debug_assert!(
live_len
.checked_add(lit_length)
.is_some_and(|end| offset <= end),
"exec_sequence_inline: offset ({}) exceeds live window (len={} + lit={}, head={}, tail={})",
offset,
live_len,
lit_length,
self.head,
self.tail,
);
if total + MAX_WILDCOPY_OVERSHOOT > cap - self.tail {
unsafe { self.exec_sequence_bounded(lit_src, lit_length, offset, match_length) };
return Ok(());
}
unsafe {
let base = self.slice.as_mut_ptr();
let op_lit = base.add(self.tail);
copy16(op_lit, lit_src);
if lit_length > 16 {
wildcopy_no_overlap(op_lit.add(16), lit_src.add(16), lit_length - 16);
}
let op_match = base.add(self.tail + lit_length);
let match_src = base.cast_const().add(self.tail + lit_length - offset);
if offset >= 16 {
wildcopy_no_overlap(op_match, match_src, match_length);
} else {
let (op2, ip2) = overlap_copy8(op_match, match_src, offset);
if match_length > 8 {
wildcopy_overlap_8byte_stride(op2, ip2, match_length - 8);
}
}
}
self.tail = new_tail;
Ok(())
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn exec_sequence_inline_avx2(
&mut self,
lit_src: *const u8,
lit_length: usize,
offset: usize,
match_length: usize,
) -> Result<(), super::errors::ExecuteSequencesError> {
use super::buffer_backend::sequence_output_fits;
use super::exec_sequence_inline::x86::{
copy16, overlap_copy8, wildcopy_no_overlap, wildcopy_no_overlap_avx2,
wildcopy_overlap_8byte_stride,
};
const MAX_WILDCOPY_OVERSHOOT: usize = 31;
let cap = self.slice.len();
let total = sequence_output_fits(lit_length, match_length, self.tail, cap, 0)?;
let new_tail = self.tail + total;
debug_assert!(offset >= 1);
debug_assert!(match_length >= 1);
let live_len = self.tail - self.head;
debug_assert!(
live_len
.checked_add(lit_length)
.is_some_and(|end| offset <= end),
"exec_sequence_inline_avx2: offset ({}) exceeds live window (len={} + lit={}, head={}, tail={})",
offset,
live_len,
lit_length,
self.head,
self.tail,
);
if total + MAX_WILDCOPY_OVERSHOOT > cap - self.tail {
unsafe { self.exec_sequence_bounded(lit_src, lit_length, offset, match_length) };
return Ok(());
}
unsafe {
let base_mut = self.slice.as_mut_ptr();
let op_lit = base_mut.add(self.tail);
copy16(op_lit, lit_src);
if lit_length > 16 {
wildcopy_no_overlap(op_lit.add(16), lit_src.add(16), lit_length - 16);
}
let op_match = base_mut.add(self.tail + lit_length);
let match_src = base_mut.cast_const().add(self.tail + lit_length - offset);
if offset >= 32 {
wildcopy_no_overlap_avx2(op_match, match_src, match_length);
} else if offset >= 16 {
wildcopy_no_overlap(op_match, match_src, match_length);
} else {
let (op2, ip2) = overlap_copy8(op_match, match_src, offset);
if match_length > 8 {
wildcopy_overlap_8byte_stride(op2, ip2, match_length - 8);
}
}
}
self.tail = new_tail;
Ok(())
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn inline_exec_base_ptr(&mut self) -> *mut u8 {
self.slice.as_mut_ptr()
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn inline_exec_commit(&mut self, new_tail: usize) {
self.tail = new_tail;
}
fn new() -> Self {
Self {
slice: &mut [],
head: 0,
tail: 0,
}
}
#[inline]
fn clear(&mut self) {
self.head = 0;
self.tail = 0;
}
#[inline(always)]
fn try_reserve(&mut self, n: usize) -> Result<(), super::buffer_backend::BackendOverflow> {
match self.tail.checked_add(n) {
Some(new_tail) if new_tail <= self.slice.len() => Ok(()),
_ => Err(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: n,
capacity: self.slice.len(),
}),
}
}
#[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(always)]
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);
}
#[inline(always)]
fn try_extend(&mut self, data: &[u8]) -> Result<(), super::buffer_backend::BackendOverflow> {
let len = data.len();
let new_tail =
self.tail
.checked_add(len)
.ok_or(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
})?;
if new_tail > self.slice.len() {
return Err(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: 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;
Ok(())
}
#[inline(always)]
fn try_extend_and_fill(
&mut self,
fill_with: u8,
fill_length: usize,
) -> Result<(), super::buffer_backend::BackendOverflow> {
let new_tail =
self.tail
.checked_add(fill_length)
.ok_or(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: fill_length,
capacity: self.slice.len(),
})?;
if new_tail > self.slice.len() {
return Err(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: fill_length,
capacity: self.slice.len(),
});
}
self.slice[self.tail..new_tail].fill(fill_with);
self.tail = new_tail;
Ok(())
}
#[inline(always)]
fn try_extend_from_within(
&mut self,
start: usize,
len: usize,
) -> Result<(), super::buffer_backend::BackendOverflow> {
let abs_start =
self.head
.checked_add(start)
.ok_or(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
})?;
let abs_end = abs_start
.checked_add(len)
.ok_or(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
})?;
if abs_end > self.tail {
return Err(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
});
}
let new_tail =
self.tail
.checked_add(len)
.ok_or(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
})?;
if new_tail > self.slice.len() {
return Err(super::buffer_backend::BackendOverflow {
tail: self.tail,
requested: len,
capacity: self.slice.len(),
});
}
unsafe { self.extend_from_within_unchecked(start, len) };
Ok(())
}
}
const _: () = {
let _: usize = WILDCOPY_OVERLENGTH;
};
#[cfg(test)]
mod tests;