Skip to content

Commit

Permalink
Clean-up Project GUID generation code (#261)
Browse files Browse the repository at this point in the history
  • Loading branch information
Corniel authored Mar 20, 2024
1 parent 0f4b708 commit 3f1f7f2
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 113 deletions.
2 changes: 1 addition & 1 deletion src/Buildalyzer/AnalyzerResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal AnalyzerResult(string projectFilePath, AnalyzerManager manager, Project
if (string.IsNullOrEmpty(projectGuid) || !Guid.TryParse(projectGuid, out _projectGuid))
{
_projectGuid = analyzer == null
? GuidUtility.Create(GuidUtility.UrlNamespace, ProjectFilePath)
? Buildalyzer.ProjectGuid.Create(ProjectFilePath)
: analyzer.ProjectGuid;
}
}
Expand Down
111 changes: 0 additions & 111 deletions src/Buildalyzer/GuidUtility.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/Buildalyzer/ProjectAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal ProjectAnalyzer(AnalyzerManager manager, string projectFilePath, Projec

// Get (or create) a project GUID
ProjectGuid = projectInSolution == null
? GuidUtility.Create(GuidUtility.UrlNamespace, ProjectFile.Name)
? Buildalyzer.ProjectGuid.Create(ProjectFile.Name)
: Guid.Parse(projectInSolution.ProjectGuid);

// Set the solution directory global property
Expand Down
57 changes: 57 additions & 0 deletions src/Buildalyzer/ProjectGuid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#nullable enable

using System.Security.Cryptography;

namespace Buildalyzer;

/// <summary>Generates Project <see cref="Guid"/>'s.</summary>
/// <remarks>
/// See: https://datatracker.ietf.org/doc/html/rfc4122.
/// </remarks>
internal static class ProjectGuid
{
/// <summary>The namespace for URL's.</summary>
private static readonly Guid UrlNamespace = Guid.Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8");

/// <summary>Generates a <see cref="Guid"/> based on the <see cref="SHA1"/> hash of the name.</summary>
public static Guid Create(string? name)
{
byte[] guid = Hash(Encoding.UTF8.GetBytes(name ?? string.Empty));

// set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8)
guid[6] = (byte)((guid[6] & 0x0F) | 0x50);

// set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10)
guid[8] = (byte)((guid[8] & 0x3F) | 0x80);

// convert the resulting UUID to local byte order (step 13)
SwapBytes(guid);
return new Guid(guid);
}

/// <summary>Generates a <see cref="SHA1"/> hash.</summary>
[Pure]
private static byte[] Hash(byte[] bytes)
{
// convert the namespace UUID to network order (step 3)
var ns = UrlNamespace.ToByteArray();

Check warning on line 37 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)

Check warning on line 37 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)

Check warning on line 37 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (macos-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)
SwapBytes(ns);

// compute the hash of the name space ID concatenated with the name (step 4)
using var sha1 = SHA1.Create();

Check warning on line 41 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (windows-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)

Check warning on line 41 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)

Check warning on line 41 in src/Buildalyzer/ProjectGuid.cs

View workflow job for this annotation

GitHub Actions / Build (macos-latest)

([deprecated] Use RCS1264 instead) Use explicit type instead of 'var' (https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)
sha1.TransformBlock(ns, 0, ns.Length, null, 0);
sha1.TransformFinalBlock(bytes, 0, bytes.Length);
return sha1.Hash![..16];
}

/// <summary>Converts a GUID (expressed as a byte array) to/from network order (MSB-first).</summary>
private static void SwapBytes(byte[] bytes)
{
Swap(bytes, 0, 3);
Swap(bytes, 1, 2);
Swap(bytes, 4, 5);
Swap(bytes, 6, 7);

static void Swap(byte[] bs, int l, int r) => (bs[r], bs[l]) = (bs[l], bs[r]);
}
}
16 changes: 16 additions & 0 deletions tests/Buildalyzer.Tests/Project_GUID_specs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#nullable enable

using Buildalyzer;
using FluentAssertions;

namespace Project_GUID_specs;

public class Creates
{
[TestCase(null, /*....*/ "1b4db7eb-4057-5ddf-91e0-36dec72071f5")]
[TestCase("", /*......*/ "1b4db7eb-4057-5ddf-91e0-36dec72071f5")]
[TestCase("ABCDEFGHIJK", "2a738916-9f0a-5c81-a8fa-cc64ba606458")]
[TestCase("Buildalyzer", "74397281-1b33-5316-aad1-c7ef52552d75")]
public void SHA1_based_GUID(string? name, Guid projectId)
=> ProjectGuid.Create(name).Should().Be(projectId);
}

0 comments on commit 3f1f7f2

Please sign in to comment.