Skip to content

Commit 2cce02a

Browse files
committed
Add FNV-1a hash algorithm implementation with 32-bit and 64-bit variants
1 parent e2a78d4 commit 2cce02a

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

hashes/fnv1a.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
FNV-1a (Fowler-Noll-Vo) is a non-cryptographic hash function created by
3+
Glenn Fowler, Landon Curt Noll, and Kiem-Phong Vo.
4+
5+
The FNV-1a variant provides better avalanche characteristics (bit changes
6+
distribute more uniformly) compared to the original FNV-1.
7+
8+
Key properties:
9+
- Fast computation
10+
- Good distribution
11+
- Non-cryptographic (not suitable for security purposes)
12+
- Widely used in hash tables and checksums
13+
14+
Algorithm:
15+
hash = FNV_offset_basis
16+
for each byte in data:
17+
hash = hash XOR byte
18+
hash = hash * FNV_prime
19+
20+
FNV-1a 32-bit:
21+
FNV_offset_basis = 2166136261 (0x811c9dc5)
22+
FNV_prime = 16777619 (0x01000193)
23+
24+
FNV-1a 64-bit:
25+
FNV_offset_basis = 14695981039346656037 (0xcbf29ce484222325)
26+
FNV_prime = 1099511628211 (0x100000001b3)
27+
28+
Source: http://www.isthe.com/chongo/tech/comp/fnv/
29+
"""
30+
31+
32+
def fnv1a_32(data: str) -> int:
33+
"""
34+
Implementation of 32-bit FNV-1a hash algorithm.
35+
36+
Args:
37+
data: Input string to hash
38+
39+
Returns:
40+
32-bit hash value as integer
41+
42+
Examples:
43+
>>> fnv1a_32('Hello')
44+
4116459851
45+
>>> fnv1a_32('World')
46+
3714116915
47+
>>> fnv1a_32('Algorithms')
48+
3235099003
49+
>>> fnv1a_32('')
50+
2166136261
51+
>>> fnv1a_32('The quick brown fox jumps over the lazy dog')
52+
76545936
53+
>>> fnv1a_32('a')
54+
3826002220
55+
>>> fnv1a_32('ab')
56+
1294271946
57+
>>> fnv1a_32('abc')
58+
440920331
59+
60+
Test with same input produces same output:
61+
>>> fnv1a_32('test') == fnv1a_32('test')
62+
True
63+
64+
Test different inputs produce different outputs:
65+
>>> fnv1a_32('test') != fnv1a_32('Test')
66+
True
67+
"""
68+
# FNV-1a 32-bit parameters
69+
fnv_offset_basis = 0x811C9DC5 # 2166136261
70+
fnv_prime = 0x01000193 # 16777619
71+
72+
hash_value = fnv_offset_basis
73+
74+
for byte in data.encode("utf-8"):
75+
hash_value ^= byte # XOR with byte
76+
hash_value *= fnv_prime # Multiply by FNV prime
77+
hash_value &= 0xFFFFFFFF # Keep it 32-bit
78+
79+
return hash_value
80+
81+
82+
def fnv1a_64(data: str) -> int:
83+
"""
84+
Implementation of 64-bit FNV-1a hash algorithm.
85+
86+
Args:
87+
data: Input string to hash
88+
89+
Returns:
90+
64-bit hash value as integer
91+
92+
Examples:
93+
>>> fnv1a_64('Hello')
94+
7201466553693376363
95+
>>> fnv1a_64('World')
96+
1088154518318865747
97+
>>> fnv1a_64('Algorithms')
98+
2924617064648692923
99+
>>> fnv1a_64('')
100+
14695981039346656037
101+
>>> fnv1a_64('The quick brown fox jumps over the lazy dog')
102+
17580284887202820368
103+
>>> fnv1a_64('a')
104+
12638187200555641996
105+
>>> fnv1a_64('ab')
106+
620445648566982762
107+
>>> fnv1a_64('abc')
108+
16654208175385433931
109+
110+
Test with same input produces same output:
111+
>>> fnv1a_64('test') == fnv1a_64('test')
112+
True
113+
114+
Test different inputs produce different outputs:
115+
>>> fnv1a_64('test') != fnv1a_64('Test')
116+
True
117+
"""
118+
# FNV-1a 64-bit parameters
119+
fnv_offset_basis = 0xCBF29CE484222325 # 14695981039346656037
120+
fnv_prime = 0x100000001B3 # 1099511628211
121+
122+
hash_value = fnv_offset_basis
123+
124+
for byte in data.encode("utf-8"):
125+
hash_value ^= byte # XOR with byte
126+
hash_value *= fnv_prime # Multiply by FNV prime
127+
hash_value &= 0xFFFFFFFFFFFFFFFF # Keep it 64-bit
128+
129+
return hash_value
130+
131+
132+
if __name__ == "__main__":
133+
import doctest
134+
135+
doctest.testmod()

0 commit comments

Comments
 (0)