use core::ops::AddAssign;
use crate::internals::hash::block::block_hash;
#[cfg(not(feature = "opt-reduce-fnv-table"))]
use crate::internals::macros::invariant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PartialFNVHash(u8);
impl PartialFNVHash {
const OLD_HASH_INIT: u32 = 0x28021967;
const FNV_HASH_INIT: u8 = (Self::OLD_HASH_INIT % block_hash::ALPHABET_SIZE as u32) as u8;
const FNV_HASH_PRIME: u32 = 0x01000193;
#[cfg(not(feature = "opt-reduce-fnv-table"))]
const FNV_TABLE: [[u8; block_hash::ALPHABET_SIZE]; block_hash::ALPHABET_SIZE] = {
let mut array = [[0u8; block_hash::ALPHABET_SIZE]; block_hash::ALPHABET_SIZE];
let mut state = 0u8;
while state < 64 {
let mut ch = 0u8;
while ch < 64 {
array[state as usize][ch as usize] =
(((state as u32).wrapping_mul(Self::FNV_HASH_PRIME) as u8) ^ ch)
% block_hash::ALPHABET_SIZE as u8;
ch += 1;
}
state += 1;
}
array
};
#[inline]
pub fn new() -> Self {
PartialFNVHash(Self::FNV_HASH_INIT)
}
#[inline]
pub fn update_by_byte(&mut self, ch: u8) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(not(feature = "opt-reduce-fnv-table"))] {
invariant!((self.value() as usize) < block_hash::ALPHABET_SIZE);
self.0 = Self::FNV_TABLE
[self.value() as usize] [ch as usize % block_hash::ALPHABET_SIZE]; } else {
self.0 = ((self.0 as u32).wrapping_mul(Self::FNV_HASH_PRIME) ^ (ch as u32)) as u8;
}
}
self
}
pub fn update_by_iter(&mut self, iter: impl Iterator<Item = u8>) -> &mut Self {
for ch in iter {
self.update_by_byte(ch);
}
self
}
pub fn update(&mut self, buf: &[u8]) -> &mut Self {
for &ch in buf.iter() {
self.update_by_byte(ch);
}
self
}
#[inline]
pub fn value(&self) -> u8 {
cfg_if::cfg_if! {
if #[cfg(not(feature = "opt-reduce-fnv-table"))] {
invariant!(self.0 < (block_hash::ALPHABET_SIZE as u8));
self.0
} else {
self.0 & (block_hash::ALPHABET_SIZE as u8).wrapping_sub(1)
}
}
}
}
impl Default for PartialFNVHash {
fn default() -> Self {
Self::new()
}
}
impl AddAssign<&[u8]> for PartialFNVHash {
#[inline(always)]
fn add_assign(&mut self, buffer: &[u8]) {
self.update(buffer);
}
}
impl<const N: usize> AddAssign<&[u8; N]> for PartialFNVHash {
#[inline(always)]
fn add_assign(&mut self, buffer: &[u8; N]) {
self.update(&buffer[..]);
}
}
impl AddAssign<u8> for PartialFNVHash {
#[inline(always)]
fn add_assign(&mut self, byte: u8) {
self.update_by_byte(byte);
}
}
#[doc(hidden)]
mod const_asserts {
use static_assertions::{const_assert, const_assert_eq};
use super::*;
const_assert_eq!(PartialFNVHash::FNV_HASH_INIT, 0x27);
const_assert!(0 < block_hash::ALPHABET_SIZE && block_hash::ALPHABET_SIZE <= 256);
const_assert!(block_hash::ALPHABET_SIZE.is_power_of_two());
const_assert!((PartialFNVHash::FNV_HASH_INIT as u16) < (block_hash::ALPHABET_SIZE as u16));
}
pub(crate) mod test_utils;
mod tests;