#![cfg(all(target_arch = "aarch64", target_endian = "little"))]
use core::arch::aarch64::{
uint8x16_t, vandq_u8, vceqq_u8, vget_lane_u64, vld1q_u8, vminvq_u8, vreinterpret_u64_u8,
vreinterpretq_u16_u8, vshrn_n_u16,
};
use super::scalar;
#[target_feature(enable = "neon")]
#[inline]
fn first_unequal(eq: uint8x16_t) -> usize {
let nibbles = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(eq), 4)),
0,
);
((!nibbles).trailing_zeros() / 4) as usize
}
#[target_feature(enable = "neon")]
#[inline]
pub(crate) unsafe fn prefix_len_simd(lhs: *const u8, rhs: *const u8, max: usize) -> usize {
let mut off = 0usize;
while off + 32 <= max {
let (eq0, eq1) = unsafe {
(
vceqq_u8(vld1q_u8(lhs.add(off)), vld1q_u8(rhs.add(off))),
vceqq_u8(vld1q_u8(lhs.add(off + 16)), vld1q_u8(rhs.add(off + 16))),
)
};
if vminvq_u8(vandq_u8(eq0, eq1)) != u8::MAX {
if vminvq_u8(eq0) != u8::MAX {
return off + first_unequal(eq0);
}
return off + 16 + first_unequal(eq1);
}
off += 32;
}
if off + 16 <= max {
let eq = unsafe { vceqq_u8(vld1q_u8(lhs.add(off)), vld1q_u8(rhs.add(off))) };
if vminvq_u8(eq) != u8::MAX {
return off + first_unequal(eq);
}
off += 16;
}
off
}
#[target_feature(enable = "neon")]
#[inline]
pub(crate) unsafe fn common_prefix_len_ptr(lhs: *const u8, rhs: *const u8, max: usize) -> usize {
let chunk = core::mem::size_of::<usize>();
if chunk <= max {
let lhs_word = unsafe { core::ptr::read_unaligned(lhs.cast::<usize>()) };
let rhs_word = unsafe { core::ptr::read_unaligned(rhs.cast::<usize>()) };
let diff = lhs_word ^ rhs_word;
if diff != 0 {
return scalar::mismatch_byte_index(diff);
}
}
let off = unsafe { prefix_len_simd(lhs, rhs, max) };
unsafe { scalar::common_prefix_len_scalar_ptr(lhs, rhs, off, max) }
}
#[target_feature(enable = "neon")]
#[inline]
pub(crate) unsafe fn count_match_from_indices(
concat: &[u8],
current_idx: usize,
candidate_idx: usize,
tail_limit: usize,
seed_len: usize,
) -> usize {
let seed = seed_len.min(tail_limit);
if seed == tail_limit {
return seed;
}
let remaining = tail_limit - seed;
let base = concat.as_ptr();
let lhs = unsafe { base.add(candidate_idx + seed) };
let rhs = unsafe { base.add(current_idx + seed) };
let extra = unsafe { common_prefix_len_ptr(lhs, rhs, remaining) };
seed + extra
}
#[cfg(test)]
mod tests;