use crate::io::{Error, Read};
pub(crate) const WILDCOPY_OVERLENGTH: usize = 32;
#[inline(always)]
pub(crate) fn sequence_output_fits(
lit_length: usize,
match_length: usize,
tail: usize,
cap: usize,
overshoot: usize,
) -> Result<usize, super::errors::ExecuteSequencesError> {
let total = lit_length + match_length;
if total + overshoot > cap - tail {
return Err(super::errors::ExecuteSequencesError::OutputBufferOverflow {
tail,
requested: total,
capacity: cap,
});
}
Ok(total)
}
pub(crate) trait BufferBackend: Sized {
const SUPPORTS_INLINE_SEQUENCE_EXEC: bool = false;
const INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER: bool = true;
const FIXED_CAPACITY: bool = false;
#[allow(unused_variables, unused_mut)]
#[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> {
unreachable!(
"exec_sequence_inline called on backend whose SUPPORTS_INLINE_SEQUENCE_EXEC is false"
);
}
#[inline(always)]
unsafe fn exec_sequence_inline_dict(
&mut self,
lit_src: *const u8,
lit_length: usize,
dict_src: &[u8],
match_length: usize,
) -> Result<(), super::errors::ExecuteSequencesError> {
#[cfg(not(target_arch = "x86_64"))]
use super::exec_sequence_inline::portable::{copy16, wildcopy_no_overlap};
#[cfg(target_arch = "x86_64")]
use super::exec_sequence_inline::x86::{copy16, wildcopy_no_overlap};
const MAX_WILDCOPY_OVERSHOOT: usize = 15;
debug_assert!(match_length >= 1);
debug_assert!(dict_src.len() >= match_length);
let cap = self.cap();
let tail = self.tail();
let total = sequence_output_fits(lit_length, match_length, tail, cap, 0)?;
unsafe {
let base = self.inline_exec_base_ptr();
let op_lit = base.add(tail);
let dict_ptr = dict_src.as_ptr();
if total + MAX_WILDCOPY_OVERSHOOT > cap - tail {
core::ptr::copy_nonoverlapping(lit_src, op_lit, lit_length);
core::ptr::copy_nonoverlapping(dict_ptr, op_lit.add(lit_length), match_length);
} else {
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(tail + lit_length);
if dict_src.len() >= match_length.next_multiple_of(16) {
wildcopy_no_overlap(op_match, dict_ptr, match_length);
} else {
core::ptr::copy_nonoverlapping(dict_ptr, op_match, match_length);
}
}
self.inline_exec_commit(tail + total);
}
Ok(())
}
#[allow(unused_variables, unused_mut, dead_code)]
#[inline(always)]
unsafe fn exec_sequence_inline_avx2(
&mut self,
lit_src: *const u8,
lit_length: usize,
offset: usize,
match_length: usize,
) -> Result<(), super::errors::ExecuteSequencesError> {
unreachable!(
"exec_sequence_inline_avx2 called on backend that did not override the default \
(UserSliceBackend and FlatBuf override on x86_64)"
);
}
#[allow(dead_code)]
#[inline(always)]
unsafe fn inline_exec_base_ptr(&mut self) -> *mut u8 {
unreachable!("inline_exec_base_ptr on a backend without inline-sequence support")
}
#[allow(dead_code)]
#[inline(always)]
unsafe fn inline_exec_commit(&mut self, _new_tail: usize) {
unreachable!("inline_exec_commit on a backend without inline-sequence support")
}
#[allow(unused_variables)]
#[inline(always)]
fn inline_exec_ok(&self, lit_length: usize, match_length: usize, offset: usize) -> bool {
true
}
#[allow(unused_variables)]
#[inline(always)]
fn inline_exec_dict_ok(&self, lit_length: usize, match_length: usize) -> bool {
true
}
fn new() -> Self;
fn clear(&mut self);
fn reserve(&mut self, n: usize);
fn reserve_exact(&mut self, n: usize) {
self.reserve(n);
}
fn try_reserve(&mut self, n: usize) -> Result<(), BackendOverflow> {
self.reserve(n);
Ok(())
}
fn set_max_capacity(&mut self, _max_capacity: usize) {}
fn set_growth_limit(&mut self, _growth_limit: usize) {}
fn len(&self) -> usize;
fn cap(&self) -> usize;
fn tail(&self) -> usize;
unsafe fn set_tail(&mut self, new_tail: usize);
fn extend(&mut self, data: &[u8]);
fn extend_and_fill(&mut self, fill_with: u8, fill_length: usize);
fn extend_from_reader<R: Read>(&mut self, read: R, fill_length: usize) -> Result<(), Error>;
unsafe fn extend_from_within_unchecked(&mut self, start: usize, len: usize);
unsafe fn extend_from_within_unchecked_branchless(&mut self, start: usize, len: usize);
fn as_slices(&self) -> (&[u8], &[u8]);
fn drop_first_n(&mut self, n: usize);
fn try_extend(&mut self, data: &[u8]) -> Result<(), BackendOverflow> {
self.extend(data);
Ok(())
}
fn try_extend_and_fill(
&mut self,
fill_with: u8,
fill_length: usize,
) -> Result<(), BackendOverflow> {
self.extend_and_fill(fill_with, fill_length);
Ok(())
}
#[allow(dead_code)]
fn try_extend_from_within(&mut self, start: usize, len: usize) -> Result<(), BackendOverflow> {
let tail = self.tail();
let capacity = self.cap();
let src_end = start.checked_add(len).ok_or(BackendOverflow {
tail,
requested: len,
capacity,
})?;
if src_end > self.len() {
return Err(BackendOverflow {
tail,
requested: len,
capacity,
});
}
self.reserve(len);
unsafe { self.extend_from_within_unchecked(start, len) };
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct BackendOverflow {
pub tail: usize,
pub requested: usize,
pub capacity: usize,
}
impl core::fmt::Display for BackendOverflow {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"BufferBackend overflow: tail={}, requested={}, capacity={}",
self.tail, self.requested, self.capacity,
)
}
}
#[cfg(test)]
mod tests;