use fearless_simd::{Simd, SimdBase, u8x16};
pub(crate) const WILDCOPY_OVERLENGTH: usize = 32;
pub(crate) const INDEX_LIMIT: usize = u32::MAX as usize;
#[inline(always)]
pub(crate) const fn idx(index: usize) -> usize {
debug_assert!(index <= INDEX_LIMIT);
index & INDEX_LIMIT
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum MatchCopy {
Scalar,
Shuffle,
}
const PERIOD: [[u8; 16]; 16] = {
let mut table = [[0u8; 16]; 16];
let mut offset = 1;
while offset < 16 {
let mut lane = 0;
while lane < 16 {
table[offset][lane] = (lane % offset) as u8;
lane += 1;
}
offset += 1;
}
table
};
const ROTATE: [[u8; 16]; 16] = {
let mut table = [[0u8; 16]; 16];
let mut offset = 1;
while offset < 16 {
let mut lane = 0;
while lane < 16 {
table[offset][lane] = ((lane + 16 % offset) % offset) as u8;
lane += 1;
}
offset += 1;
}
table
};
const SPREAD_ADD: [usize; 8] = [0, 1, 2, 1, 4, 4, 4, 4];
const SPREAD_SUB: [usize; 8] = [8, 8, 8, 7, 8, 9, 10, 11];
#[inline(always)]
fn chunk<const N: usize>(out: &mut [u8], src: usize, dst: usize) {
let (src, dst) = (idx(src), idx(dst));
let value: [u8; N] = out[src..src + N].try_into().unwrap_or([0; N]);
out[dst..dst + N].copy_from_slice(&value);
}
#[inline(always)]
pub(crate) fn wildcopy_literals(out: &mut [u8], op: usize, lits: &[u8], lp: usize, len: usize) {
let (op, lp) = (idx(op), idx(lp));
out[op..op + 16].copy_from_slice(&lits[lp..lp + 16]);
if len > 16 {
let end = idx(op + len);
let (mut dst, mut src) = (op + 16, lp + 16);
while dst < end {
let (d, s) = (idx(dst), idx(src));
out[d..d + 32].copy_from_slice(&lits[s..s + 32]);
dst += 32;
src += 32;
}
}
}
#[inline(always)]
pub(crate) fn wildcopy_match<S: Simd>(
simd: S,
mode: MatchCopy,
out: &mut [u8],
op: usize,
offset: usize,
len: usize,
) {
let end = op + len;
let mut src = op - offset;
let mut dst = op;
if offset >= 32 {
loop {
chunk::<32>(out, src, dst);
src += 32;
dst += 32;
if dst >= end {
return;
}
}
}
if offset >= 16 {
loop {
chunk::<16>(out, src, dst);
src += 16;
dst += 16;
if dst >= end {
return;
}
}
}
match mode {
MatchCopy::Shuffle => shuffle_short_match(simd, out, src, dst, offset, end),
MatchCopy::Scalar => spread_short_match(out, src, dst, offset, end),
}
}
#[inline(always)]
fn shuffle_short_match<S: Simd>(
simd: S,
out: &mut [u8],
src: usize,
mut dst: usize,
offset: usize,
end: usize,
) {
let offset = offset & 15;
let source = u8x16::from_slice(simd, &out[src..src + 16]);
let rotate = u8x16::from_slice(simd, &ROTATE[offset]);
let mut period = simd.swizzle_dyn_u8x16(source, u8x16::from_slice(simd, &PERIOD[offset]));
loop {
out[dst..dst + 16].copy_from_slice(period.as_slice());
dst += 16;
if dst >= end {
return;
}
period = simd.swizzle_dyn_u8x16(period, rotate);
}
}
#[inline(always)]
fn spread_short_match(out: &mut [u8], src: usize, dst: usize, offset: usize, end: usize) {
let mut src = src;
if offset < 8 {
for k in 0..4 {
out[dst + k] = out[src + k];
}
let shifted = src + SPREAD_ADD[offset];
chunk::<4>(out, shifted, dst + 4);
src = shifted + 8 - SPREAD_SUB[offset];
} else {
chunk::<8>(out, src, dst);
src += 8;
}
let mut dst = dst + 8;
while dst < end {
chunk::<8>(out, src, dst);
src += 8;
dst += 8;
}
}
pub(crate) fn exact_match(out: &mut [u8], op: usize, offset: usize, len: usize) {
let start = op - offset;
if offset >= len {
out.copy_within(start..start + len, op);
return;
}
let mut copied = 0;
while copied < len {
let step = (len - copied).min(offset + copied);
out.copy_within(start..start + step, op + copied);
copied += step;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Backend;
use alloc::vec::Vec;
use fearless_simd::dispatch;
fn naive(prefix: &[u8], offset: usize, len: usize) -> Vec<u8> {
let mut out = prefix.to_vec();
for _ in 0..len {
out.push(out[out.len() - offset]);
}
out
}
fn prefix() -> Vec<u8> {
(0..64u8).map(|byte| byte.wrapping_mul(37) ^ 0x5a).collect()
}
#[test]
fn wildcopied_matches_equal_byte_copies_for_every_offset_and_mode() {
let prefix = prefix();
for backend in Backend::available() {
for mode in [MatchCopy::Scalar, MatchCopy::Shuffle] {
for offset in 1..=prefix.len() {
for len in [3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 64, 100] {
let expected = naive(&prefix, offset, len);
let mut out = prefix.clone();
out.resize(prefix.len() + len + WILDCOPY_OVERLENGTH, 0xee);
dispatch!(backend.0, simd => wildcopy_match(simd, mode, &mut out, prefix.len(), offset, len));
assert_eq!(
out[..expected.len()],
expected[..],
"{backend} {mode:?} offset {offset} len {len}"
);
}
}
}
}
}
#[test]
fn exact_matches_write_nothing_past_their_end() {
let prefix = prefix();
for offset in 1..=prefix.len() {
for len in [1, 3, 8, 16, 17, 100] {
let expected = naive(&prefix, offset, len);
let mut out = prefix.clone();
out.resize(prefix.len() + len + 8, 0xee);
exact_match(&mut out, prefix.len(), offset, len);
assert_eq!(out[..expected.len()], expected[..]);
assert!(out[expected.len()..].iter().all(|&byte| byte == 0xee));
}
}
}
#[test]
fn wildcopied_literals_equal_their_source() {
let lits: Vec<u8> = (0..200u8).collect();
for len in [0, 1, 15, 16, 17, 32, 33, 48, 49, 100] {
let mut out = alloc::vec![0u8; 8 + len + WILDCOPY_OVERLENGTH];
wildcopy_literals(&mut out, 8, &lits, 3, len);
assert_eq!(out[8..8 + len], lits[3..3 + len]);
}
}
#[test]
fn shuffle_tables_spread_and_rotate_the_period() {
for offset in 1..16 {
for lane in 0..16 {
assert_eq!(usize::from(PERIOD[offset][lane]), lane % offset);
let next = (lane + 16) % offset;
assert_eq!(
usize::from(PERIOD[offset][usize::from(ROTATE[offset][lane])]),
next
);
}
}
}
}