use std::hash::{BuildHasher, Hash, Hasher};
use crate::intern::FloatInterner;
pub(crate) struct InterningApproxHasher<S: BuildHasher, I> {
hasher: S::Hasher,
intern: I,
failed: bool,
}
impl<S: BuildHasher, I> InterningApproxHasher<S, I> {
pub fn new(hash_builder: &S, intern: I) -> Self {
Self {
hasher: hash_builder.build_hasher(),
intern,
failed: false,
}
}
}
impl<S: BuildHasher, I> Hasher for InterningApproxHasher<S, I> {
fn finish(&self) -> u64 {
unreachable!("call try_finish() instead");
}
fn write(&mut self, bytes: &[u8]) {
if !self.failed {
self.hasher.write(bytes);
}
}
}
pub trait ApproxHasher: Hasher {
type Error;
fn write_f64(&mut self, x: f64);
fn write_f32(&mut self, x: f32) {
self.write_f64(x as f64);
}
fn try_finish(&self) -> Result<u64, Self::Error>;
}
impl<S: BuildHasher> ApproxHasher for InterningApproxHasher<S, &'_ mut FloatInterner> {
type Error = std::convert::Infallible;
fn write_f64(&mut self, x: f64) {
let (_, bucket) = self.intern.insert(x);
bucket.hash(&mut self.hasher);
}
fn try_finish(&self) -> Result<u64, Self::Error> {
Ok(self.hasher.finish())
}
}
impl<S: BuildHasher> ApproxHasher for InterningApproxHasher<S, &'_ FloatInterner> {
type Error = ();
fn write_f64(&mut self, x: f64) {
match self.intern.get(x) {
Some((_, bucket)) => bucket.hash(&mut self.hasher),
None => self.failed = true,
}
}
fn try_finish(&self) -> Result<u64, Self::Error> {
if self.failed {
Err(())
} else {
Ok(self.hasher.finish())
}
}
}
pub trait ApproxHash {
fn approx_hash<H: ApproxHasher>(&self, state: &mut H);
}
impl ApproxHash for f64 {
fn approx_hash<H: ApproxHasher>(&self, state: &mut H) {
state.write_f64(*self);
}
}
impl ApproxHash for f32 {
fn approx_hash<H: ApproxHasher>(&self, state: &mut H) {
state.write_f32(*self);
}
}
impl<T: ApproxHash> ApproxHash for [T] {
fn approx_hash<H: ApproxHasher>(&self, state: &mut H) {
self.len().hash(state);
for x in self {
x.approx_hash(state);
}
}
}
impl<T: ApproxHash, const N: usize> ApproxHash for [T; N] {
fn approx_hash<H: ApproxHasher>(&self, state: &mut H) {
for x in self {
x.approx_hash(state);
}
}
}