diff --git a/src/NHibernate.Test/IdGen/GuidComb/GuidCombFixture.cs b/src/NHibernate.Test/IdGen/GuidComb/GuidCombFixture.cs
new file mode 100644
index 00000000000..b79bbb807b1
--- /dev/null
+++ b/src/NHibernate.Test/IdGen/GuidComb/GuidCombFixture.cs
@@ -0,0 +1,32 @@
+using System;
+using NHibernate.Id;
+using NUnit.Framework;
+
+namespace NHibernate.Test.IdGen.GuidComb;
+
+[TestFixture]
+public class GuidCombFixture
+{
+ class GuidCombGeneratorEx : GuidCombGenerator
+ {
+ public static Guid Generate(string guid, DateTime utcNow) => GenerateComb(Guid.Parse(guid), utcNow);
+ }
+
+ [Test]
+ public void CanGenerateSequentialGuid()
+ {
+ Assert.AreEqual(Guid.Parse("076a04fa-ef4e-4093-8479-b0e10103cdc5"),
+ GuidCombGeneratorEx.Generate(
+ "076a04fa-ef4e-4093-8479-8599e96f14cf",
+ new DateTime(2023, 12, 23, 15, 45, 55, DateTimeKind.Utc)),
+ "seed: 076a04fa");
+
+ Assert.AreEqual(Guid.Parse("81162ee2-a4cb-4611-9327-d61f0137e5b6"),
+ GuidCombGeneratorEx.Generate(
+ "81162ee2-a4cb-4611-9327-23bbda36176c",
+ new DateTime(2050, 01, 29, 18, 55, 35, DateTimeKind.Utc)),
+ "seed: 81162ee2");
+
+ }
+
+}
diff --git a/src/NHibernate/Async/Id/GuidCombGenerator.cs b/src/NHibernate/Async/Id/GuidCombGenerator.cs
index 59540f3d2ca..eb4ef6c7c63 100644
--- a/src/NHibernate/Async/Id/GuidCombGenerator.cs
+++ b/src/NHibernate/Async/Id/GuidCombGenerator.cs
@@ -9,6 +9,7 @@
using System;
+using System.Diagnostics;
using NHibernate.Engine;
namespace NHibernate.Id
diff --git a/src/NHibernate/Id/GuidCombGenerator.cs b/src/NHibernate/Id/GuidCombGenerator.cs
index cb01227c979..adbbd78196d 100644
--- a/src/NHibernate/Id/GuidCombGenerator.cs
+++ b/src/NHibernate/Id/GuidCombGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using NHibernate.Engine;
namespace NHibernate.Id
@@ -36,34 +37,32 @@ public partial class GuidCombGenerator : IIdentifierGenerator
/// The new identifier as a .
public object Generate(ISessionImplementor session, object obj)
{
- return GenerateComb();
+ return GenerateComb(Guid.NewGuid(), DateTime.UtcNow);
}
///
/// Generate a new using the comb algorithm.
///
- private Guid GenerateComb()
+ protected static Guid GenerateComb(Guid guid, DateTime utcNow)
{
- byte[] guidArray = Guid.NewGuid().ToByteArray();
-
- DateTime now = DateTime.UtcNow;
-
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+ Span guidArray = stackalloc byte[16];
+ guid.TryWriteBytes(guidArray);
+#else
+ var guidArray = guid.ToByteArray();
+#endif
// Get the days and milliseconds which will be used to build the byte string
- TimeSpan days = new TimeSpan(now.Ticks - BaseDateTicks);
- TimeSpan msecs = now.TimeOfDay;
-
- // Convert to a byte array
+ var ts = new TimeSpan(utcNow.Ticks - BaseDateTicks);
+ var days = ts.Days;
+ guidArray[10] = (byte) (days >> 8);
+ guidArray[11] = (byte) days;
+
// Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333
- byte[] daysArray = BitConverter.GetBytes(days.Days);
- byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));
-
- // Reverse the bytes to match SQL Servers ordering
- Array.Reverse(daysArray);
- Array.Reverse(msecsArray);
-
- // Copy the bytes into the guid
- Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
- Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
+ var msecs = (long) (utcNow.TimeOfDay.TotalMilliseconds / 3.333333);
+ guidArray[12] = (byte) (msecs >> 24);
+ guidArray[13] = (byte) (msecs >> 16);
+ guidArray[14] = (byte) (msecs >> 8);
+ guidArray[15] = (byte) msecs;
return new Guid(guidArray);
}