use core::hash::{BuildHasher, Hash, Hasher};
use crate::util::mix::rapid_mum;
use super::rapid_const::{rapidhash_core, rapidhash_finish, rapidhash_seed, RAPID_SECRET, RAPID_SEED};
#[derive(Copy, Clone)]
#[repr(C)]
pub struct RapidHasher<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool = false, const PROTECTED: bool = false> {
a: u64,
b: u64,
seed: u64,
secrets: &'static [u64; 7], }
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct RapidBuildHasher<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool = false, const PROTECTED: bool = false> {
seed: u64,
secrets: &'static [u64; 7],
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> RapidBuildHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
#[inline]
pub const fn new(mut seed: u64) -> Self {
seed = rapidhash_seed(seed);
Self { seed, secrets: &RAPID_SECRET }
}
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> BuildHasher for RapidBuildHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
type Hasher = RapidHasher<AVALANCHE, FNV, COMPACT, PROTECTED>;
#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
Self::Hasher::new_precomputed_seed(self.seed, self.secrets)
}
#[inline] fn hash_one<T: Hash>(&self, x: T) -> u64
where
Self: Sized,
Self::Hasher: Hasher,
{
let mut hasher = self.build_hasher();
x.hash(&mut hasher); hasher.finish()
}
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> Default for RapidBuildHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
#[inline]
fn default() -> Self {
Self::new(RapidHasher::<AVALANCHE, FNV, COMPACT, PROTECTED>::DEFAULT_SEED)
}
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> RapidHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
pub const DEFAULT_SEED: u64 = RAPID_SEED;
#[inline(always)]
#[must_use]
pub const fn new(mut seed: u64) -> Self {
seed = rapidhash_seed(seed);
Self::new_precomputed_seed(seed, &RAPID_SECRET)
}
#[inline(always)]
#[must_use]
pub(super) const fn new_precomputed_seed(seed: u64, secrets: &'static [u64; 7]) -> Self {
Self {
a: 0,
b: 0,
seed,
secrets,
}
}
#[inline(always)]
#[must_use]
pub const fn default_const() -> Self {
Self::new(Self::DEFAULT_SEED)
}
#[inline(always)]
#[must_use]
pub const fn write_const(&self, bytes: &[u8]) -> Self {
const _: () = assert!(
usize::MAX as u128 <= u64::MAX as u128,
"usize is wider than u64. Please raise a github issue to support this."
);
let mut this = *self;
let (a, b, seed) = rapidhash_core::<COMPACT, PROTECTED>(self.a, self.b, self.seed, self.secrets, bytes);
this.a = a;
this.b = b;
this.seed = seed;
this
}
#[inline(always)]
#[must_use]
const fn write_num<const N: u8>(&self, bytes: u64) -> Self {
let mut this = *self;
if FNV {
const FNV_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
if N <= 32 {
this.seed ^= bytes;
this.seed = this.seed.wrapping_mul(FNV_SEED);
this.seed = this.seed.rotate_left(31);
} else {
this.a ^= (bytes ^ this.seed).wrapping_mul(FNV_SEED).rotate_left(31);
this.seed ^= (bytes >> 32).wrapping_mul(FNV_SEED).rotate_left(31);
}
} else {
this.a ^= bytes ^ RAPID_SECRET[1];
this.b ^= bytes ^ this.seed;
let (a, b) = rapid_mum::<PROTECTED>(this.a, this.b);
this.a = a;
this.b = b;
}
this
}
#[inline(always)]
#[must_use]
const fn write_128(&self, bytes: u128) -> Self {
let mut this = *self;
this.a ^= bytes as u64 ^ RAPID_SECRET[1];
this.b ^= (bytes >> 64) as u64 ^ self.seed;
let (a, b) = rapid_mum::<PROTECTED>(this.a, this.b);
this.a = a;
this.b = b;
this
}
#[inline(always)]
#[must_use]
pub const fn finish_const(&self) -> u64 {
if AVALANCHE {
rapidhash_finish::<PROTECTED>(self.a, self.b, self.seed, self.secrets)
} else {
self.a ^ self.b ^ self.seed
}
}
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> Default for RapidHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
#[inline(always)]
fn default() -> Self {
Self::new(RAPID_SEED)
}
}
impl<const AVALANCHE: bool, const FNV: bool, const COMPACT: bool, const PROTECTED: bool> Hasher for RapidHasher<AVALANCHE, FNV, COMPACT, PROTECTED> {
#[inline(always)]
fn finish(&self) -> u64 {
self.finish_const()
}
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
*self = self.write_const(bytes);
}
#[inline(always)]
fn write_u8(&mut self, i: u8) {
*self = self.write_num::<8>(i.into());
}
#[inline(always)]
fn write_u16(&mut self, i: u16) {
*self = self.write_num::<16>(i.into());
}
#[inline(always)]
fn write_u32(&mut self, i: u32) {
*self = self.write_num::<32>(i.into());
}
#[inline(always)]
fn write_u64(&mut self, i: u64) {
*self = self.write_num::<64>(i);
}
#[inline(always)]
fn write_u128(&mut self, i: u128) {
#[cfg(target_pointer_width = "16")] {
*self = self.write_num::<64>(i as u16);
}
#[cfg(target_pointer_width = "32")] {
*self = self.write_num::<64>(i as u32);
}
#[cfg(target_pointer_width = "64")] {
*self = self.write_num::<64>(i as u64);
}
}
#[inline(always)]
fn write_usize(&mut self, i: usize) {
*self = self.write_num::<64>(i as u64);
}
#[inline(always)]
fn write_i8(&mut self, i: i8) {
*self = self.write_num::<8>(i as u64);
}
#[inline(always)]
fn write_i16(&mut self, i: i16) {
*self = self.write_num::<16>(i as u64);
}
#[inline(always)]
fn write_i32(&mut self, i: i32) {
*self = self.write_num::<32>(i as u64);
}
#[inline(always)]
fn write_i64(&mut self, i: i64) {
*self = self.write_num::<64>(i as u64);
}
#[inline(always)]
fn write_i128(&mut self, i: i128) {
*self = self.write_128(i as u128);
}
#[inline(always)]
fn write_isize(&mut self, i: isize) {
#[cfg(target_pointer_width = "16")] {
*self = self.write_num::<64>(i as u16);
}
#[cfg(target_pointer_width = "32")] {
*self = self.write_num::<64>(i as u32);
}
#[cfg(target_pointer_width = "64")] {
*self = self.write_num::<64>(i as u64);
}
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
#[ignore]
#[test]
fn test_hasher_write_u64() {
assert_eq!((8 & 24) >> (8 >> 3), 4);
let ints = [1234u64, 0, 1, u64::MAX, u64::MAX - 2385962040453523];
for int in ints {
let mut hasher = RapidHasher::<true, false>::default();
hasher.write(int.to_le_bytes().as_slice());
let a = hasher.finish();
assert_eq!(int.to_le_bytes().as_slice().len(), 8);
let mut hasher = RapidHasher::<true, false>::default();
hasher.write_u64(int);
let b = hasher.finish();
assert_eq!(a, b, "Mismatching hash for u64 with input {int}");
}
}
#[test]
#[ignore]
#[cfg(feature = "std")]
fn test_num_collisions() {
let builder = RapidBuildHasher::<true, false>::default();
let mut collisions = 0;
let mut set = std::collections::HashSet::new();
for i in 0..=u16::MAX {
let hash_u16 = builder.hash_one(i) & 0xFFFFFF;
if set.contains(&hash_u16) {
collisions += 1;
} else {
set.insert(hash_u16);
}
}
assert_eq!(collisions, 0, "Collisions found when hashing numbers");
}
}