#![warn(missing_docs)]
mod blockhash;
mod compare;
mod constants;
pub mod error;
mod hasher;
mod roll;
pub use constants::Modes;
use hasher::Hasher;
use std::{
ffi::{CStr, CString},
fmt,
os::raw::c_char,
path::Path,
};
pub type Result<T> = std::result::Result<T, error::Error>;
pub struct FuzzyHash {
hasher: Hasher,
hash: Option<String>,
}
impl Default for FuzzyHash {
fn default() -> Self {
Self {
hasher: Hasher::new(),
hash: None,
}
}
}
impl FuzzyHash {
pub fn new<S: AsRef<[u8]>>(input: S) -> Self {
let input = input.as_ref();
let mut this = Self::default();
this.hasher.update(input, input.len());
this.finalize();
this
}
pub fn file<P: AsRef<Path>>(path: P) -> std::result::Result<Self, std::io::Error> {
let mut file = std::fs::File::open(path.as_ref())?;
FuzzyHash::read(&mut file)
}
pub fn read<R: std::io::Read>(reader: &mut R) -> std::result::Result<Self, std::io::Error> {
let mut hasher = Hasher::new();
loop {
let mut buffer = [0; 1024];
let len = reader.read(&mut buffer)?;
hasher.update(&buffer, len);
if len < 1024 {
break;
}
}
let mut this = Self { hasher, hash: None };
this.finalize();
Ok(this)
}
pub fn update<S: AsRef<[u8]>>(&mut self, input: S) {
let input = input.as_ref();
self.hasher.update(input, input.len());
}
pub fn finalize(&mut self) {
if self.hash.is_none() {
self.hash = self.hasher.digest(constants::Modes::None).ok();
}
}
pub fn compare<S: AsRef<str>, T: AsRef<str>>(first: S, second: T) -> Result<u32> {
compare::compare(first, second)
}
pub fn compare_to(&self, other: &FuzzyHash) -> Option<u32> {
self.hash
.as_ref()
.and_then(|ref hash| FuzzyHash::compare(hash, &other.to_string()).ok())
}
}
impl fmt::Display for FuzzyHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.hash.as_ref().unwrap_or(&String::new()))
}
}
impl From<&str> for FuzzyHash {
fn from(s: &str) -> Self {
Self {
hasher: Hasher::new(),
hash: Some(s.to_string()),
}
}
}
impl From<String> for FuzzyHash {
fn from(s: String) -> Self {
Self {
hasher: Hasher::new(),
hash: Some(s),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn fuzzyhash(buf: *const u8, length: usize) -> *mut c_char {
let data = std::slice::from_raw_parts(buf, length);
let mut fuzzy_hash = FuzzyHash::new(data);
fuzzy_hash.finalize();
let s = CString::new(fuzzy_hash.to_string()).unwrap();
s.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn fuzzyhash_compare(first: *const c_char, second: *const c_char) -> u32 {
let f = FuzzyHash::new(CStr::from_ptr(first).to_string_lossy().into_owned());
let s = FuzzyHash::new(CStr::from_ptr(second).to_string_lossy().into_owned());
f.compare_to(&s).unwrap_or(0)
}