use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
#[derive(Copy, Clone, Debug)]
pub struct JumpHasher<H = DefaultHasher> {
slots: Slots,
hasher: H,
}
impl<H: Hasher> JumpHasher<H> {
pub fn new_with_hasher<S: Into<Slots>>(slots: S, hasher: H) -> Self {
Self {
slots: slots.into(),
hasher,
}
}
}
impl<H: Hasher + Default> JumpHasher<H> {
pub fn new<S: Into<Slots>>(slots: S) -> Self {
Self::new_with_hasher(slots, H::default())
}
}
impl<H: Hasher> Hasher for JumpHasher<H> {
fn finish(&self) -> u64 {
hash(self.hasher.finish(), self.slots) as u64
}
fn write(&mut self, bytes: &[u8]) {
self.hasher.write(bytes)
}
}
#[derive(Copy, Clone, Debug)]
pub struct Slots(u32);
impl From<u32> for Slots {
fn from(value: u32) -> Self {
assert!(value > 0, "slots must be greater than 0");
Self(value)
}
}
pub fn hash<S: Into<Slots>>(mut key: u64, slots: S) -> u32 {
let slots = slots.into();
let (mut b, mut j) = (-1i64, 0i64);
while j < slots.0 as i64 {
b = j;
key = key.wrapping_mul(2862933555777941757).wrapping_add(1);
j = ((b.wrapping_add(1) as f64) * (((1u64 << 31) as f64) / (((key >> 33) + 1) as f64)))
as i64;
}
b as u32
}
#[cfg(test)]
mod tests {
use crate::JumpHasher;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[test]
fn test_struct() {
#[derive(Hash)]
struct Test(i32, String);
let test = Test(123456, "test".to_string());
check_range(&test);
}
#[test]
fn test_str() {
let test = "test 1";
check_range(&test);
}
#[test]
fn test_int() {
let test = 123456;
check_range(&test);
}
fn check_range<H: Hash>(test: &H) {
for slots in 1..1000 {
check_algorithm(slots, test);
}
}
fn check_algorithm<H: Hash>(slots: u32, test: H) {
let mut hasher: JumpHasher = JumpHasher::new(slots);
test.hash(&mut hasher);
let hash = hasher.finish();
for i in (hash.wrapping_add(1) as u32)..=slots {
let mut hasher = JumpHasher::<DefaultHasher>::new(i);
test.hash(&mut hasher);
assert_eq!(hasher.finish(), hash)
}
}
}