diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index c263ccdbe1..abb3aa0612 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -76,6 +76,39 @@ public static class CoreHelpers return new Guid(guidArray); } + internal static DateTime DateFromComb(Guid combGuid) + { + var guidArray = combGuid.ToByteArray(); + var daysArray = new byte[4]; + var msecsArray = new byte[4]; + + Array.Copy(guidArray, guidArray.Length - 6, daysArray, 2, 2); + Array.Copy(guidArray, guidArray.Length - 4, msecsArray, 0, 4); + + Array.Reverse(daysArray); + Array.Reverse(msecsArray); + + var days = BitConverter.ToInt32(daysArray, 0); + var msecs = BitConverter.ToInt32(msecsArray, 0); + + var time = new TimeSpan(days, 0, 0, 0, (int)(msecs * 3.333333)); + return new DateTime(_baseDateTicks + time.Ticks, DateTimeKind.Utc); + } + + internal static double BinForComb(Guid combGuid, int binCount) + { + // From System.Web.Util.HashCodeCombiner + uint CombineHashCodes(uint h1, byte h2) + { + return (uint)(((h1 << 5) + h1) ^ h2); + } + var guidArray = combGuid.ToByteArray(); + var randomArray = new byte[10]; + Array.Copy(guidArray, 0, randomArray, 0, 10); + var hash = randomArray.Aggregate((uint)randomArray.Length, CombineHashCodes); + return hash % binCount; + } + public static string CleanCertificateThumbprint(string thumbprint) { // Clean possible garbage characters from thumbprint copy/paste diff --git a/test/Core.Test/Utilities/CoreHelpersTests.cs b/test/Core.Test/Utilities/CoreHelpersTests.cs index af11567989..ac02234e34 100644 --- a/test/Core.Test/Utilities/CoreHelpersTests.cs +++ b/test/Core.Test/Utilities/CoreHelpersTests.cs @@ -71,6 +71,31 @@ public class CoreHelpersTests Assert.Equal(expectedComb, comb); } + [Theory] + [MemberData(nameof(GenerateCombCases))] + public void DateFromComb_WithComb_Success(Guid inputGuid, DateTime inputTime) + { + var comb = CoreHelpers.GenerateComb(inputGuid, inputTime); + var inverseComb = CoreHelpers.DateFromComb(comb); + + Assert.Equal(inputTime, inverseComb, TimeSpan.FromMilliseconds(4)); + } + + [Theory] + [InlineData("00000000-0000-0000-0000-000000000000", 1, 0)] + [InlineData("00000000-0000-0000-0000-000000000001", 1, 0)] + [InlineData("00000000-0000-0000-0000-000000000000", 500, 430)] + [InlineData("00000000-0000-0000-0000-000000000001", 500, 430)] + [InlineData("10000000-0000-0000-0000-000000000001", 500, 454)] + [InlineData("00000000-0000-0100-0000-000000000001", 500, 19)] + public void BinForComb_Success(string guidString, int nbins, int expectedBin) + { + var guid = Guid.Parse(guidString); + var bin = CoreHelpers.BinForComb(guid, nbins); + + Assert.Equal(expectedBin, bin); + } + /* [Fact] public void ToGuidIdArrayTVP_Success()