fast-tlsh 0.1.4

Library to generate / parse / compare TLSH locality sensitive hashes
Documentation
// SPDX-License-Identifier: Apache-2.0 OR MIT
// SPDX-FileCopyrightText: Copyright (C) 2024 Tsukasa OI <floss_ssdeep@irq.a4lg.com>.

//! The Q ratio pair part of the fuzzy hash.

use crate::compare::dist_qratios::{distance, MAX_DISTANCE};
use crate::errors::ParseError;
use crate::parse::hex_str::decode_rev_1;

/// Inner Q ratio pair struct.
#[bitfield_struct::bitfield(u8, order = Lsb)]
#[derive(PartialEq, Eq)]
struct InnerQRatios {
    /// The "Q1 ratio" value.
    #[bits(4)]
    q1ratio: u8,
    /// The "Q2 ratio" value.
    #[bits(4)]
    q2ratio: u8,
}

/// Q ratio pair encoded in a fuzzy hash.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct FuzzyHashQRatios {
    /// Inner Q ratio pair encoding.
    ///
    /// We wrap a struct generated by bitfield-struct for vendoring
    /// (constraining the access for our needs).
    qratios: InnerQRatios,
}

impl FuzzyHashQRatios {
    /// The maximum distance between two Q ratio pairs.
    pub const MAX_DISTANCE: u32 = MAX_DISTANCE;

    /// Creates the object from two Q ratio values.
    #[inline(always)]
    pub(crate) fn new(q1ratio: u8, q2ratio: u8) -> Self {
        Self {
            qratios: InnerQRatios::new()
                .with_q1ratio(q1ratio)
                .with_q2ratio(q2ratio),
        }
    }

    /// Creates the object from the raw encoding.
    #[inline(always)]
    pub(crate) fn from_raw(qratios: u8) -> Self {
        Self {
            qratios: InnerQRatios::from(qratios),
        }
    }

    /// Decode the object from a subset of
    /// the TLSH's hexadecimal representation.
    #[inline]
    pub(crate) fn from_str_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
        if bytes.len() != 2 {
            return Err(ParseError::InvalidStringLength);
        }
        decode_rev_1(bytes)
            .ok_or(ParseError::InvalidCharacter)
            .map(Self::from_raw)
    }

    /// Retrieves the Q ratio pair in the packed (raw) form.
    #[inline(always)]
    pub fn value(&self) -> u8 {
        self.qratios.into_bits()
    }

    /// Retrieves the "Q1 ratio" value.
    ///
    /// This is equivalent to the lowest 4-bits of the binary encoding
    /// and corresponds to the first character of the hexadecimal representation
    /// of a fuzzy hash.
    #[inline(always)]
    pub fn q1ratio(&self) -> u8 {
        self.qratios.q1ratio()
    }

    /// Retrieves the "Q2 ratio" value.
    ///
    /// This is equivalent to the highest 4-bits of the binary encoding
    /// and corresponds to the second character of the hexadecimal representation
    /// of a fuzzy hash.
    #[inline(always)]
    pub fn q2ratio(&self) -> u8 {
        self.qratios.q2ratio()
    }

    /// Compare against another Q ratio pair and
    /// return the distance between them.
    #[inline(always)]
    pub fn compare(&self, other: &FuzzyHashQRatios) -> u32 {
        distance(self.qratios.into_bits(), other.qratios.into_bits())
    }
}

mod tests;