use crate::buckets::constrained::{FuzzyHashBucketMapper, FuzzyHashBucketsInfo};
use crate::generate::bucket_aggregation;
use crate::hash::body::{BODY_SIZE_LONG, BODY_SIZE_NORMAL, BODY_SIZE_SHORT};
use crate::pearson::{tlsh_b_mapping_256, tlsh_b_mapping_48};
pub const NUM_BUCKETS_SHORT: usize = 48;
pub const NUM_BUCKETS_NORMAL: usize = 128;
pub const NUM_BUCKETS_LONG: usize = 256;
static_assertions::const_assert_eq!(NUM_BUCKETS_SHORT % 4, 0);
static_assertions::const_assert_eq!(NUM_BUCKETS_NORMAL % 4, 0);
static_assertions::const_assert_eq!(NUM_BUCKETS_LONG % 4, 0);
pub(crate) mod constrained {
use super::*;
mod private {
pub trait Sealed {}
}
pub trait FuzzyHashBucketMapper: private::Sealed {
type RawBucketType;
type RawBodyType;
const MIN_NONZERO_BUCKETS: usize;
fn b_mapping(b0: u8, b1: u8, b2: u8, b3: u8) -> u8;
const IS_B_MAPPING_CONSTRAINED_WITHIN_BUCKETS: bool;
fn aggregate_buckets(
out: &mut Self::RawBodyType,
buckets: &Self::RawBucketType,
q1: u32,
q2: u32,
q3: u32,
);
}
pub struct FuzzyHashBucketsInfo<const SIZE_BUCKETS: usize>;
impl private::Sealed for FuzzyHashBucketsInfo<NUM_BUCKETS_SHORT> {}
impl FuzzyHashBucketMapper for FuzzyHashBucketsInfo<NUM_BUCKETS_SHORT> {
type RawBucketType = [u32; NUM_BUCKETS_SHORT];
type RawBodyType = [u8; BODY_SIZE_SHORT];
const MIN_NONZERO_BUCKETS: usize = 18;
#[inline(always)]
fn b_mapping(b0: u8, b1: u8, b2: u8, b3: u8) -> u8 {
tlsh_b_mapping_48(b0, b1, b2, b3)
}
const IS_B_MAPPING_CONSTRAINED_WITHIN_BUCKETS: bool = false;
#[inline(always)]
fn aggregate_buckets(
out: &mut Self::RawBodyType,
buckets: &Self::RawBucketType,
q1: u32,
q2: u32,
q3: u32,
) {
bucket_aggregation::aggregate_48(out, buckets, q1, q2, q3);
}
}
impl private::Sealed for FuzzyHashBucketsInfo<NUM_BUCKETS_NORMAL> {}
impl FuzzyHashBucketMapper for FuzzyHashBucketsInfo<NUM_BUCKETS_NORMAL> {
type RawBucketType = [u32; NUM_BUCKETS_NORMAL];
type RawBodyType = [u8; BODY_SIZE_NORMAL];
const MIN_NONZERO_BUCKETS: usize = NUM_BUCKETS_NORMAL / 2 + 1;
#[inline(always)]
fn b_mapping(b0: u8, b1: u8, b2: u8, b3: u8) -> u8 {
tlsh_b_mapping_256(b0, b1, b2, b3)
}
const IS_B_MAPPING_CONSTRAINED_WITHIN_BUCKETS: bool = false;
#[inline(always)]
fn aggregate_buckets(
out: &mut Self::RawBodyType,
buckets: &Self::RawBucketType,
q1: u32,
q2: u32,
q3: u32,
) {
bucket_aggregation::aggregate_128(out, buckets, q1, q2, q3);
}
}
impl private::Sealed for FuzzyHashBucketsInfo<NUM_BUCKETS_LONG> {}
impl FuzzyHashBucketMapper for FuzzyHashBucketsInfo<NUM_BUCKETS_LONG> {
type RawBucketType = [u32; NUM_BUCKETS_LONG];
type RawBodyType = [u8; BODY_SIZE_LONG];
const MIN_NONZERO_BUCKETS: usize = NUM_BUCKETS_LONG / 2 + 1;
#[inline(always)]
fn b_mapping(b0: u8, b1: u8, b2: u8, b3: u8) -> u8 {
tlsh_b_mapping_256(b0, b1, b2, b3)
}
const IS_B_MAPPING_CONSTRAINED_WITHIN_BUCKETS: bool = true;
#[inline(always)]
fn aggregate_buckets(
out: &mut Self::RawBodyType,
buckets: &Self::RawBucketType,
q1: u32,
q2: u32,
q3: u32,
) {
bucket_aggregation::aggregate_256(out, buckets, q1, q2, q3);
}
}
pub trait LongFuzzyHashBucketMapper: FuzzyHashBucketMapper {}
impl LongFuzzyHashBucketMapper for FuzzyHashBucketsInfo<NUM_BUCKETS_NORMAL> {}
impl LongFuzzyHashBucketMapper for FuzzyHashBucketsInfo<NUM_BUCKETS_LONG> {}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(align(16))]
pub(crate) struct FuzzyHashBucketsData<const SIZE_BUCKETS: usize>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
#[cfg(not(feature = "opt-low-memory-buckets"))]
pub(crate) buckets: [u32; 256],
#[cfg(feature = "opt-low-memory-buckets")]
pub(crate) buckets: [u32; SIZE_BUCKETS],
}
impl<const SIZE_BUCKETS: usize> FuzzyHashBucketsData<SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
pub(crate) fn new() -> Self {
cfg_if::cfg_if! {
if #[cfg(not(feature = "opt-low-memory-buckets"))] {
Self { buckets: [0; 256] }
} else {
Self { buckets: [0; SIZE_BUCKETS] }
}
}
}
#[inline(always)]
pub(crate) fn data(&self) -> &[u32] {
&self.buckets[..SIZE_BUCKETS]
}
#[inline(always)]
pub(crate) fn increment(&mut self, index: u8) {
let index = index as usize;
#[cfg(feature = "opt-low-memory-buckets")]
if !FuzzyHashBucketsInfo::<SIZE_BUCKETS>::IS_B_MAPPING_CONSTRAINED_WITHIN_BUCKETS
&& index >= SIZE_BUCKETS
{
return;
}
self.buckets[index] = self.buckets[index].wrapping_add(1);
}
}