#![cfg(target_arch = "aarch64")]
use std::arch::aarch64::*;
#[inline]
unsafe fn first_match_lane(cmp: uint8x16_t) -> Option<usize> {
let nibbles = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)),
0,
);
(nibbles != 0).then(|| (nibbles.trailing_zeros() / 4) as usize)
}
#[inline]
unsafe fn last_match_lane(cmp: uint8x16_t) -> Option<usize> {
let nibbles = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)),
0,
);
(nibbles != 0).then(|| 15 - (nibbles.leading_zeros() / 4) as usize)
}
#[inline]
pub unsafe fn memchr_neon(needle: u8, haystack: &[u8]) -> Option<usize> {
let len = haystack.len();
let ptr = haystack.as_ptr();
let needle_vec = vdupq_n_u8(needle);
let mut offset = 0;
while offset + 16 <= len {
let data = vld1q_u8(ptr.add(offset));
if let Some(lane) = first_match_lane(vceqq_u8(data, needle_vec)) {
return Some(offset + lane);
}
offset += 16;
}
(offset..len).find(|&i| *haystack.get_unchecked(i) == needle)
}
#[inline]
pub unsafe fn memchr2_neon(needle1: u8, needle2: u8, haystack: &[u8]) -> Option<usize> {
let len = haystack.len();
let ptr = haystack.as_ptr();
let (v1, v2) = (vdupq_n_u8(needle1), vdupq_n_u8(needle2));
let mut offset = 0;
while offset + 16 <= len {
let data = vld1q_u8(ptr.add(offset));
let cmp = vorrq_u8(vceqq_u8(data, v1), vceqq_u8(data, v2));
if let Some(lane) = first_match_lane(cmp) {
return Some(offset + lane);
}
offset += 16;
}
(offset..len).find(|&i| {
let b = *haystack.get_unchecked(i);
b == needle1 || b == needle2
})
}
#[inline]
pub unsafe fn memchr3_neon(
needle1: u8,
needle2: u8,
needle3: u8,
haystack: &[u8],
) -> Option<usize> {
let len = haystack.len();
let ptr = haystack.as_ptr();
let (v1, v2, v3) = (
vdupq_n_u8(needle1),
vdupq_n_u8(needle2),
vdupq_n_u8(needle3),
);
let mut offset = 0;
while offset + 16 <= len {
let data = vld1q_u8(ptr.add(offset));
let cmp = vorrq_u8(
vorrq_u8(vceqq_u8(data, v1), vceqq_u8(data, v2)),
vceqq_u8(data, v3),
);
if let Some(lane) = first_match_lane(cmp) {
return Some(offset + lane);
}
offset += 16;
}
(offset..len).find(|&i| {
let b = *haystack.get_unchecked(i);
b == needle1 || b == needle2 || b == needle3
})
}
#[inline]
pub unsafe fn memchr_range_neon(lo: u8, hi: u8, haystack: &[u8]) -> Option<usize> {
let len = haystack.len();
let ptr = haystack.as_ptr();
let (lo_vec, hi_vec) = (vdupq_n_u8(lo), vdupq_n_u8(hi));
let mut offset = 0;
while offset + 16 <= len {
let data = vld1q_u8(ptr.add(offset));
let cmp = vandq_u8(vcgeq_u8(data, lo_vec), vcleq_u8(data, hi_vec));
if let Some(lane) = first_match_lane(cmp) {
return Some(offset + lane);
}
offset += 16;
}
(offset..len).find(|&i| {
let b = *haystack.get_unchecked(i);
b >= lo && b <= hi
})
}
#[inline]
pub unsafe fn memrchr_neon(needle: u8, haystack: &[u8]) -> Option<usize> {
let len = haystack.len();
let ptr = haystack.as_ptr();
let needle_vec = vdupq_n_u8(needle);
let mut end = len;
while end >= 16 {
let offset = end - 16;
let data = vld1q_u8(ptr.add(offset));
if let Some(lane) = last_match_lane(vceqq_u8(data, needle_vec)) {
return Some(offset + lane);
}
end = offset;
}
(0..end)
.rev()
.find(|&i| *haystack.get_unchecked(i) == needle)
}