File: FowlerNollVo1aHash.cs
Web Access
Project: ..\..\..\src\StringTools\StringTools.csproj (Microsoft.NET.StringTools)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
 
namespace Microsoft.NET.StringTools
{
    /// <summary>
    /// Fowler/Noll/Vo hashing.
    /// </summary>
    public static class FowlerNollVo1aHash
    {
        // Fowler/Noll/Vo hashing.
        // http://www.isthe.com/chongo/tech/comp/fnv/
        // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
        // http://www.isthe.com/chongo/src/fnv/hash_32a.c
 
        // 32 bit FNV prime and offset basis for FNV-1a.
        private const uint fnvPrimeA32Bit = 16777619;
        private const uint fnvOffsetBasisA32Bit = 2166136261;
 
        // 64 bit FNV prime and offset basis for FNV-1a.
        private const long fnvPrimeA64Bit = 1099511628211;
        private const long fnvOffsetBasisA64Bit = unchecked((long)14695981039346656037);
 
        /// <summary>
        /// Computes 32 bit Fowler/Noll/Vo-1a hash of a string (regardless of encoding).
        /// </summary>
        /// <param name="text">String to be hashed.</param>
        /// <returns>32 bit signed hash</returns>
        public static int ComputeHash32(string text)
        {
            uint hash = fnvOffsetBasisA32Bit;
 
            unchecked
            {
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
                    byte b = (byte)ch;
                    hash ^= b;
                    hash *= fnvPrimeA32Bit;
 
                    b = (byte)(ch >> 8);
                    hash ^= b;
                    hash *= fnvPrimeA32Bit;
                }
            }
 
            return unchecked((int)hash);
        }
 
        /// <summary>
        /// Computes 32 bit Fowler/Noll/Vo-1a inspired hash of a string.
        /// The hashing algorithm process the data by the whole 16bit chars, instead of by bytes.
        ///  this speeds up the hashing process almost by 2x, while not significantly increasing collisions rate.
        /// Analysis: https://github.com/KirillOsenkov/MSBuildStructuredLog/wiki/String-Hashing#faster-fnv-1a
        /// </summary>
        /// <param name="text">String to be hashed.</param>
        /// <returns>32 bit unsigned hash</returns>
        public static int ComputeHash32Fast(string text)
        {
            uint hash = fnvOffsetBasisA32Bit;
 
            unchecked
            {
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
 
                    hash = (hash ^ ch) * fnvPrimeA32Bit;
                }
            }
 
            return unchecked((int)hash);
        }
 
        /// <summary>
        /// Computes 64 bit Fowler/Noll/Vo-1a inspired hash of a string.
        /// The hashing algorithm process the data by the whole 16bit chars, instead of by bytes.
        ///  this speeds up the hashing process almost by 2x, while not significantly increasing collisions rate.
        /// Analysis: https://github.com/KirillOsenkov/MSBuildStructuredLog/wiki/String-Hashing#faster-fnv-1a
        /// </summary>
        /// <param name="text">String to be hashed.</param>
        /// <returns>64 bit unsigned hash</returns>
        public static long ComputeHash64Fast(string text)
        {
            long hash = fnvOffsetBasisA64Bit;
 
            unchecked
            {
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
 
                    hash = (hash ^ ch) * fnvPrimeA64Bit;
                }
            }
 
            return hash;
        }
 
        /// <summary>
        /// Computes 64 bit Fowler/Noll/Vo-1a hash of a string (regardless of encoding).
        /// </summary>
        /// <param name="text">String to be hashed.</param>
        /// <returns>64 bit unsigned hash</returns>
        public static long ComputeHash64(string text)
        {
            long hash = fnvOffsetBasisA64Bit;
 
            unchecked
            {
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
                    byte b = (byte)ch;
                    hash ^= b;
                    hash *= fnvPrimeA64Bit;
 
                    b = (byte)(ch >> 8);
                    hash ^= b;
                    hash *= fnvPrimeA64Bit;
                }
            }
 
            return hash;
        }
 
        /// <summary>
        /// Combines two 64 bit hashes generated by <see cref="FowlerNollVo1aHash"/> class into one.
        /// </summary>
        /// <param name="left">First hash value to be combined.</param>
        /// <param name="right">Second hash value to be combined.</param>
        /// <returns></returns>
        public static long Combine64(long left, long right)
        {
            unchecked
            {
                return (left ^ right) * fnvPrimeA64Bit;
            }
        }
    }
}