diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index 3fff5c28e65..3b60f4f1a4c 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -398,11 +398,11 @@ internal static string ConvertFromBase64(string toDecode) } /// - /// Hash the string independent of bitness and target framework. + /// Hash the string independent of bitness, target framework and default codepage of the environment. /// internal static int StableStringHash(string toHash) { - return CommunicationsUtilities.GetHashCode(toHash); + return FowlerNollVo1aHash.ComputeHash32(toHash); } /// diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs index 0a21182e83c..ffc7d17dbc0 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs @@ -14,6 +14,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Framework.Profiler; using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; #nullable disable @@ -1274,13 +1275,13 @@ public HashKey(string text) } else { - value = FnvHash64.GetHashCode(text); + value = FowlerNollVo1aHash.ComputeHash64Fast(text); } } public static HashKey Combine(HashKey left, HashKey right) { - return new HashKey(FnvHash64.Combine(left.value, right.value)); + return new HashKey(FowlerNollVo1aHash.Combine64(left.value, right.value)); } public HashKey Add(HashKey other) => Combine(this, other); @@ -1310,35 +1311,5 @@ public override string ToString() return value.ToString(); } } - - internal static class FnvHash64 - { - public const ulong Offset = 14695981039346656037; - public const ulong Prime = 1099511628211; - - public static ulong GetHashCode(string text) - { - ulong hash = Offset; - - unchecked - { - for (int i = 0; i < text.Length; i++) - { - char ch = text[i]; - hash = (hash ^ ch) * Prime; - } - } - - return hash; - } - - public static ulong Combine(ulong left, ulong right) - { - unchecked - { - return (left ^ right) * Prime; - } - } - } } } diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index a42e76cc270..67490385761 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -153,6 +153,7 @@ + diff --git a/src/Build/Utilities/FowlerNollVo1aHash.cs b/src/Build/Utilities/FowlerNollVo1aHash.cs new file mode 100644 index 00000000000..a9b319e7cc0 --- /dev/null +++ b/src/Build/Utilities/FowlerNollVo1aHash.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Build.Utilities +{ + internal 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 ulong fnvPrimeA64Bit = 1099511628211; + private const ulong fnvOffsetBasisA64Bit = 14695981039346656037; + + /// + /// Computes 32 bit Fowler/Noll/Vo-1a hash of a string (regardless of encoding). + /// + /// String to be hashed. + /// 32 bit signed hash + internal 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); + } + + /// + /// Computes 64 bit Fowler/Noll/Vo-1a hash optimized for ASCII strings. + /// The hashing algorithm considers only the first 8 bits of each character. + /// Analysis: https://github.com/KirillOsenkov/MSBuildStructuredLog/wiki/String-Hashing#faster-fnv-1a + /// + /// String to be hashed. + /// 64 bit unsigned hash + internal static ulong ComputeHash64Fast(string text) + { + ulong hash = fnvOffsetBasisA64Bit; + + unchecked + { + for (int i = 0; i < text.Length; i++) + { + char ch = text[i]; + + hash = (hash ^ ch) * fnvPrimeA64Bit; + } + } + + return hash; + } + + /// + /// Computes 64 bit Fowler/Noll/Vo-1a hash of a string (regardless of encoding). + /// + /// String to be hashed. + /// 64 bit unsigned hash + internal static ulong ComputeHash64(string text) + { + ulong 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; + } + + internal static ulong Combine64(ulong left, ulong right) + { + unchecked + { + return (left ^ right) * fnvPrimeA64Bit; + } + } + } +} diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 316a44681b9..4c1a4b8f53b 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -385,11 +385,19 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_GenerateBindingRedirectsIntermediateAppConfig>$(IntermediateOutputPath)$(TargetFileName).config + + + $(MSBuildProjectFile) + + $(MSBuildProjectFile.Substring(0,8)).$(ProjectGuid.Substring(1,8)) + + $(MSBuildProjectFile.Substring(0,8)).$([MSBuild]::StableStringHash($(MSBuildProjectFile)).ToString("X8")) + - +