FNV-1a hash algorithm in C#
This small project is an implementation of the FNV-1a hash algorithm for 32-, 64-, 128-, 256-, 512- and 1024-bit variants. All implemented classes descend from the System.IO.Hashing's NonCryptographicHashAlgorithm, which should make for easy adoption.
Example:
namespace Fnv1aTest
{
using System;
using System.Globalization;
using System.IO.Hashing;
using System.Text;
using Fnv1a;
public static class Program
{
public static void Main()
{
NonCryptographicHashAlgorithm alg = new Fnv1a64();
alg.Append(Encoding.UTF8.GetBytes("foobar"));
Console.WriteLine(((ulong)BitConverter.ToInt64(alg.GetCurrentHash(), 0)).ToString("X8", CultureInfo.InvariantCulture));
}
}
}This will output 85944171F73967E8 as the FNV-1A 64-bit hash of the string "foobar".
BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.3 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.102
[Host] : .NET 10.0.2 (10.0.2, 10.0.225.61305), X64 RyuJIT x86-64-v3
DefaultJob : .NET 10.0.2 (10.0.2, 10.0.225.61305), X64 RyuJIT x86-64-v3
| Method | PayloadLength | Mean | Error | StdDev | Ratio | RatioSD |
|---|---|---|---|---|---|---|
| Fnv1A32Block | 32 | 51.40 ns | 0.129 ns | 0.114 ns | 1.00 | 0.00 |
| Fnv1A32SingleByte | 32 | 104.00 ns | 0.198 ns | 0.165 ns | 2.02 | 0.01 |
| Fnv1A64Block | 32 | 45.47 ns | 0.389 ns | 0.364 ns | 0.88 | 0.01 |
| Fnv1A64SingleByte | 32 | 102.70 ns | 0.247 ns | 0.231 ns | 2.00 | 0.01 |
| Fnv1A128Block | 32 | 232.16 ns | 0.185 ns | 0.154 ns | 4.52 | 0.01 |
| Fnv1A128SingleByte | 32 | 183.46 ns | 0.256 ns | 0.240 ns | 3.57 | 0.01 |
| Fnv1A256Block | 32 | 881.68 ns | 0.473 ns | 0.369 ns | 17.15 | 0.04 |
| Fnv1A256SingleByte | 32 | 925.48 ns | 0.646 ns | 0.540 ns | 18.00 | 0.04 |
| Fnv1A512Block | 32 | 2,199.22 ns | 2.361 ns | 1.843 ns | 42.78 | 0.10 |
| Fnv1A512SingleByte | 32 | 2,209.79 ns | 2.459 ns | 2.054 ns | 42.99 | 0.10 |
| Fnv1A1024Block | 32 | 22,662.06 ns | 25.452 ns | 22.562 ns | 440.87 | 1.04 |
| Fnv1A1024SingleByte | 32 | 22,361.64 ns | 8.785 ns | 7.336 ns | 435.02 | 0.94 |
| Fnv1A32Block | 1024 | 1,824.70 ns | 0.527 ns | 0.411 ns | 1.00 | 0.00 |
| Fnv1A32SingleByte | 1024 | 3,335.86 ns | 1.242 ns | 1.101 ns | 1.83 | 0.00 |
| Fnv1A64Block | 1024 | 1,551.64 ns | 2.794 ns | 2.477 ns | 0.85 | 0.00 |
| Fnv1A64SingleByte | 1024 | 3,280.27 ns | 5.973 ns | 5.587 ns | 1.80 | 0.00 |
| Fnv1A128Block | 1024 | 7,505.65 ns | 14.720 ns | 12.292 ns | 4.11 | 0.01 |
| Fnv1A128SingleByte | 1024 | 6,699.54 ns | 8.626 ns | 7.203 ns | 3.67 | 0.00 |
| Fnv1A256Block | 1024 | 27,622.35 ns | 4.264 ns | 3.329 ns | 15.14 | 0.00 |
| Fnv1A256SingleByte | 1024 | 28,971.42 ns | 5.264 ns | 4.395 ns | 15.88 | 0.00 |
| Fnv1A512Block | 1024 | 68,376.71 ns | 116.513 ns | 97.293 ns | 37.47 | 0.05 |
| Fnv1A512SingleByte | 1024 | 70,705.70 ns | 145.422 ns | 128.913 ns | 38.75 | 0.07 |
| Fnv1A1024Block | 1024 | 724,792.79 ns | 1,498.557 ns | 1,328.432 ns | 397.21 | 0.71 |
| Fnv1A1024SingleByte | 1024 | 715,605.56 ns | 339.826 ns | 301.247 ns | 392.18 | 0.18 |
| Fnv1A32Block | 65536 | 116,459.35 ns | 27.407 ns | 22.886 ns | 1.00 | 0.00 |
| Fnv1A32SingleByte | 65536 | 207,938.78 ns | 256.201 ns | 213.939 ns | 1.79 | 0.00 |
| Fnv1A64Block | 65536 | 99,176.16 ns | 17.502 ns | 15.515 ns | 0.85 | 0.00 |
| Fnv1A64SingleByte | 65536 | 209,497.21 ns | 171.765 ns | 143.432 ns | 1.80 | 0.00 |
| Fnv1A128Block | 65536 | 480,519.81 ns | 258.369 ns | 201.717 ns | 4.13 | 0.00 |
| Fnv1A128SingleByte | 65536 | 386,017.04 ns | 152.126 ns | 142.299 ns | 3.31 | 0.00 |
| Fnv1A256Block | 65536 | 1,766,838.49 ns | 325.950 ns | 272.183 ns | 15.17 | 0.00 |
| Fnv1A256SingleByte | 65536 | 1,879,842.11 ns | 262.729 ns | 232.902 ns | 16.14 | 0.00 |
| Fnv1A512Block | 65536 | 4,392,293.09 ns | 3,646.681 ns | 3,232.688 ns | 37.72 | 0.03 |
| Fnv1A512SingleByte | 65536 | 4,423,738.27 ns | 6,647.503 ns | 5,892.838 ns | 37.99 | 0.05 |
| Fnv1A1024Block | 65536 | 46,356,388.15 ns | 12,217.543 ns | 10,202.202 ns | 398.05 | 0.11 |
| Fnv1A1024SingleByte | 65536 | 45,820,265.71 ns | 124,585.155 ns | 104,034.247 ns | 393.44 | 0.86 |
Special thanks to crookseta for the missing-values project which allowed for the 256- and 512-bit variants to not have to use BigInteger, which was very slow.