use crate::buckets::{NUM_BUCKETS_LONG, NUM_BUCKETS_NORMAL, NUM_BUCKETS_SHORT};
use crate::compare::dist_body::{
distance_12, distance_32, distance_64, MAX_DISTANCE_LONG, MAX_DISTANCE_NORMAL,
MAX_DISTANCE_SHORT,
};
use crate::errors::ParseError;
#[cfg(not(feature = "opt-simd-parse-hex"))]
use crate::parse::hex_str::decode_array;
pub const BODY_SIZE_SHORT: usize = NUM_BUCKETS_SHORT / 4;
pub const BODY_SIZE_NORMAL: usize = NUM_BUCKETS_NORMAL / 4;
pub const BODY_SIZE_LONG: usize = NUM_BUCKETS_LONG / 4;
mod private {
pub trait Sealed {}
}
pub trait FuzzyHashBody: private::Sealed {
const NUM_BUCKETS: usize;
const SIZE: usize;
const MAX_DISTANCE: u32;
fn quartile(&self, index: usize) -> u8;
fn compare(&self, other: &Self) -> u32;
}
#[repr(align(16))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FuzzyHashBodyData<const SIZE_BODY: usize> {
data: [u8; SIZE_BODY],
}
impl<const SIZE_BODY: usize> FuzzyHashBodyData<SIZE_BODY> {
pub(crate) fn from_raw(data: [u8; SIZE_BODY]) -> Self {
Self { data }
}
#[inline]
pub(crate) fn from_str_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
if bytes.len() != SIZE_BODY * 2 {
return Err(ParseError::InvalidStringLength);
}
let mut data = [0u8; SIZE_BODY];
cfg_if::cfg_if! {
if #[cfg(feature = "opt-simd-parse-hex")] {
let result =
hex_simd::decode(bytes, hex_simd::Out::from_slice(data.as_mut_slice())).is_ok();
} else {
let result = decode_array(&mut data, bytes);
}
}
if result {
Ok(Self { data })
} else {
Err(ParseError::InvalidCharacter)
}
}
#[inline(always)]
pub fn data(&self) -> &[u8; SIZE_BODY] {
&self.data
}
}
impl private::Sealed for FuzzyHashBodyData<BODY_SIZE_SHORT> {}
impl FuzzyHashBody for FuzzyHashBodyData<BODY_SIZE_SHORT> {
const NUM_BUCKETS: usize = NUM_BUCKETS_SHORT;
const SIZE: usize = BODY_SIZE_SHORT;
const MAX_DISTANCE: u32 = MAX_DISTANCE_SHORT;
#[inline(always)]
fn quartile(&self, index: usize) -> u8 {
assert!(index < Self::NUM_BUCKETS);
(self.data[self.data.len() - 1 - index / 4] >> (2 * (index % 4))) & 0b11
}
#[inline(always)]
fn compare(&self, other: &Self) -> u32 {
distance_12(&self.data, &other.data)
}
}
impl private::Sealed for FuzzyHashBodyData<BODY_SIZE_NORMAL> {}
impl FuzzyHashBody for FuzzyHashBodyData<BODY_SIZE_NORMAL> {
const NUM_BUCKETS: usize = NUM_BUCKETS_NORMAL;
const SIZE: usize = BODY_SIZE_NORMAL;
const MAX_DISTANCE: u32 = MAX_DISTANCE_NORMAL;
#[inline(always)]
fn quartile(&self, index: usize) -> u8 {
assert!(index < Self::NUM_BUCKETS);
(self.data[self.data.len() - 1 - index / 4] >> (2 * (index % 4))) & 0b11
}
#[inline(always)]
fn compare(&self, other: &Self) -> u32 {
distance_32(&self.data, &other.data)
}
}
impl private::Sealed for FuzzyHashBodyData<BODY_SIZE_LONG> {}
impl FuzzyHashBody for FuzzyHashBodyData<BODY_SIZE_LONG> {
const NUM_BUCKETS: usize = NUM_BUCKETS_LONG;
const SIZE: usize = BODY_SIZE_LONG;
const MAX_DISTANCE: u32 = MAX_DISTANCE_LONG;
#[inline(always)]
fn quartile(&self, index: usize) -> u8 {
assert!(index < Self::NUM_BUCKETS);
(self.data[self.data.len() - 1 - index / 4] >> (2 * (index % 4))) & 0b11
}
#[inline(always)]
fn compare(&self, other: &Self) -> u32 {
distance_64(&self.data, &other.data)
}
}
mod tests;