FNV1a64.cs

Posted on July 30, 2020July 30, 2020Tags , ,   Leave a comment on FNV1a64.cs

Fowler–Noll–Vo hash function 64-Bit

using System;
using System.Security.Cryptography;
/// <summary>
///     Works very well for a 64 bit hashing algorithm.
///     No collisions with 10M random byte arrays.(64Bit version only) MJS 
///     https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
///     Very fast 88MBpS.
/// </summary>
[Serializable]
public class FNV1a64 : HashAlgorithm
{
    private const ulong K = 0x100000001B3;
    public        ulong _hash;
    public        ulong Hash;
    public        ulong Seed = 0xCBF29CE484222325;
    public FNV1a64()
    {
        _hash = Seed;
    }
    public FNV1a64(ulong seed)
    {
        Seed  = seed;
        _hash = Seed;
    }
    public override int HashSize => 64;
    public override void Initialize()
    {
        _hash = Seed;
    }
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        Hash64(bytes, ibStart, cbSize);
    }
    private unsafe void Hash64(byte[] bytes, int ibStart, int cbSize)
    {
        if (bytes == null)
            return;
        if (ibStart >= bytes.Length || cbSize > bytes.Length)
            return;
        fixed (byte* pb = bytes)
        {
            var nb = pb + ibStart;
            while (cbSize >= 8)
            {
                _hash  ^= *(ulong*) nb;
                nb     += 8;
                cbSize -= 8;
                _hash  *= K;
            }
            switch (cbSize & 7)
            {
                case 7:
                    _hash ^= *(ulong*) (nb + 6);
                    _hash *= K;
                    goto case 6;
                case 6:
                    _hash ^= *(ulong*) (nb + 5);
                    _hash *= K;
                    goto case 5;
                case 5:
                    _hash ^= *(ulong*) (nb + 4);
                    _hash *= K;
                    goto case 4;
                case 4:
                    _hash ^= *(ulong*) (nb + 3);
                    _hash *= K;
                    goto case 3;
                case 3:
                    _hash ^= *(ulong*) (nb + 2);
                    _hash *= K;
                    goto case 2;
                case 2:
                    _hash ^= *(ulong*) (nb + 1);
                    _hash *= K;
                    goto case 1;
                case 1:
                    _hash ^= *nb;
                    _hash *= K;
                    break;
            }
        }
    }
    /// <summary>
    ///     Exposed so that hash64 can be used externally.
    /// </summary>
    public unsafe ulong Hash64(byte[] bytes)
    {
        var cbSize = bytes.Length;
        _hash = Seed;
        fixed (byte* pb = bytes)
        {
            var nb = pb;
            while (cbSize >= 8)
            {
                _hash  ^= *(ulong*) nb;
                nb     += 8;
                cbSize -= 8;
                _hash  *= K;
            }
            switch (cbSize & 7)
            {
                case 7:
                    _hash ^= *(ulong*) (nb + 6);
                    _hash *= K;
                    goto case 6;
                case 6:
                    _hash ^= *(ulong*) (nb + 5);
                    _hash *= K;
                    goto case 5;
                case 5:
                    _hash ^= *(ulong*) (nb + 4);
                    _hash *= K;
                    goto case 4;
                case 4:
                    _hash ^= *(ulong*) (nb + 3);
                    _hash *= K;
                    goto case 3;
                case 3:
                    _hash ^= *(ulong*) (nb + 2);
                    _hash *= K;
                    goto case 2;
                case 2:
                    _hash ^= *(ulong*) (nb + 1);
                    _hash *= K;
                    goto case 1;
                case 1:
                    _hash ^= *nb;
                    _hash *= K;
                    break;
            }
        }
        return _hash;
    }
    protected override byte[] HashFinal()
    {
        Hash = _hash;
        return _hash.GetBytes();
    }
}