use super::super::ByteOrder;
use super::super::add::read_small;
use super::{AND, AND_NOT, NOT_AND, OR, XOR};
use core::arch::aarch64::{
uint8x16_t, uint16x8_t, vaddlvq_u8, vaddq_u8, vaddq_u32, vaddvq_u32, vandq_u8, vbicq_u8,
vcgeq_u8, vcntq_u8, vdupq_n_u8, vdupq_n_u16, veorq_u8, vextq_u8, vld1q_u8, vorrq_u8,
vpadalq_u8, vpaddlq_u16, vrev64q_u8, vst1q_u8,
};
#[inline(always)]
unsafe fn op<const OP: u8>(x: uint8x16_t, y: uint8x16_t) -> uint8x16_t {
unsafe {
match OP {
AND => vandq_u8(x, y),
OR => vorrq_u8(x, y),
XOR => veorq_u8(x, y),
AND_NOT => vbicq_u8(x, y),
_ => {
debug_assert!(OP == NOT_AND);
vbicq_u8(y, x)
}
}
}
}
#[inline(always)]
fn op64<const OP: u8>(x: u64, y: u64) -> u64 {
match OP {
AND => x & y,
OR => x | y,
XOR => x ^ y,
AND_NOT => x & !y,
_ => !x & y,
}
}
#[inline(always)]
unsafe fn reversed(v: uint8x16_t) -> uint8x16_t {
unsafe {
let r = vrev64q_u8(v);
vextq_u8::<8>(r, r)
}
}
#[inline(always)]
unsafe fn block<const OP: u8, const MIXED: bool>(
x: *const u8,
y: *const u8,
n: usize,
k: usize,
) -> uint8x16_t {
unsafe {
let a = vld1q_u8(x.add(k));
let b = if MIXED {
reversed(vld1q_u8(y.add(n - 16 - k)))
} else {
vld1q_u8(y.add(k))
};
op::<OP>(a, b)
}
}
#[inline(always)]
unsafe fn read8(p: *const u8) -> u64 {
unsafe { u64::from_ne_bytes(p.cast::<[u8; 8]>().read()) }
}
#[inline(always)]
unsafe fn write8(p: *mut u8, v: u64) {
unsafe { p.cast::<[u8; 8]>().write(v.to_ne_bytes()) }
}
#[inline]
pub(super) unsafe fn logic<const OP: u8, const MIXED: bool, const DOWN: bool>(
dst: *mut u8,
x: *const u8,
y: *const u8,
n: usize,
) {
unsafe {
if n >= 16 {
let last = if DOWN { 0 } else { n - 16 };
let w = block::<OP, MIXED>(x, y, n, last);
if DOWN {
let mut e = n;
while e > 64 {
for j in 1..=4 {
let k = e - 16 * j;
vst1q_u8(dst.add(k), block::<OP, MIXED>(x, y, n, k));
}
e -= 64;
}
while e > 16 {
e -= 16;
vst1q_u8(dst.add(e), block::<OP, MIXED>(x, y, n, e));
}
} else {
let mut k = 0;
while k + 64 < n {
for j in 0..4 {
let b = k + 16 * j;
vst1q_u8(dst.add(b), block::<OP, MIXED>(x, y, n, b));
}
k += 64;
}
while k + 16 < n {
vst1q_u8(dst.add(k), block::<OP, MIXED>(x, y, n, k));
k += 16;
}
}
vst1q_u8(dst.add(last), w);
} else if n >= 8 {
let (lo, hi) = if MIXED {
(
op64::<OP>(read8(x), read8(y.add(n - 8)).swap_bytes()),
op64::<OP>(read8(x.add(n - 8)), read8(y).swap_bytes()),
)
} else {
(
op64::<OP>(read8(x), read8(y)),
op64::<OP>(read8(x.add(n - 8)), read8(y.add(n - 8))),
)
};
write8(dst, lo);
write8(dst.add(n - 8), hi);
} else {
for i in 0..n {
let k = if DOWN { n - 1 - i } else { i };
let b = if MIXED { *y.add(n - 1 - k) } else { *y.add(k) };
*dst.add(k) = op64::<OP>(u64::from(*x.add(k)), u64::from(b)) as u8;
}
}
}
}
#[inline(never)]
pub(super) unsafe fn copy_reversed(dst: *mut u8, src: *const u8, n: usize) {
unsafe {
if n >= 16 {
let mut k = 0;
while k + 16 < n {
vst1q_u8(dst.add(k), reversed(vld1q_u8(src.add(n - 16 - k))));
k += 16;
}
vst1q_u8(dst.add(n - 16), reversed(vld1q_u8(src)));
} else if n >= 8 {
write8(dst, read8(src.add(n - 8)).swap_bytes());
write8(dst.add(n - 8), read8(src).swap_bytes());
} else {
for k in 0..n {
*dst.add(k) = *src.add(n - 1 - k);
}
}
}
}
const STEPS: usize = 1023;
#[inline]
pub(super) unsafe fn count_ones(p: *const u8, n: usize) -> u64 {
unsafe {
if n <= 16 {
let v = read_small(
&*core::ptr::slice_from_raw_parts(p, n),
ByteOrder::LittleEndian,
);
return u64::from(v.count_ones());
}
let count = |k: usize| vcntq_u8(vld1q_u8(p.add(k)));
let mut ones = 0u64;
let mut k = 0;
while n - k >= 128 {
let steps = ((n - k) / 128).min(STEPS);
let (mut a0, mut a1): (uint16x8_t, uint16x8_t) = (vdupq_n_u16(0), vdupq_n_u16(0));
for _ in 0..steps {
let t0 = vaddq_u8(
vaddq_u8(count(k), count(k + 16)),
vaddq_u8(count(k + 32), count(k + 48)),
);
let t1 = vaddq_u8(
vaddq_u8(count(k + 64), count(k + 80)),
vaddq_u8(count(k + 96), count(k + 112)),
);
a0 = vpadalq_u8(a0, t0);
a1 = vpadalq_u8(a1, t1);
k += 128;
}
ones += u64::from(vaddvq_u32(vaddq_u32(vpaddlq_u16(a0), vpaddlq_u16(a1))));
}
let (mut b, mut c) = (vdupq_n_u8(0), vdupq_n_u8(0));
while n - k >= 32 {
b = vaddq_u8(b, count(k));
c = vaddq_u8(c, count(k + 16));
k += 32;
}
if n - k >= 16 {
b = vaddq_u8(b, count(k));
k += 16;
}
if k < n {
const LANES: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let lanes = vld1q_u8(LANES.as_ptr());
let new = vcgeq_u8(lanes, vdupq_n_u8((16 - (n - k)) as u8));
b = vaddq_u8(b, vandq_u8(count(n - 16), new));
}
ones + u64::from(vaddlvq_u8(vaddq_u8(b, c)))
}
}