use core::hash::BuildHasher;
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;
#[inline(always)]
fn update_32(mut hash: u32, input: &[u8]) -> u32 {
for &byte in input {
hash = (hash ^ u32::from(byte)).wrapping_mul(FNV1A_32_PRIME);
}
hash
}
#[inline(always)]
fn update_64(mut hash: u64, input: &[u8]) -> u64 {
for &byte in input {
hash = (hash ^ u64::from(byte)).wrapping_mul(FNV1A_64_PRIME);
}
hash
}
#[must_use]
#[inline]
pub fn fnv1a_32(input: &[u8]) -> u32 {
fnv1a_32_with_offset_basis(input, FNV1A_32_OFFSET_BASIS)
}
#[must_use]
#[inline]
pub fn fnv1a_32_with_offset_basis(input: &[u8], offset_basis: u32) -> u32 {
update_32(offset_basis, input)
}
#[must_use]
#[inline]
pub fn fnv1a_64(input: &[u8]) -> u64 {
fnv1a_64_with_offset_basis(input, FNV1A_64_OFFSET_BASIS)
}
#[must_use]
#[inline]
pub fn fnv1a_64_with_offset_basis(input: &[u8], offset_basis: u64) -> u64 {
update_64(offset_basis, input)
}
#[derive(Clone, Copy, Debug)]
pub struct Fnv1a32 {
hash: u32,
offset_basis: u32,
}
impl Fnv1a32 {
#[must_use]
pub const fn new() -> Self {
Self::with_offset_basis(FNV1A_32_OFFSET_BASIS)
}
#[must_use]
pub const fn with_offset_basis(offset_basis: u32) -> Self {
Self {
hash: offset_basis,
offset_basis,
}
}
#[inline]
pub fn update(&mut self, input: &[u8]) {
self.hash = update_32(self.hash, input);
}
#[must_use]
pub const fn digest(&self) -> u32 {
self.hash
}
#[must_use]
pub const fn offset_basis(&self) -> u32 {
self.offset_basis
}
pub fn reset(&mut self) {
self.hash = self.offset_basis;
}
}
impl Default for Fnv1a32 {
fn default() -> Self {
Self::new()
}
}
impl Hasher for Fnv1a32 {
#[inline]
fn finish(&self) -> u64 {
u64::from(self.digest())
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.update(bytes);
}
}
#[derive(Clone, Copy, Debug)]
pub struct Fnv1a32Builder {
offset_basis: u32,
}
impl Fnv1a32Builder {
#[must_use]
pub const fn with_offset_basis(offset_basis: u32) -> Self {
Self { offset_basis }
}
}
impl Default for Fnv1a32Builder {
fn default() -> Self {
Self::with_offset_basis(FNV1A_32_OFFSET_BASIS)
}
}
impl BuildHasher for Fnv1a32Builder {
type Hasher = Fnv1a32;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
Fnv1a32::with_offset_basis(self.offset_basis)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Fnv1a64 {
hash: u64,
offset_basis: u64,
}
impl Fnv1a64 {
#[must_use]
pub const fn new() -> Self {
Self::with_offset_basis(FNV1A_64_OFFSET_BASIS)
}
#[must_use]
pub const fn with_offset_basis(offset_basis: u64) -> Self {
Self {
hash: offset_basis,
offset_basis,
}
}
#[inline]
pub fn update(&mut self, input: &[u8]) {
self.hash = update_64(self.hash, input);
}
#[must_use]
pub const fn digest(&self) -> u64 {
self.hash
}
#[must_use]
pub const fn offset_basis(&self) -> u64 {
self.offset_basis
}
pub fn reset(&mut self) {
self.hash = self.offset_basis;
}
}
impl Default for Fnv1a64 {
fn default() -> Self {
Self::new()
}
}
impl Hasher for Fnv1a64 {
#[inline]
fn finish(&self) -> u64 {
self.digest()
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.update(bytes);
}
}
#[derive(Clone, Copy, Debug)]
pub struct Fnv1a64Builder {
offset_basis: u64,
}
impl Fnv1a64Builder {
#[must_use]
pub const fn with_offset_basis(offset_basis: u64) -> Self {
Self { offset_basis }
}
}
impl Default for Fnv1a64Builder {
fn default() -> Self {
Self::with_offset_basis(FNV1A_64_OFFSET_BASIS)
}
}
impl BuildHasher for Fnv1a64Builder {
type Hasher = Fnv1a64;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
Fnv1a64::with_offset_basis(self.offset_basis)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn specification_vectors_match() {
let vectors = [
(b"".as_slice(), 0x811c_9dc5, 0xcbf2_9ce4_8422_2325),
(b"a".as_slice(), 0xe40c_292c, 0xaf63_dc4c_8601_ec8c),
(b"foobar".as_slice(), 0xbf9c_f968, 0x8594_4171_f739_67e8),
(
b"Hello!\x01\xff\xed".as_slice(),
0xfd9d_3881,
0xbd51_ea70_94ee_6fa1,
),
];
for (input, expected32, expected64) in vectors {
assert_eq!(fnv1a_32(input), expected32);
assert_eq!(fnv1a_64(input), expected64);
}
}
#[test]
fn reset_restores_offset_basis() {
let mut hash = Fnv1a64::new();
hash.update(b"before");
hash.reset();
hash.update(b"after");
assert_eq!(hash.digest(), fnv1a_64(b"after"));
}
}