use crate::buckets::constrained::{
FuzzyHashBucketMapper, FuzzyHashBucketsInfo, LongFuzzyHashBucketMapper,
};
use crate::buckets::{NUM_BUCKETS_LONG, NUM_BUCKETS_NORMAL, NUM_BUCKETS_SHORT};
use crate::compare::dist_checksum::{distance_1, distance_3};
use crate::errors::ParseError;
use crate::parse::hex_str::decode_rev_array;
use crate::pearson::tlsh_b_mapping_256;
pub const CHECKSUM_SIZE_NORMAL: usize = 1;
pub const CHECKSUM_SIZE_LONG: usize = 3;
pub(crate) mod private {
pub trait Sealed {}
}
pub(crate) mod inner {
pub trait InnerChecksum: super::private::Sealed {
fn update(&mut self, curr: u8, prev: u8);
}
pub trait OneByteChecksumChecker: super::private::Sealed {
#[allow(unused_variables)]
fn is_valid(checksum: u8) -> bool {
true
}
}
}
struct OneByteChecksumChecker<const SIZE_BUCKETS: usize>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper;
impl private::Sealed for OneByteChecksumChecker<NUM_BUCKETS_SHORT> {}
impl inner::OneByteChecksumChecker for OneByteChecksumChecker<NUM_BUCKETS_SHORT> {
fn is_valid(checksum: u8) -> bool {
checksum <= NUM_BUCKETS_SHORT as u8
}
}
impl private::Sealed for OneByteChecksumChecker<NUM_BUCKETS_NORMAL> {}
impl inner::OneByteChecksumChecker for OneByteChecksumChecker<NUM_BUCKETS_NORMAL> {}
impl private::Sealed for OneByteChecksumChecker<NUM_BUCKETS_LONG> {}
impl inner::OneByteChecksumChecker for OneByteChecksumChecker<NUM_BUCKETS_LONG> {}
pub trait FuzzyHashChecksum: inner::InnerChecksum {
const SIZE: usize;
const MAX_DISTANCE: u32;
fn is_valid(&self) -> bool;
fn compare(&self, other: &Self) -> u32;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct FuzzyHashChecksumData<const SIZE_CKSUM: usize, const SIZE_BUCKETS: usize>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
data: [u8; SIZE_CKSUM],
}
impl<const SIZE_CKSUM: usize, const SIZE_BUCKETS: usize>
FuzzyHashChecksumData<SIZE_CKSUM, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
pub(crate) fn new() -> Self {
Self {
data: [0; SIZE_CKSUM],
}
}
pub(crate) fn from_raw(data: &[u8; SIZE_CKSUM]) -> Self {
Self { data: *data }
}
#[inline]
pub(crate) fn from_str_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
if bytes.len() != SIZE_CKSUM * 2 {
return Err(ParseError::InvalidStringLength);
}
let mut data = [0u8; SIZE_CKSUM];
if decode_rev_array(&mut data, bytes) {
Ok(Self { data })
} else {
Err(ParseError::InvalidCharacter)
}
}
#[inline(always)]
pub fn data(&self) -> &[u8; SIZE_CKSUM] {
&self.data
}
}
impl<const SIZE_BUCKETS: usize> private::Sealed
for FuzzyHashChecksumData<CHECKSUM_SIZE_NORMAL, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
}
impl<const SIZE_BUCKETS: usize> inner::InnerChecksum
for FuzzyHashChecksumData<CHECKSUM_SIZE_NORMAL, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
{
#[inline(always)]
fn update(&mut self, curr: u8, prev: u8) {
self.data[0] = FuzzyHashBucketsInfo::<SIZE_BUCKETS>::b_mapping(0, curr, prev, self.data[0]);
}
}
impl<const SIZE_BUCKETS: usize> FuzzyHashChecksum
for FuzzyHashChecksumData<CHECKSUM_SIZE_NORMAL, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: FuzzyHashBucketMapper,
OneByteChecksumChecker<SIZE_BUCKETS>: inner::OneByteChecksumChecker,
{
const SIZE: usize = CHECKSUM_SIZE_NORMAL;
const MAX_DISTANCE: u32 = CHECKSUM_SIZE_NORMAL as u32;
fn is_valid(&self) -> bool {
use inner::OneByteChecksumChecker as _;
OneByteChecksumChecker::<SIZE_BUCKETS>::is_valid(self.data[0])
}
#[inline(always)]
fn compare(&self, other: &Self) -> u32 {
distance_1(self.data, other.data)
}
}
impl<const SIZE_BUCKETS: usize> private::Sealed
for FuzzyHashChecksumData<CHECKSUM_SIZE_LONG, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: LongFuzzyHashBucketMapper,
{
}
impl<const SIZE_BUCKETS: usize> inner::InnerChecksum
for FuzzyHashChecksumData<CHECKSUM_SIZE_LONG, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: LongFuzzyHashBucketMapper,
{
#[inline(always)]
fn update(&mut self, curr: u8, prev: u8) {
self.data[0] = FuzzyHashBucketsInfo::<SIZE_BUCKETS>::b_mapping(0, curr, prev, self.data[0]);
self.data[1] = tlsh_b_mapping_256(self.data[0], curr, prev, self.data[1]);
self.data[2] = tlsh_b_mapping_256(self.data[1], curr, prev, self.data[2]);
}
}
impl<const SIZE_BUCKETS: usize> FuzzyHashChecksum
for FuzzyHashChecksumData<CHECKSUM_SIZE_LONG, SIZE_BUCKETS>
where
FuzzyHashBucketsInfo<SIZE_BUCKETS>: LongFuzzyHashBucketMapper,
{
const SIZE: usize = CHECKSUM_SIZE_LONG;
const MAX_DISTANCE: u32 = CHECKSUM_SIZE_LONG as u32;
#[inline(always)]
fn is_valid(&self) -> bool {
true
}
#[inline(always)]
fn compare(&self, other: &Self) -> u32 {
distance_3(self.data, other.data)
}
}
mod tests;