use std::*;
use super::*;
use super::jewel::*;
pub fn hamming_naive(a: &[u8], b: &[u8]) -> u32 {
let len = a.len();
assert!(len == b.len());
let mut res = 0u32;
for i in 0..len {
res += (a[i] != b[i]) as u32;
}
res
}
pub fn hamming_search_naive<'a>(needle: &'a [u8], haystack: &'a [u8]) -> Box<dyn Iterator<Item = Match> + 'a> {
hamming_search_naive_with_opts(needle, haystack, ((needle.len() as u32) >> 1) + ((needle.len() as u32) & 1), SearchType::Best)
}
pub fn hamming_search_naive_with_opts<'a>(needle: &'a [u8], haystack: &'a [u8], k: u32, search_type: SearchType) -> Box<dyn Iterator<Item = Match> + 'a> {
let needle_len = needle.len();
let haystack_len = haystack.len();
if needle_len > haystack_len {
return Box::new(iter::empty());
}
let len = haystack_len + 1 - needle_len;
let mut curr_k = k;
let mut i = 0;
let res = iter::from_fn(move || {
'outer: while i < len {
let mut final_res = 0u32;
for j in 0..needle_len {
final_res += (needle[j] != haystack[i + j]) as u32;
if final_res > curr_k {
i += 1;
continue 'outer;
}
}
match search_type {
SearchType::Best => curr_k = final_res,
_ => ()
}
i += 1;
return Some((Match{start: i - 1, end: i + needle_len - 1, k: final_res}, curr_k));
}
None
});
if search_type == SearchType::Best {
let mut res_vec = Vec::with_capacity(haystack_len / needle_len);
res.for_each(|m| {
res_vec.push(m.0);
curr_k = m.1;
});
return Box::new(res_vec.into_iter().filter(move |m| m.k == curr_k));
}
Box::new(res.map(|m| m.0))
}
pub fn hamming_words_64(a: &[u8], b: &[u8]) -> u32 {
assert!(a.len() == b.len());
unsafe {
let mut res = 0u32;
let a_ptr = a.as_ptr() as *const u64;
let b_ptr = b.as_ptr() as *const u64;
let words_len = (a.len() >> 3) as isize;
for i in 0..words_len {
let mut r = (*a_ptr.offset(i)) ^ (*b_ptr.offset(i));
r |= r >> 4;
r &= 0x0f0f0f0f0f0f0f0fu64;
r |= r >> 2;
r &= 0x3333333333333333u64;
r |= r >> 1;
r &= 0x5555555555555555u64;
res += r.count_ones();
}
let words_rem = a.len() & 7;
if words_rem > 0 {
let mut r = (*a_ptr.offset(words_len)) ^ (*b_ptr.offset(words_len));
r |= r >> 4;
r &= 0x0f0f0f0f0f0f0f0fu64;
r |= r >> 2;
r &= 0x3333333333333333u64;
r |= r >> 1;
r &= 0x5555555555555555u64;
res += (r & ((1u64 << ((words_rem as u64) << 3u64)) - 1u64)).count_ones();
}
res
}
}
pub fn hamming_words_128(a: &[u8], b: &[u8]) -> u32 {
assert!(a.len() == b.len());
unsafe {
let mut res = 0u32;
let a_ptr = a.as_ptr() as *const u128;
let b_ptr = b.as_ptr() as *const u128;
let words_len = (a.len() >> 4) as isize;
for i in 0..words_len {
let mut r = (*a_ptr.offset(i)) ^ (*b_ptr.offset(i));
r |= r >> 4;
r &= 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0fu128;
r |= r >> 2;
r &= 0x33333333333333333333333333333333u128;
r |= r >> 1;
r &= 0x55555555555555555555555555555555u128;
res += r.count_ones();
}
let words_rem = a.len() & 15;
if words_rem > 0 {
let mut r = (*a_ptr.offset(words_len)) ^ (*b_ptr.offset(words_len));
r |= r >> 4;
r &= 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0fu128;
r |= r >> 2;
r &= 0x33333333333333333333333333333333u128;
r |= r >> 1;
r &= 0x55555555555555555555555555555555u128;
res += (r & ((1u128 << ((words_rem as u128) << 3u128)) - 1u128)).count_ones();
}
res
}
}
pub fn hamming_simd_parallel(a: &[u8], b: &[u8]) -> u32 {
assert!(a.len() == b.len());
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if cfg!(feature = "jewel-avx") && is_x86_feature_detected!("avx2") {
return unsafe {Avx::count_mismatches(a.as_ptr(), b.as_ptr(), a.len())};
}else if cfg!(feature = "jewel-sse") && is_x86_feature_detected!("sse4.1") {
return unsafe {Sse::count_mismatches(a.as_ptr(), b.as_ptr(), a.len())};
}
}
hamming_naive(a, b)
}
pub fn hamming_simd_movemask(a: &[u8], b: &[u8]) -> u32 {
assert!(a.len() == b.len());
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if cfg!(feature = "jewel-avx") && is_x86_feature_detected!("avx2") {
return unsafe {Avx::mm_count_mismatches(a.as_ptr(), b.as_ptr(), a.len())};
}else if cfg!(feature = "jewel-sse") && is_x86_feature_detected!("sse4.1") {
return unsafe {Sse::mm_count_mismatches(a.as_ptr(), b.as_ptr(), a.len())};
}
}
hamming_naive(a, b)
}
pub fn hamming(a: &[u8], b: &[u8]) -> u32 {
hamming_simd_parallel(a, b)
}
pub fn hamming_search_simd<'a>(needle: &'a [u8], haystack: &'a [u8]) -> Box<dyn Iterator<Item = Match> + 'a> {
hamming_search_simd_with_opts(needle, haystack, ((needle.len() as u32) >> 1) + ((needle.len() as u32) & 1), SearchType::Best)
}
pub fn hamming_search_simd_with_opts<'a>(needle: &'a [u8], haystack: &'a [u8], k: u32, search_type: SearchType) -> Box<dyn Iterator<Item = Match> + 'a> {
if needle.len() > haystack.len() {
return Box::new(iter::empty());
}
if needle.len() == 0 {
return Box::new(iter::empty());
}
check_no_null_bytes(haystack);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if cfg!(feature = "jewel-avx") && is_x86_feature_detected!("avx2") {
return unsafe {hamming_search_simd_core_avx(needle, haystack, k, search_type)};
}else if cfg!(feature = "jewel-sse") && is_x86_feature_detected!("sse4.1") {
return unsafe {hamming_search_simd_core_sse(needle, haystack, k, search_type)};
}
}
hamming_search_naive_with_opts(needle, haystack, k, search_type)
}
macro_rules! create_hamming_search_simd_core {
($name:ident, $jewel:ty, $target:literal) => {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = $target)]
unsafe fn $name<'a>(needle: &'a [u8], haystack: &'a [u8], k: u32, search_type: SearchType) -> Box<dyn Iterator<Item = Match> + 'a> {
#[cfg(feature = "debug")]
{
println!("Debug: Hamming search Jewel vector type {} for target {}.", stringify!($jewel), stringify!($target));
}
let needle_len = needle.len();
let haystack_len = haystack.len();
let needle_vector = <$jewel>::loadu(needle.as_ptr(), needle_len);
let len = if needle_vector.upper_bound() > haystack_len {0} else {haystack_len + 1 - needle_vector.upper_bound()};
let real_len = haystack_len + 1 - needle_len;
let haystack_ptr = haystack.as_ptr();
let mut curr_k = k;
let mut i = 0;
let res = iter::from_fn(move || {
while i < len {
let final_res = <$jewel>::vector_count_mismatches(&needle_vector, haystack_ptr.offset(i as isize), needle_len);
i += 1;
if final_res <= curr_k {
match search_type {
SearchType::Best => curr_k = final_res,
_ => ()
}
return Some((Match{start: i - 1, end: i + needle_len - 1, k: final_res}, curr_k));
}
}
'outer: while i < real_len {
let mut final_res = 0u32;
for j in 0..needle_len {
final_res += (*needle.get_unchecked(j) != *haystack.get_unchecked(i + j)) as u32;
if final_res > curr_k {
i += 1;
continue 'outer;
}
}
match search_type {
SearchType::Best => curr_k = final_res,
_ => ()
}
i += 1;
return Some((Match{start: i - 1, end: i + needle_len - 1, k: final_res}, curr_k));
}
None
});
if search_type == SearchType::Best {
let mut res_vec = Vec::with_capacity(haystack_len / needle_len);
res.for_each(|m| {
res_vec.push(m.0);
curr_k = m.1;
});
return Box::new(res_vec.into_iter().filter(move |m| m.k == curr_k));
}
Box::new(res.map(|m| m.0))
}
};
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
create_hamming_search_simd_core!(hamming_search_simd_core_avx, Avx, "avx2");
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
create_hamming_search_simd_core!(hamming_search_simd_core_sse, Sse, "sse4.1");
pub fn hamming_search<'a>(needle: &'a [u8], haystack: &'a [u8]) -> Box<dyn Iterator<Item = Match> + 'a> {
hamming_search_simd(needle, haystack)
}