#[inline]
pub fn simd_memcmp(a: &[u8], b: &[u8]) -> usize {
#[cfg(target_arch = "x86_64")]
{
if std::is_x86_feature_detected!("avx2") {
return unsafe { simd_memcmp_avx2(a, b) };
}
}
#[cfg(target_arch = "aarch64")]
{
if std::arch::is_aarch64_feature_detected!("neon") {
return unsafe { simd_memcmp_neon(a, b) };
}
}
simd_memcmp_scalar(a, b)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[allow(clippy::cast_ptr_alignment)]
unsafe fn simd_memcmp_avx2(a: &[u8], b: &[u8]) -> usize {
use std::arch::x86_64::{__m256i, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8};
let len = a.len().min(b.len());
let mut i: usize = 0;
let pa = a.as_ptr();
let pb = b.as_ptr();
while let Some(next) = i.checked_add(32) {
if next > len {
break;
}
let chunk_a = _mm256_loadu_si256(pa.add(i).cast::<__m256i>());
let chunk_b = _mm256_loadu_si256(pb.add(i).cast::<__m256i>());
let cmp = _mm256_cmpeq_epi8(chunk_a, chunk_b);
let mask_signed = _mm256_movemask_epi8(cmp);
let mask = u32::from_ne_bytes(mask_signed.to_ne_bytes());
if mask != u32::MAX {
let inverted = !mask;
let diff_index = usize::try_from(inverted.trailing_zeros()).unwrap_or(usize::MAX);
return i.checked_add(diff_index).unwrap_or(len);
}
i = next;
}
simd_memcmp_tail(a, b, i, len)
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
unsafe fn simd_memcmp_neon(a: &[u8], b: &[u8]) -> usize {
use std::arch::aarch64::{vceqq_u8, vld1q_u8, vminvq_u8};
let len = a.len().min(b.len());
let mut i: usize = 0;
let pa = a.as_ptr();
let pb = b.as_ptr();
while let Some(next) = i.checked_add(16) {
if next > len {
break;
}
let chunk_a = vld1q_u8(pa.add(i));
let chunk_b = vld1q_u8(pb.add(i));
let cmp = vceqq_u8(chunk_a, chunk_b);
let min = vminvq_u8(cmp);
if min != 0xFF_u8 {
let mut j: usize = 0;
while j < 16 {
let pos = i.checked_add(j).unwrap_or(len);
let av = a.get(pos).copied().unwrap_or(0);
let bv = b.get(pos).copied().unwrap_or(0);
if av != bv || pos >= len {
return pos;
}
j = j.checked_add(1).unwrap_or(16);
}
}
i = next;
}
simd_memcmp_tail(a, b, i, len)
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
#[inline]
fn simd_memcmp_tail(a: &[u8], b: &[u8], start: usize, len: usize) -> usize {
let mut i = start;
while i < len {
let av = a.get(i).copied().unwrap_or(0);
let bv = b.get(i).copied().unwrap_or(0);
if av != bv {
return i;
}
i = i.checked_add(1).unwrap_or(len);
}
i
}
#[inline]
fn simd_memcmp_scalar(a: &[u8], b: &[u8]) -> usize {
a.iter().zip(b.iter()).take_while(|(x, y)| x == y).count()
}