File: FnvHash64Function.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.ProjectModel\NuGet.ProjectModel.csproj (NuGet.ProjectModel)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using NuGet.Packaging;

namespace NuGet.ProjectModel
{
    /// <summary>
    /// A Fowler-Noll-Vo (FNV) 64-bit hash function that supports non-cryptographic hashing to optimize speed and minimize collisions.
    /// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
    /// </summary>
    internal sealed class FnvHash64Function : IHashFunction
    {
        private ulong _hash;

        public void Update(byte[] data, int offset, int count)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (_hash == 0)
            {
                _hash = FnvHash64.Hash(data, count);
            }
            else
            {
                _hash = FnvHash64.Update(_hash, data, count);
            }
        }

        public byte[] GetHashBytes()
        {
            return BitConverter.GetBytes(_hash);
        }

        public string GetHash()
        {
            return Convert.ToBase64String(GetHashBytes());
        }

        // Interface is Disposable for SHA512 - this class has nothing to dispose.
        public void Dispose() { }

        internal static class FnvHash64
        {
            public const ulong Offset = 14695981039346656037;
            public const ulong Prime = 1099511628211;

            public static ulong Hash(byte[] data, int count = 0)
            {
                ulong hash = Offset;

                if (count == 0)
                {
                    count = data.Length;
                }

                unchecked
                {
                    for (int i = 0; i < count; i++)
                    {
                        var b = data[i];
                        hash = (hash ^ b) * Prime;
                    }
                }

                return hash;
            }

            public static ulong Combine(ulong left, ulong right)
            {
                unchecked
                {
                    return (left ^ right) * Prime;
                }
            }

            public static ulong Update(ulong current, byte[] data, int count = 0)
            {
                return Combine(current, Hash(data, count));
            }
        }
    }
}