use core::hash::Hasher;
pub const FNV1A_32_OFFSET_BASIS: u32 = 0x811c_9dc5;
pub const FNV1A_32_PRIME: u32 = 0x0100_0193;
pub const FNV1A_64_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
pub const FNV1A_64_PRIME: u64 = 0x0000_0100_0000_01b3;
#[must_use]
pub const fn fnv1a_32(bytes: &[u8]) -> u32 {
let mut hash = FNV1A_32_OFFSET_BASIS;
let mut at = 0;
while at < bytes.len() {
hash = (hash ^ bytes[at] as u32).wrapping_mul(FNV1A_32_PRIME);
at += 1;
}
hash
}
#[must_use]
pub const fn fnv1a_64(bytes: &[u8]) -> u64 {
fnv1a_64_from(FNV1A_64_OFFSET_BASIS, bytes)
}
const fn fnv1a_64_from(mut hash: u64, bytes: &[u8]) -> u64 {
let mut at = 0;
while at < bytes.len() {
hash = (hash ^ bytes[at] as u64).wrapping_mul(FNV1A_64_PRIME);
at += 1;
}
hash
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fnv1a64(u64);
impl Fnv1a64 {
#[must_use]
pub const fn new() -> Self {
Self(FNV1A_64_OFFSET_BASIS)
}
}
impl Default for Fnv1a64 {
fn default() -> Self {
Self::new()
}
}
impl Hasher for Fnv1a64 {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
self.0 = fnv1a_64_from(self.0, bytes);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_hashes_match_the_published_vectors() {
assert_eq!(fnv1a_32(b""), 0x811c_9dc5);
assert_eq!(fnv1a_32(b"a"), 0xe40c_292c);
assert_eq!(fnv1a_32(b"foobar"), 0xbf9c_f968);
assert_eq!(fnv1a_64(b""), 0xcbf2_9ce4_8422_2325);
assert_eq!(fnv1a_64(b"a"), 0xaf63_dc4c_8601_ec8c);
assert_eq!(fnv1a_64(b"foobar"), 0x8594_4171_f739_67e8);
}
#[test]
fn the_hasher_agrees_with_the_function_across_pieces() {
let mut hasher = Fnv1a64::new();
assert_eq!(hasher.finish(), fnv1a_64(b""));
hasher.write(b"foo");
hasher.write(b"");
hasher.write(b"bar");
assert_eq!(hasher.finish(), fnv1a_64(b"foobar"));
assert_eq!(Fnv1a64::default(), Fnv1a64::new());
}
}