use core::num::Wrapping;
static_assertions::const_assert_eq!(super::BODY_OUTLIER_VALUE, 6);
#[inline(always)]
pub(crate) fn sub_distance(x: u32, y: u32) -> u32 {
let x = Wrapping(x);
let y = Wrapping(y);
let mask_dibit_01 = Wrapping(0x5555_5555u32);
let mask_dibit_10 = Wrapping(0xaaaa_aaaau32);
let mask_nibble_0011 = Wrapping(0x3333_3333u32);
let mask_byte_00001111 = Wrapping(0x0f0f_0f0fu32);
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(0x01010101)) >> 24).0 }
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(4)
.zip(body2.as_slice().chunks_exact(4))
{
let x = u32::from_ne_bytes(x.try_into().unwrap());
let y = u32::from_ne_bytes(y.try_into().unwrap());
total += sub_distance(x, y);
}
total
}
)*
}
}
distance_func_template! {
distance_12 = 12;
distance_32 = 32;
distance_64 = 64;
}