#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
target_endian = "little"
))]
pub(crate) const MIN_BYTES: usize = 256;
#[inline]
fn nibble_tables(columns: &[u8; 8]) -> ([u8; 16], [u8; 16]) {
let mut low = [0u8; 16];
let mut high = [0u8; 16];
for b in 0..4 {
for n in 0..(1usize << b) {
low[n | (1 << b)] = low[n] ^ columns[b];
high[n | (1 << b)] = high[n] ^ columns[4 + b];
}
}
(low, high)
}
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
target_endian = "little"
))]
#[inline]
pub(crate) fn byte_butterfly<const INVERSE: bool>(
lo: &mut [u8],
hi: &mut [u8],
columns: &[u8; 8],
) -> usize {
use core::arch::aarch64::{
uint8x16_t, vandq_u8, vdupq_n_u8, veorq_u8, vld1q_u8, vqtbl1q_u8, vshrq_n_u8,
};
use crate::lanes::butterfly_run;
let (low, high) = nibble_tables(columns);
let (low, high, nibble) = unsafe {
(
vld1q_u8(low.as_ptr()),
vld1q_u8(high.as_ptr()),
vdupq_n_u8(0x0f),
)
};
butterfly_run::<uint8x16_t, INVERSE>(lo, hi, |x| {
unsafe {
veorq_u8(
vqtbl1q_u8(low, vandq_u8(x, nibble)),
vqtbl1q_u8(high, vshrq_n_u8::<4>(x)),
)
}
})
}
#[cfg(test)]
mod tests {
use p3_binary_field::{BinaryField8, TowerLevel};
use super::nibble_tables;
#[test]
fn the_nibble_tables_are_the_field_product() {
for t in 0..=u8::MAX {
let t = BinaryField8::from_repr(t);
let columns = core::array::from_fn(|b| (t * BinaryField8::from_repr(1 << b)).to_repr());
let (low, high) = nibble_tables(&columns);
for x in 0..=u8::MAX {
let got = low[(x & 0x0f) as usize] ^ high[(x >> 4) as usize];
let want = (t * BinaryField8::from_repr(x)).to_repr();
assert_eq!(got, want, "t={t:?} x={x:#04x}");
}
}
}
}