Skip to content

Commit

Permalink
Shorten UTD marker file (#9387)
Browse files Browse the repository at this point in the history
* Shorten UTD marker file

* Improve the StableStringHash intrinsic function and use it in the copy mareker filename

* Unify FNV implementations

* Apply PR suggestions

* Move FNV hashing to stringtools

* Fix leftovers

* Add comments
  • Loading branch information
JanKrivanek authored Nov 30, 2023
1 parent 5fcddc7 commit c4aec6d
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 37 deletions.
5 changes: 3 additions & 2 deletions src/Build/Evaluation/IntrinsicFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
using Microsoft.Build.Utilities;
using Microsoft.NET.StringTools;
using Microsoft.Win32;

// Needed for DoesTaskHostExistForParameters
Expand Down Expand Up @@ -398,11 +399,11 @@ internal static string ConvertFromBase64(string toDecode)
}

/// <summary>
/// Hash the string independent of bitness and target framework.
/// Hash the string independent of bitness, target framework and default codepage of the environment.
/// </summary>
internal static int StableStringHash(string toHash)
{
return CommunicationsUtilities.GetHashCode(toHash);
return FowlerNollVo1aHash.ComputeHash32(toHash);
}

/// <summary>
Expand Down
40 changes: 6 additions & 34 deletions src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Profiler;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
using Microsoft.NET.StringTools;

#nullable disable

Expand Down Expand Up @@ -1259,9 +1261,9 @@ private void Write(IExtendedBuildEventArgs extendedData)

internal readonly struct HashKey : IEquatable<HashKey>
{
private readonly ulong value;
private readonly long value;

private HashKey(ulong i)
private HashKey(long i)
{
value = i;
}
Expand All @@ -1274,13 +1276,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);
Expand Down Expand Up @@ -1310,35 +1312,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;
}
}
}
}
}
135 changes: 135 additions & 0 deletions src/StringTools/FowlerNollVo1aHash.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using System;

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;

#if NET35
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;
}
}
#else
ReadOnlySpan<byte> span = MemoryMarshal.Cast<char, byte>(text.AsSpan());
foreach (byte b in span)
{
hash = unchecked((hash ^ b) * fnvPrimeA32Bit);
}
#endif

return unchecked((int)hash);
}

/// <summary>
/// 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
/// </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;

#if NET35
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;
}
}
#else
ReadOnlySpan<byte> span = MemoryMarshal.Cast<char, byte>(text.AsSpan());
foreach (byte b in span)
{
hash = unchecked((hash ^ b) * fnvPrimeA64Bit);
}
#endif

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;
}
}
}
}
11 changes: 10 additions & 1 deletion src/Tasks/Microsoft.Common.CurrentVersion.targets
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,20 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<PropertyGroup>
<_GenerateBindingRedirectsIntermediateAppConfig>$(IntermediateOutputPath)$(TargetFileName).config</_GenerateBindingRedirectsIntermediateAppConfig>
</PropertyGroup>

<PropertyGroup Condition="'$(MSBuildCopyMarkerName)' == ''">
<MSBuildCopyMarkerName>$(MSBuildProjectFile)</MSBuildCopyMarkerName>
<!-- For a long MSBuildProjectFile let's shorten this to 17 chars - using the first 8 chars of the filename and either the ProjectGuid if it exists -->
<MSBuildCopyMarkerName Condition="'$(MSBuildCopyMarkerName.Length)' &gt; '17' and '$(ProjectGuid)' != ''">$(MSBuildProjectFile.Substring(0,8)).$(ProjectGuid.Substring(1,8))</MSBuildCopyMarkerName>
<!-- or a filename hash if the guid is not present (in such case the filename was not shortened and is still over 17 chars long). -->
<MSBuildCopyMarkerName Condition="'$(MSBuildCopyMarkerName.Length)' &gt; '17'">$(MSBuildProjectFile.Substring(0,8)).$([MSBuild]::StableStringHash($(MSBuildProjectFile)).ToString("X8"))</MSBuildCopyMarkerName>
<MSBuildCopyMarkerName>$(MSBuildCopyMarkerName).Up2Date</MSBuildCopyMarkerName>
</PropertyGroup>

<ItemGroup>
<IntermediateAssembly Include="$(IntermediateOutputPath)$(TargetName)$(TargetExt)"/>
<FinalDocFile Include="@(DocFileItem->'$(OutDir)%(Filename)%(Extension)')"/>
<CopyUpToDateMarker Include="$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', '$(MSBuildProjectFile).CopyComplete'))" />
<CopyUpToDateMarker Include="$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', '$(MSBuildCopyMarkerName)'))" />
</ItemGroup>

<ItemGroup Condition="'$(ProduceReferenceAssembly)' == 'true'">
Expand Down

0 comments on commit c4aec6d

Please sign in to comment.