ssdeep/internals/generate/hashes/
partial_fnv.rs1use core::ops::AddAssign;
10
11use crate::internals::hash::block::block_hash;
12
13#[cfg(not(feature = "opt-reduce-fnv-table"))]
14use crate::internals::macros::invariant;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct PartialFNVHash(u8);
27
28impl PartialFNVHash {
29 const OLD_HASH_INIT: u32 = 0x28021967;
34
35 const FNV_HASH_INIT: u8 = (Self::OLD_HASH_INIT % block_hash::ALPHABET_SIZE as u32) as u8;
39
40 const FNV_HASH_PRIME: u32 = 0x01000193;
44
45 #[cfg(not(feature = "opt-reduce-fnv-table"))]
51 const FNV_TABLE: [[u8; block_hash::ALPHABET_SIZE]; block_hash::ALPHABET_SIZE] = {
52 let mut array = [[0u8; block_hash::ALPHABET_SIZE]; block_hash::ALPHABET_SIZE];
53 let mut state = 0u8;
54 while state < 64 {
55 let mut ch = 0u8;
56 while ch < 64 {
57 array[state as usize][ch as usize] =
58 (((state as u32).wrapping_mul(Self::FNV_HASH_PRIME) as u8) ^ ch)
59 % block_hash::ALPHABET_SIZE as u8;
60 ch += 1;
61 }
62 state += 1;
63 }
64 array
65 };
66
67 #[inline]
69 pub fn new() -> Self {
70 PartialFNVHash(Self::FNV_HASH_INIT)
71 }
72
73 #[inline]
75 pub fn update_by_byte(&mut self, ch: u8) -> &mut Self {
76 cfg_if::cfg_if! {
77 if #[cfg(not(feature = "opt-reduce-fnv-table"))] {
78 invariant!((self.value() as usize) < block_hash::ALPHABET_SIZE);
79 self.0 = Self::FNV_TABLE
80 [self.value() as usize] [ch as usize % block_hash::ALPHABET_SIZE]; } else {
83 self.0 = ((self.0 as u32).wrapping_mul(Self::FNV_HASH_PRIME) ^ (ch as u32)) as u8;
84 }
85 }
86 self
87 }
88
89 pub fn update_by_iter(&mut self, iter: impl Iterator<Item = u8>) -> &mut Self {
91 for ch in iter {
92 self.update_by_byte(ch);
93 }
94 self
95 }
96
97 pub fn update(&mut self, buf: &[u8]) -> &mut Self {
99 for &ch in buf.iter() {
100 self.update_by_byte(ch);
101 }
102 self
103 }
104
105 #[inline]
110 pub fn value(&self) -> u8 {
111 cfg_if::cfg_if! {
112 if #[cfg(not(feature = "opt-reduce-fnv-table"))] {
113 invariant!(self.0 < (block_hash::ALPHABET_SIZE as u8));
114 self.0
115 } else {
116 self.0 & (block_hash::ALPHABET_SIZE as u8).wrapping_sub(1)
117 }
118 }
119 }
120}
121
122impl Default for PartialFNVHash {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl AddAssign<&[u8]> for PartialFNVHash {
129 #[inline(always)]
131 fn add_assign(&mut self, buffer: &[u8]) {
132 self.update(buffer);
133 }
134}
135
136impl<const N: usize> AddAssign<&[u8; N]> for PartialFNVHash {
137 #[inline(always)]
139 fn add_assign(&mut self, buffer: &[u8; N]) {
140 self.update(&buffer[..]);
141 }
142}
143
144impl AddAssign<u8> for PartialFNVHash {
145 #[inline(always)]
147 fn add_assign(&mut self, byte: u8) {
148 self.update_by_byte(byte);
149 }
150}
151
152#[doc(hidden)]
154mod const_asserts {
155 use static_assertions::{const_assert, const_assert_eq};
156
157 use super::*;
158
159 const_assert_eq!(PartialFNVHash::FNV_HASH_INIT, 0x27);
162
163 const_assert!(0 < block_hash::ALPHABET_SIZE && block_hash::ALPHABET_SIZE <= 256);
165 const_assert!(block_hash::ALPHABET_SIZE.is_power_of_two());
166 const_assert!((PartialFNVHash::FNV_HASH_INIT as u16) < (block_hash::ALPHABET_SIZE as u16));
167}
168
169pub(crate) mod test_utils;
170mod tests;