I have been implementing various hash functions that are absent in .NET framework. I decided to converge them into a library. My primary goals are to provide well-tested and performant implementations. The library currently supports SipHash, MurmurHash3, xxHash and FNV-1a.
To install it on NuGet:
Install-Package HashDepot
I try to add anything that's not in C# runtime and quite popular. For instance, there are multiple xxHash implementations for C# but they differentiate in terms of API complexity and performance. Although I didn't try out SIMD optimizations, the existing code is quite fast.
This one claims to be one of the fastest hash functions and it's actually amazing. Even without any SIMD optimizations, it outperforms everything, even a plain checksum by a factor of two. The implementation assumes little endian machines. Example usage:
var buffer = Encoding.UTF8.GetBytes("some string");
uint result = XXHash.Hash32(buffer, seed: 123);
ulong result = XXHash.Hash64(buffer); // default seed is zero
SipHash is resistant to hash-flood attacks against hashtables and uses a key parameter to ensure HMAC-like authenticity yet faster. Unfortuantely a native .NET implementation does not exist. It is my take on it, and it is really fast for a managed environment. It's standard SipHash-2-4 implementation with 64-bit. To use it:
var buffer = Encoding.UTF8.GetBytes("some string");
var key = new byte[16] { .. your random key here .. };
ulong result = SipHash24.Hash64(buffer, key);
If you have a larger buffer than 2GB it's better to use streaming functions instead.
MurmurHash3 provides a good balance between performance and homogenity but is essentially prone to hash-flood attacks (trivial to force collisions). HashDepot implements its x86 flavor (not x64). An example use is:
var buffer = Encoding.UTF8.GetBytes("some string");
uint seed = // .. preferred seed value ...
uint result = MurmurHash3.Hash32(buffer, seed);
A straightforward implementation of FNV-1a hash algorithm for .NET. Usage is very simple. For instance to calculate 32-bit FNV-1a hash of ASCII string "some string":
var buffer = Encoding.UTF8.GetBytes("some string");
uint result = Fnv1a.Hash32(buffer); // 32-bit hash
ulong result = Fnv1a.Hash64(buffer); // 64-bit hash
All hashes also provide stream-based (albeit slow) functions with their async variants too. In order to get the hash of a stream just call the function with a stream instead of a memory buffer:
ulong result = XXHash.Hash64(stream);
If you'd like to run it asynchronously, use the async variant:
uint result = await MurmurHash3.Hash32Async(stream);
Benchmarks are performed on a 1MB buffer.
BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22621.819) Snapdragon Compute Platform, 1 CPU, 8 logical and 8 physical cores .NET SDK=7.0.100 [Host] : .NET 6.0.11 (6.0.1122.52304), Arm64 RyuJIT AdvSIMD DefaultJob : .NET 6.0.11 (6.0.1122.52304), Arm64 RyuJIT AdvSIMD
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
Checksum_32 | 234.9 us | 4.61 us | 7.17 us | 231.4 us |
XXHash_32 | 248.3 us | 2.13 us | 1.66 us | 248.3 us |
XXHash_64 | 231.1 us | 4.40 us | 4.89 us | 231.4 us |
MurmurHash3_x86 | 498.9 us | 6.19 us | 5.79 us | 498.5 us |
SipHash24_32 | 497.4 us | 4.30 us | 3.35 us | 498.1 us |
Fnv1a_32 | 1,112.1 us | 21.93 us | 25.25 us | 1,108.1 us |
Fnv1a_64 | 1,064.2 us | 10.09 us | 8.95 us | 1,065.1 us |
You're more than welcome to contribute fixes or new hash algorithms. Please keep these in mind:
- Make sure the code builds without warnings.
- Include unit tests for the fixed bug, or the new feature.
- If you're proposing a new hash algorithm, please make sure that it's not in C# runtime, there isn't an existing library that is already tremendously popular, and HashDepot's simplistic approach would provide a benefit over the alternatives.
MIT License. See LICENSE file for details