diff --git a/Core.Tests/GuidV8Tests/NewGuid.cs b/Core.Tests/GuidV8Tests/NewGuid.cs index fbff084..fd4a5c7 100644 --- a/Core.Tests/GuidV8Tests/NewGuid.cs +++ b/Core.Tests/GuidV8Tests/NewGuid.cs @@ -4,13 +4,87 @@ public class NewGuid { [Theory] [InlineData(GuidV8Entropy.Weak, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 250000000)] [InlineData(GuidV8Entropy.Strong, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 250000000)] + [InlineData(GuidV8Entropy.Weak, + -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, + -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, + -6576, -6162, -5630, -5505, -5472, -5382, -4706, -4680, -4509, -4454, + -4314, -3920, -3251, -3233, -3116, -2792, -2685, -2574, -2338, -2173, + -1934, -1914, -1528, -1483, -1410, -870, -757, -730, -263, -220, + 151, 426, 588, 970, 1213, 1269, 1376, 1397, 1450, 1770, + 2063, 2577, 2750, 2860, 3139, 3161, 3488, 3630, 3774, 4004, + 4163, 4198, 4366, 4662, 4746, 4879, 5467, 5601, 5912, 5979, + 6128, 6277, 6323, 6437, 6699, 6853, 7556, 7776, 7795, 8099, + 8336, 8592, 8682, 8683, 8818, 8904, 9375, 9466, 9551, 9708)] + [InlineData(GuidV8Entropy.Strong, + -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, + -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, + -6576, -6162, -5630, -5505, -5472, -5382, -4706, -4680, -4509, -4454, + -4314, -3920, -3251, -3233, -3116, -2792, -2685, -2574, -2338, -2173, + -1934, -1914, -1528, -1483, -1410, -870, -757, -730, -263, -220, + 151, 426, 588, 970, 1213, 1269, 1376, 1397, 1450, 1770, + 2063, 2577, 2750, 2860, 3139, 3161, 3488, 3630, 3774, 4004, + 4163, 4198, 4366, 4662, 4746, 4879, 5467, 5601, 5912, 5979, + 6128, 6277, 6323, 6437, 6699, 6853, 7556, 7776, 7795, 8099, + 8336, 8592, 8682, 8683, 8818, 8904, 9375, 9466, 9551, 9708)] + [InlineData(GuidV8Entropy.Weak, + 5635912, 6673780, 17277183, 17512959, 19098799, 21672621, 30581958, 30824885, 31874213, 35192781, + 36337094, 37752116, 38387215, 39154682, 40525427, 52288093, 55218356, 59065156, 65231785, 75430932, + 76289058, 79078058, 85770685, 85925884, 94726743, 94864163, 95781967, 96150006, 96482085, 102570414, + 107768232, 110571078, 110680108, 117974892, 119800380, 126381415, 135895862, 140034471, 149039187, 150906974, + 156853001, 160514433, 166446323, 170148965, 171759448, 176494242, 184537553, 188558155, 197194403, 197615804, + 201195323, 202294490, 203040975, 203331457, 205016944, 213460258, 217072025, 217185345, 231344025, 232390198, + 235053215, 240175073, 245030721, 252275255, 252310334, 277070940, 277359970, 280624756, 288601124, 292427106, + 292563035, 299285016, 303834917, 310357836, 315078337, 316367236, 318311758, 318873972, 319675272, 321784171, + 324204294, 327667283, 330287252, 338438172, 349863360, 360777768, 366398711, 368637150, 368776734, 371900343, + 379094084, 379818879, 381448333, 381814627, 382393101, 382483709, 385600870, 389455134, 396115960, 399364095)] + [InlineData(GuidV8Entropy.Strong, + 5635912, 6673780, 17277183, 17512959, 19098799, 21672621, 30581958, 30824885, 31874213, 35192781, + 36337094, 37752116, 38387215, 39154682, 40525427, 52288093, 55218356, 59065156, 65231785, 75430932, + 76289058, 79078058, 85770685, 85925884, 94726743, 94864163, 95781967, 96150006, 96482085, 102570414, + 107768232, 110571078, 110680108, 117974892, 119800380, 126381415, 135895862, 140034471, 149039187, 150906974, + 156853001, 160514433, 166446323, 170148965, 171759448, 176494242, 184537553, 188558155, 197194403, 197615804, + 201195323, 202294490, 203040975, 203331457, 205016944, 213460258, 217072025, 217185345, 231344025, 232390198, + 235053215, 240175073, 245030721, 252275255, 252310334, 277070940, 277359970, 280624756, 288601124, 292427106, + 292563035, 299285016, 303834917, 310357836, 315078337, 316367236, 318311758, 318873972, 319675272, 321784171, + 324204294, 327667283, 330287252, 338438172, 349863360, 360777768, 366398711, 368637150, 368776734, 371900343, + 379094084, 379818879, 381448333, 381814627, 382393101, 382483709, 385600870, 389455134, 396115960, 399364095)] + public void Guids_Differing_By_Minutes_Should_Be_Sortable(GuidV8Entropy entropy, params int[] seconds) + { + var rng = new Random(25); + var referenceTime = new DateTime(2024, 05, 17, 15, 36, 13, 771, DateTimeKind.Utc); + SortedList expected = new(seconds.Length); + foreach (var s in seconds) + { + var timestamp = referenceTime.AddMinutes(s); + expected.Add(timestamp, GuidV8.NewGuid(timestamp, entropy)); + } + + var sut = expected.Values.ToArray(); + rng.Shuffle(sut); + + sut.Order().Should().Equal(expected.Select(x => x.Value)); + sut.OrderBy(x => x.ToString()).Should().Equal(expected.Select(x => x.Value)); + + sut.OrderDescending().Should().Equal(expected.Reverse().Select(x => x.Value)); + sut.OrderByDescending(x => x.ToString()).Should().Equal(expected.Reverse().Select(x => x.Value)); + } + + [Theory] + [InlineData(GuidV8Entropy.Weak, + -250000000, -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 2100000000)] + [InlineData(GuidV8Entropy.Strong, + -250000000, -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 2100000000)] [InlineData(GuidV8Entropy.Weak, -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, @@ -43,7 +117,7 @@ public class NewGuid 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] + 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484, 982233485)] [InlineData(GuidV8Entropy.Strong, 6629058, 24114993, 40561510, 46245969, 46876997, 48747281, 80489854, 110237218, 117445694, 118974860, 135132579, 141760591, 149114066, 158322437, 159065333, 164925904, 173848639, 175086337, 175704556, 176335514, @@ -54,7 +128,7 @@ public class NewGuid 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] + 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484, 982233485)] public void Guids_Differing_By_Seconds_Should_Be_Sortable(GuidV8Entropy entropy, params int[] seconds) { var rng = new Random(25); @@ -69,19 +143,22 @@ public class NewGuid var sut = expected.Values.ToArray(); rng.Shuffle(sut); - sut.Order().Should().BeEquivalentTo(expected.Select(x => x.Value)); - sut.OrderDescending().Should().BeEquivalentTo(expected.Reverse().Select(x => x.Value)); + sut.Order().Should().Equal(expected.Select(x => x.Value)); + sut.OrderBy(x => x.ToString()).Should().Equal(expected.Select(x => x.Value)); + + sut.OrderDescending().Should().Equal(expected.Reverse().Select(x => x.Value)); + sut.OrderByDescending(x => x.ToString()).Should().Equal(expected.Reverse().Select(x => x.Value)); } [Theory] [InlineData(GuidV8Entropy.Weak, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -250000000, -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 2100000000)] [InlineData(GuidV8Entropy.Strong, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, + -250000000, -25000000, -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] + 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000, 2100000000)] [InlineData(GuidV8Entropy.Weak, -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, @@ -114,7 +191,7 @@ public class NewGuid 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] + 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484, 982233485)] [InlineData(GuidV8Entropy.Strong, 6629058, 24114993, 40561510, 46245969, 46876997, 48747281, 80489854, 110237218, 117445694, 118974860, 135132579, 141760591, 149114066, 158322437, 159065333, 164925904, 173848639, 175086337, 175704556, 176335514, @@ -125,10 +202,10 @@ public class NewGuid 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] + 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484, 982233485)] public void Guids_Differing_By_Milliseconds_Should_Be_Sortable(GuidV8Entropy entropy, params int[] seconds) { - var rng = new Random(25); + var rng = new Random(26); var referenceTime = new DateTime(2024, 05, 17, 15, 36, 13, 771, DateTimeKind.Utc); SortedList expected = new(seconds.Length); foreach (var s in seconds) @@ -140,78 +217,10 @@ public class NewGuid var sut = expected.Values.ToArray(); rng.Shuffle(sut); - sut.Order().Should().BeEquivalentTo(expected.Select(x => x.Value)); - sut.OrderDescending().Should().BeEquivalentTo(expected.Reverse().Select(x => x.Value)); - } + sut.Order().Should().Equal(expected.Select(x => x.Value)); + sut.OrderBy(x => x.ToString()).Should().Equal(expected.Select(x => x.Value)); - [Theory] - [InlineData(GuidV8Entropy.Weak, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, - -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] - [InlineData(GuidV8Entropy.Strong, - -25000000 -10000000, -5000000, -2000000, -1000000, -500000, -250000, -100000, -25000, -10000, -5000, - -2500, -1000, -500, -499, -497, -450, -300, -200, -50, -1, 0, 1, 2, 5, 10, 20, 50, 100, 200, 350, 500, 1000, - 2500, 5000, 10000, 25000, 100000, 250000, 500000, 1000000, 2000000, 5000000, 10000000)] - [InlineData(GuidV8Entropy.Weak, - -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, - -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, - -6576, -6162, -5630, -5505, -5472, -5382, -4706, -4680, -4509, -4454, - -4314, -3920, -3251, -3233, -3116, -2792, -2685, -2574, -2338, -2173, - -1934, -1914, -1528, -1483, -1410, -870, -757, -730, -263, -220, - 151, 426, 588, 970, 1213, 1269, 1376, 1397, 1450, 1770, - 2063, 2577, 2750, 2860, 3139, 3161, 3488, 3630, 3774, 4004, - 4163, 4198, 4366, 4662, 4746, 4879, 5467, 5601, 5912, 5979, - 6128, 6277, 6323, 6437, 6699, 6853, 7556, 7776, 7795, 8099, - 8336, 8592, 8682, 8683, 8818, 8904, 9375, 9466, 9551, 9708)] - [InlineData(GuidV8Entropy.Strong, - -9863, -9740, -9214, -8878, -8674, -8652, -8640, -8565, -8518, -8449, - -8390, -8193, -8108, -7808, -7501, -7203, -7133, -7020, -6983, -6855, - -6576, -6162, -5630, -5505, -5472, -5382, -4706, -4680, -4509, -4454, - -4314, -3920, -3251, -3233, -3116, -2792, -2685, -2574, -2338, -2173, - -1934, -1914, -1528, -1483, -1410, -870, -757, -730, -263, -220, - 151, 426, 588, 970, 1213, 1269, 1376, 1397, 1450, 1770, - 2063, 2577, 2750, 2860, 3139, 3161, 3488, 3630, 3774, 4004, - 4163, 4198, 4366, 4662, 4746, 4879, 5467, 5601, 5912, 5979, - 6128, 6277, 6323, 6437, 6699, 6853, 7556, 7776, 7795, 8099, - 8336, 8592, 8682, 8683, 8818, 8904, 9375, 9466, 9551, 9708)] - [InlineData(GuidV8Entropy.Weak, - 6629058, 24114993, 40561510, 46245969, 46876997, 48747281, 80489854, 110237218, 117445694, 118974860, - 135132579, 141760591, 149114066, 158322437, 159065333, 164925904, 173848639, 175086337, 175704556, 176335514, - 200302773, 207133553, 230088723, 234521706, 239587338, 263755571, 264571928, 290118284, 292346548, 319322378, - 320988273, 322638028, 324110126, 326855208, 332533719, 336668313, 350798512, 366520367, 380595181, 405033666, - 410929500, 414099488, 417697882, 421269768, 431907031, 435262715, 442168482, 449797406, 458508845, 489039529, - 503344305, 514934509, 537515867, 555210743, 563951463, 578783864, 580560518, 580902916, 605801607, 635231377, - 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, - 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, - 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] - [InlineData(GuidV8Entropy.Strong, - 6629058, 24114993, 40561510, 46245969, 46876997, 48747281, 80489854, 110237218, 117445694, 118974860, - 135132579, 141760591, 149114066, 158322437, 159065333, 164925904, 173848639, 175086337, 175704556, 176335514, - 200302773, 207133553, 230088723, 234521706, 239587338, 263755571, 264571928, 290118284, 292346548, 319322378, - 320988273, 322638028, 324110126, 326855208, 332533719, 336668313, 350798512, 366520367, 380595181, 405033666, - 410929500, 414099488, 417697882, 421269768, 431907031, 435262715, 442168482, 449797406, 458508845, 489039529, - 503344305, 514934509, 537515867, 555210743, 563951463, 578783864, 580560518, 580902916, 605801607, 635231377, - 636537435, 637241486, 676077268, 691630262, 696158602, 714007596, 715537265, 718873134, 724266320, 741049828, - 749379956, 757561933, 758668417, 761735205, 770349479, 797570403, 805896481, 809050934, 821655964, 821980469, - 830824227, 840429528, 851772315, 859717719, 859763860, 867675943, 912124563, 914880620, 923914294, 930298008, - 932610035, 937468680, 945565998, 949277691, 949397209, 951283050, 953249971, 953953188, 976210158, 982233484)] - public void Guids_Differing_By_Microseconds_Should_Be_Sortable(GuidV8Entropy entropy, params int[] seconds) - { - var rng = new Random(25); - var referenceTime = new DateTime(2024, 05, 17, 15, 36, 13, 771, DateTimeKind.Utc); - SortedList expected = new(seconds.Length); - foreach (var s in seconds) - { - var timestamp = referenceTime.AddMicroseconds(s); - expected.Add(timestamp, GuidV8.NewGuid(timestamp, entropy)); - } - - var sut = expected.Values.ToArray(); - rng.Shuffle(sut); - - sut.Order().Should().BeEquivalentTo(expected.Select(x => x.Value)); - sut.OrderDescending().Should().BeEquivalentTo(expected.Reverse().Select(x => x.Value)); + sut.OrderDescending().Should().Equal(expected.Reverse().Select(x => x.Value)); + sut.OrderByDescending(x => x.ToString()).Should().Equal(expected.Reverse().Select(x => x.Value)); } } diff --git a/Core/GuidV8.cs b/Core/GuidV8.cs index 13f9d39..41ae401 100644 --- a/Core/GuidV8.cs +++ b/Core/GuidV8.cs @@ -13,28 +13,28 @@ public static class GuidV8 [Pure] public static Guid NewGuid(DateTime dateTime, GuidV8Entropy entropy = GuidV8Entropy.Strong) { - var timestamp = dateTime.ToBinary() & 0x3FFF_FFFF_FFFF_FFFF; + var epoch = dateTime.Subtract(DateTime.UnixEpoch); + var timestamp = epoch.Ticks / (TimeSpan.TicksPerMillisecond / 10); Span ts = stackalloc byte[8]; MemoryMarshal.Write(ts, timestamp); Span bytes = stackalloc byte[16]; - ts[4..].CopyTo(bytes[..4]); - ts[2..4].CopyTo(bytes[4..6]); - ts[..2].CopyTo(bytes[6..8]); - - bytes[7] = (byte)((bytes[7] & 0x0f) | 0x80); + ts[0..2].CopyTo(bytes[4..6]); + ts[2..6].CopyTo(bytes[..4]); if (entropy == GuidV8Entropy.Strong) { - RandomNumberGenerator.Fill(bytes[8..]); + RandomNumberGenerator.Fill(bytes[6..]); } else { - Random.Shared.NextBytes(bytes[8..]); + Random.Shared.NextBytes(bytes[6..]); } + bytes[7] = (byte)((bytes[7] & 0x0F) | 0x80); + return new Guid(bytes); } }