use core::num::Wrapping;
static_assertions::const_assert_eq!(super::BODY_OUTLIER_VALUE, 6);
#[inline(always)]
fn sub_distance(x: u64, y: u64) -> u32 {
let x = Wrapping(x);
let y = Wrapping(y);
let mask_dibit_01 = Wrapping(0x5555_5555_5555_5555u64);
let mask_dibit_10 = Wrapping(0xaaaa_aaaa_aaaa_aaaau64);
let mask_nibble_0011 = Wrapping(0x3333_3333_3333_3333u64);
let mask_byte_00001111 = Wrapping(0x0f0f_0f0f_0f0f_0f0fu64);
let z = x ^ y;
let ta = y & mask_dibit_01;
let tb = x & mask_dibit_01;
let ta = (ta << 1) + ta; let tb = mask_dibit_10 - tb;
let ta = ta ^ x;
let tb = tb ^ x;
let sa = ta & z; let tb = tb & z;
let ta = sa >> 2;
let sa = sa & mask_nibble_0011;
let tb = tb >> 1;
let ta = ta & mask_nibble_0011;
let tb = (tb << 1) + tb; let sa = sa + ta; let sb = tb & z; let tb = sb >> 2;
let sb = sb & mask_nibble_0011;
let tb = tb & mask_nibble_0011;
let sb = sb + tb;
let s = sa + sb; let t = s >> 4;
let s = s & mask_byte_00001111;
let t = t & mask_byte_00001111;
let s = s + t; ((s * Wrapping(0x0101010101010101)) >> 56).0 as u32 }
#[inline]
pub fn distance_12(body1: &[u8; 12], body2: &[u8; 12]) -> u32 {
let x = u64::from_ne_bytes(body1[0..8].try_into().unwrap());
let y = u64::from_ne_bytes(body2[0..8].try_into().unwrap());
let mut total = sub_distance(x, y);
let x = u32::from_ne_bytes(body1[8..12].try_into().unwrap());
let y = u32::from_ne_bytes(body2[8..12].try_into().unwrap());
total += super::pseudo_simd_32::sub_distance(x, y);
total
}
macro_rules! distance_func_template {
{$($name:ident = $size:literal;)*} => {
$(
#[doc = concat!("Computes the distance between two ", stringify!($size), "-byte TLSH bodies.")]
#[inline]
pub fn $name(body1: &[u8; $size], body2: &[u8; $size]) -> u32 {
let mut total = 0;
for (x, y) in body1
.as_slice()
.chunks_exact(8)
.zip(body2.as_slice().chunks_exact(8))
{
let x = u64::from_ne_bytes(x.try_into().unwrap());
let y = u64::from_ne_bytes(y.try_into().unwrap());
total += sub_distance(x, y);
}
total
}
)*
}
}
distance_func_template! {
distance_32 = 32;
distance_64 = 64;
}