#[cfg(any(
test,
all(
target_arch = "x86_64",
target_feature = "gfni",
target_feature = "avx512f",
target_feature = "avx512bw"
)
))]
pub(crate) const fn group_mask(group: usize, offset: usize) -> u64 {
let mut mask = 0u64;
let mut position = offset;
while position < 64 {
mask |= 1 << position;
position += group;
}
mask
}
pub(crate) trait ByteRegister: Copy {
const BYTES: usize;
unsafe fn load(from: *const u8) -> Self;
unsafe fn store(to: *mut u8, value: Self);
fn xor(self, other: Self) -> Self;
}
#[cfg(any(
test,
all(
target_arch = "x86_64",
target_feature = "gfni",
target_feature = "avx512f",
target_feature = "avx512bw"
)
))]
pub(crate) trait ByteLanes: ByteRegister {
fn rotate_group<const GROUP: usize>(self, shift: usize) -> Self;
fn affine(self, matrix: u64) -> Self;
fn affine_merge(self, source: Self, matrix: u64, mask: u64) -> Self;
}
#[allow(clippy::chunks_exact_to_as_chunks)]
#[inline]
pub(crate) fn butterfly_run<L: ByteRegister, const INVERSE: bool>(
lo: &mut [u8],
hi: &mut [u8],
map: impl Fn(L) -> L,
) -> usize {
assert_eq!(lo.len(), hi.len(), "butterfly lengths differ");
let lo = lo.chunks_exact_mut(L::BYTES);
let hi = hi.chunks_exact_mut(L::BYTES);
let covered = lo.len() * L::BYTES;
for (lo, hi) in lo.zip(hi) {
unsafe {
let a = L::load(lo.as_ptr());
let b = L::load(hi.as_ptr());
if INVERSE {
let b = b.xor(a);
L::store(hi.as_mut_ptr(), b);
L::store(lo.as_mut_ptr(), a.xor(map(b)));
} else {
let a = a.xor(map(b));
L::store(lo.as_mut_ptr(), a);
L::store(hi.as_mut_ptr(), b.xor(a));
}
}
}
covered
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "gfni",
target_feature = "avx512f",
target_feature = "avx512bw"
))]
mod x86_64 {
use core::arch::x86_64::{
__m512i, _mm512_gf2p8affine_epi64_epi8, _mm512_loadu_si512,
_mm512_mask_gf2p8affine_epi64_epi8, _mm512_ror_epi32, _mm512_set1_epi64,
_mm512_shuffle_epi8, _mm512_storeu_si512, _mm512_xor_si512,
};
use super::{ByteLanes, ByteRegister};
static SWAP_PAIRS: [u8; 64] = {
let mut pattern = [0u8; 64];
let mut i = 0;
while i < 64 {
pattern[i] = (i ^ 1) as u8;
i += 1;
}
pattern
};
impl ByteRegister for __m512i {
const BYTES: usize = 64;
#[inline(always)]
unsafe fn load(from: *const u8) -> Self {
unsafe { _mm512_loadu_si512(from.cast()) }
}
#[inline(always)]
unsafe fn store(to: *mut u8, value: Self) {
unsafe { _mm512_storeu_si512(to.cast(), value) }
}
#[inline(always)]
fn xor(self, other: Self) -> Self {
unsafe { _mm512_xor_si512(self, other) }
}
}
impl ByteLanes for __m512i {
#[inline(always)]
fn rotate_group<const GROUP: usize>(self, shift: usize) -> Self {
unsafe {
match (GROUP, shift) {
(_, 0) => self,
(2, 1) => {
let pattern = _mm512_loadu_si512(SWAP_PAIRS.as_ptr().cast());
_mm512_shuffle_epi8(self, pattern)
}
(4, 1) => _mm512_ror_epi32::<8>(self),
(4, 2) => _mm512_ror_epi32::<16>(self),
(4, 3) => _mm512_ror_epi32::<24>(self),
_ => panic!("unsupported byte-group rotation"),
}
}
}
#[inline(always)]
fn affine(self, matrix: u64) -> Self {
unsafe { _mm512_gf2p8affine_epi64_epi8::<0>(self, _mm512_set1_epi64(matrix as i64)) }
}
#[inline(always)]
fn affine_merge(self, source: Self, matrix: u64, mask: u64) -> Self {
unsafe {
_mm512_mask_gf2p8affine_epi64_epi8::<0>(
self,
mask,
source,
_mm512_set1_epi64(matrix as i64),
)
}
}
}
}
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
target_endian = "little"
))]
mod aarch64 {
use core::arch::aarch64::{uint8x16_t, veorq_u8, vld1q_u8, vst1q_u8};
use super::ByteRegister;
impl ByteRegister for uint8x16_t {
const BYTES: usize = 16;
#[inline(always)]
unsafe fn load(from: *const u8) -> Self {
unsafe { vld1q_u8(from) }
}
#[inline(always)]
unsafe fn store(to: *mut u8, value: Self) {
unsafe { vst1q_u8(to, value) }
}
#[inline(always)]
fn xor(self, other: Self) -> Self {
unsafe { veorq_u8(self, other) }
}
}
}