#![cfg_attr(feature = "nightly", feature(i128_type))]
#![deny(missing_docs)]
#[cfg(feature = "extprim")]
extern crate extprim;
#[cfg(feature = "extprim_literals")]
#[macro_use]
extern crate extprim_literals;
pub trait FnvHasher {
type Hash;
fn finish(&self) -> Self::Hash;
fn write(&mut self, bytes: &[u8]);
}
#[derive(Debug, Default)]
pub struct Fnv0<T> {
hash: T,
}
#[derive(Debug)]
pub struct Fnv1<T> {
hash: T,
}
#[derive(Debug)]
pub struct Fnv1a<T> {
hash: T,
}
impl<T: Default> Fnv0<T> {
pub fn new() -> Self {
Self::default()
}
}
impl<T> Fnv0<T> {
pub fn with_key(key: T) -> Self {
Self { hash: key }
}
}
impl<T> Fnv1<T> {
pub fn with_key(key: T) -> Self {
Self { hash: key }
}
}
impl<T> Fnv1a<T> {
pub fn with_key(key: T) -> Self {
Self { hash: key }
}
}
macro_rules! fnv0_impl {
($type: ty, $prime: expr, $from_byte: ident) => {
impl FnvHasher for Fnv0<$type> {
type Hash = $type;
fn finish(&self) -> Self::Hash {
self.hash
}
fn write(&mut self, bytes: &[u8]) {
let mut hash = self.hash;
for byte in bytes {
hash = hash.wrapping_mul($prime);
hash ^= ($from_byte)(*byte);
}
self.hash = hash;
}
}
}
}
macro_rules! fnv1_impl {
($type: ty, $offset: expr, $prime: expr, $from_byte: ident) => {
impl Default for Fnv1<$type> {
fn default() -> Self {
Self {
hash: $offset
}
}
}
impl Fnv1<$type> {
pub fn new() -> Self {
Self::default()
}
}
impl FnvHasher for Fnv1<$type> {
type Hash = $type;
fn finish(&self) -> Self::Hash {
self.hash
}
fn write(&mut self, bytes: &[u8]) {
let mut hash = self.hash;
for byte in bytes {
hash = hash.wrapping_mul($prime);
hash ^= ($from_byte)(*byte);
}
self.hash = hash;
}
}
}
}
macro_rules! fnv1a_impl {
($type: ty, $offset: expr, $prime: expr, $from_byte: ident) => {
impl Default for Fnv1a<$type> {
fn default() -> Self {
Self {
hash: $offset
}
}
}
impl Fnv1a<$type> {
pub fn new() -> Self {
Self::default()
}
}
impl FnvHasher for Fnv1a<$type> {
type Hash = $type;
fn finish(&self) -> Self::Hash {
self.hash
}
fn write(&mut self, bytes: &[u8]) {
let mut hash = self.hash;
for byte in bytes {
hash ^= ($from_byte)(*byte);
hash = hash.wrapping_mul($prime);
}
self.hash = hash;
}
}
}
}
macro_rules! fnv_hasher_impl {
($type: ty) => {
impl ::std::hash::Hasher for $type {
fn finish(&self) -> u64 {
::FnvHasher::finish(self)
}
fn write(&mut self, bytes: &[u8]) {
::FnvHasher::write(self, bytes);
}
}
}
}
macro_rules! fnv_impl {
(u64, $offset: expr, $prime: expr, $from_byte: ident) => {
fnv0_impl!(u64, $prime, $from_byte);
fnv_hasher_impl!(Fnv0<u64>);
fnv1_impl!(u64, $offset, $prime, $from_byte);
fnv_hasher_impl!(Fnv1<u64>);
fnv1a_impl!(u64, $offset, $prime, $from_byte);
fnv_hasher_impl!(Fnv1a<u64>);
};
($type: ty, $offset: expr, $prime: expr, $from_byte: ident) => {
fnv0_impl!($type, $prime, $from_byte);
fnv1_impl!($type, $offset, $prime, $from_byte);
fnv1a_impl!($type, $offset, $prime, $from_byte);
};
}
fn u32_from_byte(byte: u8) -> u32 {
byte.into()
}
fn u64_from_byte(byte: u8) -> u64 {
byte.into()
}
fnv_impl!(u32, 0x811c_9dc5, 0x100_0193, u32_from_byte);
fnv_impl!(u64, 0xcbf2_9ce4_8422_2325, 0x100_0000_01B3, u64_from_byte);
#[cfg(feature = "u128")]
fn extprim_u128_from_byte(byte: u8) -> extprim::u128::u128 {
extprim::u128::u128::new(u64::from(byte))
}
#[cfg(feature = "u128")]
fnv_impl!(
extprim::u128::u128,
u128!(0x6C62272E07BB014262B821756295C58D),
u128!(0x0000000001000000000000000000013B),
extprim_u128_from_byte
);
#[cfg(feature = "nightly")]
fn core_u128_from_byte(byte: u8) -> u128 {
byte.into()
}
#[cfg(feature = "nightly")]
fnv_impl!(
u128,
0x6C62272E07BB014262B821756295C58Du128,
0x0000000001000000000000000000013Bu128,
core_u128_from_byte
);
#[cfg(test)]
mod tests {
use {Fnv0, Fnv1, Fnv1a, FnvHasher};
use std::iter;
macro_rules! fnv0_tests {
($($name: ident: $size: ty, $input: expr, $expected_hash: expr,)*) => {
$(
#[test]
fn $name() {
let mut fnv0 = Fnv0::<$size>::new();
fnv0.write($input);
let result = fnv0.finish();
assert_eq!(result, $expected_hash);
}
)*
};
}
macro_rules! fnv1_tests {
($($name: ident: $size: ty, $input: expr, $expected_hash: expr,)*) => {
$(
#[test]
fn $name() {
let mut fnv1 = Fnv1::<$size>::new();
fnv1.write($input);
let result = fnv1.finish();
assert_eq!(result, $expected_hash);
}
)*
};
}
macro_rules! fnv1a_tests {
($($name: ident: $size: ty, $input: expr, $expected_hash: expr,)*) => {
$(
#[test]
fn $name() {
let mut fnv1a = Fnv1a::<$size>::new();
fnv1a.write($input);
let result = fnv1a.finish();
assert_eq!(result, $expected_hash);
}
)*
};
}
fn repeat(slice: &[u8], times: usize) -> Vec<u8> {
iter::repeat(slice)
.take(times)
.flat_map(|x| x)
.cloned()
.collect()
}
include!("fnv_test_cases.rs");
#[cfg(feature = "u128")]
fnv0_tests!{
fnv0_offset_calculation_extprim_128_bit: ::extprim::u128::u128, b"chongo <Landon Curt Noll> /\\../\\", u128!(0x6C62272E07BB014262B821756295C58D),
}
#[cfg(feature = "nightly")]
fnv0_tests!{
fnv0_offset_calculation_128_bit: u128, b"chongo <Landon Curt Noll> /\\../\\", 0x6C62272E07BB014262B821756295C58D,
}
}