From 662619f791d16e30cf3a587fad256461b20f9672 Mon Sep 17 00:00:00 2001 From: amsga <49681949+amsga@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:03:17 +0800 Subject: [PATCH] Added UUID v7 generated based on current system date and time as well as Fixed Bit-Length Dedicated Counter (Method 1). Added UUID v7 generated based on current system date and time as well as Replace Leftmost Random Bits with Increased Clock Precision (Method 3). --- .github/workflows/dotnet.yml | 2 +- CHANGELOG.md | 8 + README.md | 2 +- UUIDUtil/UUIDNamespace.cs | 10 +- UUIDUtil/UUIDUtil.csproj | 4 +- UUIDUtil/UUIDv1.cs | 136 ++++++++--------- UUIDUtil/UUIDv6.cs | 106 ++++++------- UUIDUtil/UUIDv7.cs | 143 +++++++++++++----- UUIDUtil/Uuid.cs | 41 +++-- XUnitTestProjectUUID/UnitTestUUIDv7.cs | 78 +++++++++- .../XUnitTestProjectUUID.csproj | 10 +- 11 files changed, 357 insertions(+), 183 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 060f0e4..3a90210 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - dotnet: [ '6.0.x' ] + dotnet: [ '8.0.x' ] name: .NET ${{ matrix.dotnet }} steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 46df061..607242e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v2.1.0-beta] - 2024-06-05 +[v2.1.0-beta](https://github.com/TensionDev/UUIDUtil/releases/tag/v2.1.0-beta) + +### Changed +- Added UUID v7 generated based on current system date and time as well as Fixed Bit-Length Dedicated Counter (Method 1). +- Added UUID v7 generated based on current system date and time as well as Replace Leftmost Random Bits with Increased Clock Precision (Method 3). + + ## [v2.1.0-alpha] - 2023-06-15 [v2.1.0-alpha](https://github.com/TensionDev/UUIDUtil/releases/tag/v2.1.0-alpha) diff --git a/README.md b/README.md index b36e8fc..defb031 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ This project references the following documents for implementation. - [Universally unique identifier - Wikipedia](https://en.wikipedia.org/wiki/Universally_unique_identifier) - [MySQL :: MySQL 8.0 Reference Manual :: 12.24 Miscellaneous Functions](https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid) - [rfc4122](https://datatracker.ietf.org/doc/html/rfc4122) -- [draft-ietf-uuidrev-rfc4122bis-14](https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis) +- [rfc9562](https://datatracker.ietf.org/doc/html/rfc9562) diff --git a/UUIDUtil/UUIDNamespace.cs b/UUIDUtil/UUIDNamespace.cs index e1f3be1..c437d4e 100644 --- a/UUIDUtil/UUIDNamespace.cs +++ b/UUIDUtil/UUIDNamespace.cs @@ -23,23 +23,23 @@ namespace TensionDev.UUID /// /// Class Library to generate Universally Unique Identifier (UUID) / Globally Unique Identifier (GUID) based on Version 3 (MD5 namespace name-based). /// - public class UUIDNamespace + public static class UUIDNamespace { /// /// Namespace for Domain Name System /// - public static Uuid DNS = new Uuid(0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); + public static readonly Uuid DNS = new Uuid(0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); /// /// Namespace for URLs /// - public static Uuid URL = new Uuid(0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); + public static readonly Uuid URL = new Uuid(0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); /// /// Namespace for ISO Object IDs (OIDs) /// - public static Uuid OID = new Uuid(0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); + public static readonly Uuid OID = new Uuid(0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); /// /// Namespace for X.500 Distinguished Names(DNs) /// - public static Uuid X500 = new Uuid(0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); + public static readonly Uuid X500 = new Uuid(0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8); } } diff --git a/UUIDUtil/UUIDUtil.csproj b/UUIDUtil/UUIDUtil.csproj index 19c1d51..543a7b6 100644 --- a/UUIDUtil/UUIDUtil.csproj +++ b/UUIDUtil/UUIDUtil.csproj @@ -7,12 +7,12 @@ true true TensionDev.UUID - 2.1.0-alpha + 2.1.0-beta TensionDev amsga TensionDev TensionDev.UUID A project to store various UUID functions within a library for future use. - Copyright (c) TensionDev 2021 - 2023 + Copyright (c) TensionDev 2021 Apache-2.0 https://github.com/TensionDev/UUIDUtil https://github.com/TensionDev/UUIDUtil diff --git a/UUIDUtil/UUIDv1.cs b/UUIDUtil/UUIDv1.cs index 77ec578..7165ff5 100644 --- a/UUIDUtil/UUIDv1.cs +++ b/UUIDUtil/UUIDv1.cs @@ -25,10 +25,10 @@ public class UUIDv1 { protected internal static System.Net.NetworkInformation.PhysicalAddress s_physicalAddress = System.Net.NetworkInformation.PhysicalAddress.None; protected internal static Int32 s_clock = Int32.MinValue; - protected internal static DateTime s_epoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc); + protected internal static readonly DateTime s_epoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc); - protected internal static Object s_initLock = new Object(); - protected internal static Object s_clockLock = new Object(); + protected internal static readonly Object s_initLock = new Object(); + protected internal static readonly Object s_clockLock = new Object(); /// /// Initialises a new GUID/UUID based on Version 1 (date-time and MAC address) @@ -39,68 +39,6 @@ public static Uuid NewUUIDv1() return NewUUIDv1(DateTime.UtcNow); } - /// - /// Initialises the 48-bit Node ID and returns it.
- /// Returns the MAC Address of a Network Interface Card, if available. - /// Otherwise, returns a randomly genrated 48-bit Node ID. - ///
- /// A byte-array representing the 48-bit Node ID - public static Byte[] GetNodeID() - { - if (System.Net.NetworkInformation.PhysicalAddress.None.Equals(s_physicalAddress)) - { - System.Net.NetworkInformation.NetworkInterface[] networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); - if (networkInterfaces.Length > 0) - { - s_physicalAddress = networkInterfaces[0].GetPhysicalAddress(); - } - else - { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] fakeNode = new Byte[6]; - cryptoServiceProvider.GetBytes(fakeNode); - fakeNode[0] = (Byte)(fakeNode[0] | 0x01); - s_physicalAddress = new System.Net.NetworkInformation.PhysicalAddress(fakeNode); - } - } - } - - return s_physicalAddress.GetAddressBytes(); - } - - /// - /// Intialises the 14-bit Clock Sequence and returns the current value with the Variant.
- /// Will return an incremented Clock Sequence on each call, modulo 14-bit. - ///
- /// A byte-array representing the 14-bit Clock Sequence, together with the Variant - public static Byte[] GetClockSequence() - { - lock (s_initLock) - { - if (s_clock < 0) - { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] clockInit = new Byte[4]; - cryptoServiceProvider.GetBytes(clockInit); - s_clock = BitConverter.ToInt32(clockInit, 0) & 0x3FFF; - s_clock |= 0x8000; - } - } - } - - Int32 result; - lock (s_clockLock) - { - result = s_clock++; - if (s_clock >= 0xC000) - s_clock = 0x8000; - } - - return BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder((Int16)result)); - } - /// /// Initialises a new GUID/UUID based on Version 1 (date-time and MAC address), based on the given date and time. /// @@ -146,10 +84,10 @@ public static Uuid NewUUIDv1(DateTime dateTime, Byte[] clockSequence, Byte[] nod if (nodeID.Length < 6) throw new ArgumentException(String.Format("Node ID contains less than 48-bit: {0} bytes", nodeID.Length), nameof(nodeID)); - TimeSpan timesince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); - Int64 timeinterval = timesince.Ticks; + TimeSpan timeSince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); + Int64 timeInterval = timeSince.Ticks; - Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeinterval)); + Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeInterval)); Byte[] hex = new Byte[16]; @@ -178,5 +116,67 @@ public static Uuid NewUUIDv1(DateTime dateTime, Byte[] clockSequence, Byte[] nod return Id; } + + /// + /// Initialises the 48-bit Node ID and returns it.
+ /// Returns the MAC Address of a Network Interface Card, if available. + /// Otherwise, returns a randomly genrated 48-bit Node ID. + ///
+ /// A byte-array representing the 48-bit Node ID + public static Byte[] GetNodeID() + { + if (System.Net.NetworkInformation.PhysicalAddress.None.Equals(s_physicalAddress)) + { + System.Net.NetworkInformation.NetworkInterface[] networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); + if (networkInterfaces.Length > 0) + { + s_physicalAddress = networkInterfaces[0].GetPhysicalAddress(); + } + else + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] fakeNode = new Byte[6]; + cryptoServiceProvider.GetBytes(fakeNode); + fakeNode[0] = (Byte)(fakeNode[0] | 0x01); + s_physicalAddress = new System.Net.NetworkInformation.PhysicalAddress(fakeNode); + } + } + } + + return s_physicalAddress.GetAddressBytes(); + } + + /// + /// Intialises the 14-bit Clock Sequence and returns the current value with the Variant.
+ /// Will return an incremented Clock Sequence on each call, modulo 14-bit. + ///
+ /// A byte-array representing the 14-bit Clock Sequence, together with the Variant + public static Byte[] GetClockSequence() + { + lock (s_initLock) + { + if (s_clock < 0) + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] clockInit = new Byte[4]; + cryptoServiceProvider.GetBytes(clockInit); + s_clock = BitConverter.ToInt32(clockInit, 0) & 0x3FFF; + s_clock |= 0x8000; + } + } + } + + Int32 result; + lock (s_clockLock) + { + result = s_clock++; + if (s_clock >= 0xC000) + s_clock = 0x8000; + } + + return BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder((Int16)result)); + } } } diff --git a/UUIDUtil/UUIDv6.cs b/UUIDUtil/UUIDv6.cs index de4c991..81424c4 100644 --- a/UUIDUtil/UUIDv6.cs +++ b/UUIDUtil/UUIDv6.cs @@ -24,10 +24,10 @@ namespace TensionDev.UUID public class UUIDv6 { protected internal static Int32 s_clock = Int32.MinValue; - protected internal static DateTime s_epoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc); + protected internal static readonly DateTime s_epoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc); - protected internal static Object s_initLock = new Object(); - protected internal static Object s_clockLock = new Object(); + protected internal static readonly Object s_initLock = new Object(); + protected internal static readonly Object s_clockLock = new Object(); /// /// Initialises a new GUID/UUID based on Version 6 (date-time) @@ -38,53 +38,6 @@ public static Uuid NewUUIDv6() return NewUUIDv6(DateTime.UtcNow); } - /// - /// Initialises the 48-bit Node ID and returns it.
- /// Returns a randomly genrated 48-bit Node ID. - ///
- /// A byte-array representing the 48-bit Node ID - public static Byte[] GetNodeID() - { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] fakeNode = new Byte[6]; - cryptoServiceProvider.GetBytes(fakeNode); - return fakeNode; - } - } - - /// - /// Intialises the 14-bit Clock Sequence and returns the current value with the Variant.
- /// Will return an incremented Clock Sequence on each call, modulo 14-bit. - ///
- /// A byte-array representing the 14-bit Clock Sequence, together with the Variant - public static Byte[] GetClockSequence() - { - lock (s_initLock) - { - if (s_clock < 0) - { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] clockInit = new Byte[4]; - cryptoServiceProvider.GetBytes(clockInit); - s_clock = BitConverter.ToInt32(clockInit, 0) & 0x3FFF; - s_clock |= 0x8000; - } - } - } - - Int32 result; - lock (s_clockLock) - { - result = s_clock++; - if (s_clock >= 0xC000) - s_clock = 0x8000; - } - - return BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder((Int16)result)); - } - /// /// Initialises a new GUID/UUID based on Version 6 (date-time), based on the given date and time. /// @@ -130,10 +83,10 @@ public static Uuid NewUUIDv6(DateTime dateTime, Byte[] clockSequence, Byte[] nod if (nodeID.Length < 6) throw new ArgumentException(String.Format("Node ID contains less than 48-bit: {0} bytes", nodeID.Length), nameof(nodeID)); - TimeSpan timesince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); - Int64 timeinterval = timesince.Ticks << 4; + TimeSpan timeSince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); + Int64 timeInterval = timeSince.Ticks << 4; - Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeinterval)); + Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeInterval)); Byte[] hex = new Byte[16]; @@ -162,5 +115,52 @@ public static Uuid NewUUIDv6(DateTime dateTime, Byte[] clockSequence, Byte[] nod return Id; } + + /// + /// Initialises the 48-bit Node ID and returns it.
+ /// Returns a randomly genrated 48-bit Node ID. + ///
+ /// A byte-array representing the 48-bit Node ID + public static Byte[] GetNodeID() + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] fakeNode = new Byte[6]; + cryptoServiceProvider.GetBytes(fakeNode); + return fakeNode; + } + } + + /// + /// Intialises the 14-bit Clock Sequence and returns the current value with the Variant.
+ /// Will return an incremented Clock Sequence on each call, modulo 14-bit. + ///
+ /// A byte-array representing the 14-bit Clock Sequence, together with the Variant + public static Byte[] GetClockSequence() + { + lock (s_initLock) + { + if (s_clock < 0) + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] clockInit = new Byte[4]; + cryptoServiceProvider.GetBytes(clockInit); + s_clock = BitConverter.ToInt32(clockInit, 0) & 0x3FFF; + s_clock |= 0x8000; + } + } + } + + Int32 result; + lock (s_clockLock) + { + result = s_clock++; + if (s_clock >= 0xC000) + s_clock = 0x8000; + } + + return BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder((Int16)result)); + } } } diff --git a/UUIDUtil/UUIDv7.cs b/UUIDUtil/UUIDv7.cs index b7b37d5..ef173db 100644 --- a/UUIDUtil/UUIDv7.cs +++ b/UUIDUtil/UUIDv7.cs @@ -15,6 +15,7 @@ // limitations under the License. using System; +using System.Threading; namespace TensionDev.UUID { @@ -23,45 +24,35 @@ namespace TensionDev.UUID ///
public class UUIDv7 { - protected internal static DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + protected internal static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - /// - /// Initialises a new GUID/UUID based on Version 7 (date-time) - /// - /// A new Uuid object - public static Uuid NewUUIDv7() - { - return NewUUIDv7(DateTime.UtcNow); - } + protected internal static UInt16 s_counter = 0; + protected internal static readonly Object s_counterLock = new Object(); - /// - /// Initialises the 12-bit rand_a and returns it.
- /// Returns a randomly genrated 16-bit rand_a. - ///
- /// A byte-array representing the 16-bit rand_a - public static Byte[] GetRandomA() + public enum GenerationMethod { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] fakeNode = new Byte[2]; - cryptoServiceProvider.GetBytes(fakeNode); - return fakeNode; - } + /// + /// Random bits for the remaining 74 bits. + /// + Random = 0, + /// + /// Fixed Bit-Length Dedicated Counter (Method 1) + /// + Method1 = 1, + //Method2 = 2, + /// + /// Replace Leftmost Random Bits with Increased Clock Precision (Method 3) + /// + Method3 = 3, } /// - /// Initialises the 62-bit rand_b and returns it.
- /// Returns a randomly genrated 64-bit rand_b. + /// Initialises a new GUID/UUID based on Version 7 (date-time) ///
- /// A byte-array representing the 64-bit rand_b - public static Byte[] GetRandomB() + /// A new Uuid object + public static Uuid NewUUIDv7(GenerationMethod method = GenerationMethod.Random) { - using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) - { - Byte[] fakeNode = new Byte[8]; - cryptoServiceProvider.GetBytes(fakeNode); - return fakeNode; - } + return NewUUIDv7(DateTime.UtcNow, method); } /// @@ -69,9 +60,20 @@ public static Byte[] GetRandomB() /// /// Given Date and Time /// A new Uuid object - public static Uuid NewUUIDv7(DateTime dateTime) + public static Uuid NewUUIDv7(DateTime dateTime, GenerationMethod method = GenerationMethod.Random) { - return NewUUIDv7(dateTime, GetRandomA(), GetRandomB()); + switch (method) + { + default: + case GenerationMethod.Random: + return NewUUIDv7(dateTime, GetRandomA(), GetRandomB()); + + case GenerationMethod.Method1: + return NewUUIDv7(dateTime, GetFixedBitLengthDedicatedCounterA(), GetRandomB()); + + case GenerationMethod.Method3: + return NewUUIDv7(dateTime, GetIncreasedClockPrecisionA(dateTime), GetRandomB()); + } } /// @@ -97,10 +99,10 @@ public static Uuid NewUUIDv7(DateTime dateTime, Byte[] randomA, Byte[] randomB) if (randomB.Length < 8) throw new ArgumentException(String.Format("rand_b contains less than 64-bit: {0} bytes", randomB.Length), nameof(randomB)); - TimeSpan timesince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); - Int64 timeinterval = ((Int64)timesince.TotalMilliseconds) << 16; + TimeSpan timeSince = dateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); + Int64 timeInterval = ((Int64)timeSince.TotalMilliseconds) << 16; - Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeinterval)); + Byte[] time = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(timeInterval)); Byte[] hex = new Byte[16]; @@ -128,5 +130,74 @@ public static Uuid NewUUIDv7(DateTime dateTime, Byte[] randomA, Byte[] randomB) return Id; } + + /// + /// Initialises the 12-bit rand_a and returns it.
+ /// Returns a randomly genrated 16-bit rand_a. + ///
+ /// A byte-array representing the 16-bit rand_a + public static Byte[] GetRandomA() + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] fakeNode = new Byte[2]; + cryptoServiceProvider.GetBytes(fakeNode); + return fakeNode; + } + } + + /// + /// Initialises the 12-bit rand_a based on Method 1 in Section 6.2 and returns it.
+ /// Returns a Fixed-Length Dedicated Counter 16-bit rand_a. + ///
+ /// A byte-array representing the 16-bit rand_a + public static Byte[] GetFixedBitLengthDedicatedCounterA() + { + lock (s_counterLock) + { + Int16 value = Convert.ToInt16(s_counter); + Byte[] counter = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(value)); + + ++s_counter; + if (s_counter >= 0x1000) + s_counter = 0; + + return counter; + } + } + + /// + /// Initialises the 12-bit rand_a based on Method 3 in Section 6.2 and returns it.
+ /// Returns a Increased Clock Precision 16-bit rand_a. + ///
+ /// + /// A byte-array representing the 16-bit rand_a + public static Byte[] GetIncreasedClockPrecisionA(DateTime currentDateTime) + { + TimeSpan timeSince = currentDateTime.ToUniversalTime() - s_epoch.ToUniversalTime(); + Int64 timeInterval = ((Int64)timeSince.TotalMilliseconds); + Double precisionInterval = timeSince.TotalMilliseconds - timeInterval; + Int16 precisionA = (Int16)Math.Floor(precisionInterval * 0x1000); + + Byte[] bytes = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(precisionA)); + + return bytes; + + } + + /// + /// Initialises the 62-bit rand_b and returns it.
+ /// Returns a randomly genrated 64-bit rand_b. + ///
+ /// A byte-array representing the 64-bit rand_b + public static Byte[] GetRandomB() + { + using (System.Security.Cryptography.RNGCryptoServiceProvider cryptoServiceProvider = new System.Security.Cryptography.RNGCryptoServiceProvider()) + { + Byte[] fakeNode = new Byte[8]; + cryptoServiceProvider.GetBytes(fakeNode); + return fakeNode; + } + } } } diff --git a/UUIDUtil/Uuid.cs b/UUIDUtil/Uuid.cs index 306ae35..9842b4e 100644 --- a/UUIDUtil/Uuid.cs +++ b/UUIDUtil/Uuid.cs @@ -20,7 +20,7 @@ namespace TensionDev.UUID { - public class Uuid : IComparable, IEquatable + public sealed class Uuid : IComparable, IEquatable { private uint _time_low; private ushort _time_mid; @@ -162,8 +162,8 @@ public Uuid(uint a, ushort b, ushort c, byte d, byte e, byte[] f) : this() throw new ArgumentException("f is not 6 bytes long"); _time_low = (uint)System.Net.IPAddress.HostToNetworkOrder((int)a); - _time_mid = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)b); - _time_hi_and_version = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)c); + _time_mid = (ushort)System.Net.IPAddress.HostToNetworkOrder((Int16)b); + _time_hi_and_version = (ushort)System.Net.IPAddress.HostToNetworkOrder((Int16)c); _clock_seq_hi_and_reserved = d; _clock_seq_low = e; f.CopyTo(_node, 0); @@ -186,8 +186,8 @@ public Uuid(uint a, ushort b, ushort c, byte d, byte e, byte[] f) : this() public Uuid(uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) : this() { _time_low = (uint)System.Net.IPAddress.HostToNetworkOrder((int)a); - _time_mid = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)b); - _time_hi_and_version = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)c); + _time_mid = (ushort)System.Net.IPAddress.HostToNetworkOrder((Int16)b); + _time_hi_and_version = (ushort)System.Net.IPAddress.HostToNetworkOrder((Int16)c); _clock_seq_hi_and_reserved = d; _clock_seq_low = e; _node[0] = f; @@ -233,6 +233,7 @@ public static bool TryParse(string input, out Uuid result) } catch (Exception) { + // Quietly suppress exception on Parse. } return vs; @@ -297,11 +298,11 @@ public bool Equals(Uuid other) /// /// Returns a value that indicates whether this instance is equal to a specified object. /// - /// The object to compare with this instance. + /// The object to compare with this instance. /// true if o is a Uuid that has the same value as this instance; otherwise,false. - public override bool Equals(object o) + public override bool Equals(object obj) { - if (o is Uuid other) + if (obj is Uuid other) { return Equals(other); } @@ -483,10 +484,10 @@ private string ToStringBraces() /// true if a and b are equal; otherwise, false. public static bool operator ==(Uuid a, Uuid b) { - if (a == null && b == null) + if (a is null && b is null) return true; - if (a == null || b == null) + if (a is null || b is null) return false; return a.Equals(b); @@ -502,5 +503,25 @@ private string ToStringBraces() { return !(a == b); } + + public static bool operator <(Uuid a, Uuid b) + { + return a.CompareTo(b) < 0; + } + + public static bool operator >(Uuid a, Uuid b) + { + return a.CompareTo(b) > 0; + } + + public static bool operator <=(Uuid a, Uuid b) + { + return a.CompareTo(b) <= 0; + } + + public static bool operator >=(Uuid a, Uuid b) + { + return a.CompareTo(b) >= 0; + } } } diff --git a/XUnitTestProjectUUID/UnitTestUUIDv7.cs b/XUnitTestProjectUUID/UnitTestUUIDv7.cs index b100bd0..1448112 100644 --- a/XUnitTestProjectUUID/UnitTestUUIDv7.cs +++ b/XUnitTestProjectUUID/UnitTestUUIDv7.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -13,7 +14,7 @@ public void TestGetRandomA() { byte[] randA = TensionDev.UUID.UUIDv7.GetRandomA(); - Assert.True(randA.Length == 2); + Assert.Equal(2, randA.Length); } [Fact] @@ -25,12 +26,85 @@ public void TestRandomGetRandomA() Assert.NotEqual(randA1, randA2); } + [Fact] + public void TestGetFixedBitLengthDedicatedCounterA() + { + byte[] counterA = TensionDev.UUID.UUIDv7.GetFixedBitLengthDedicatedCounterA(); + + Assert.Equal(2, counterA.Length); + } + + [Fact] + public void TestUniqueGetFixedBitLengthDedicatedCounterA() + { + ConcurrentBag countersA = new ConcurrentBag(); + + Parallel.For(0, 0x1000, + counter => + { + countersA.Add(TensionDev.UUID.UUIDv7.GetFixedBitLengthDedicatedCounterA()); + }); + + IEnumerable distinctCounters = countersA.Distinct(); + + Assert.Equal(distinctCounters.Count(), countersA.Count); + Assert.Equal(0x1000, distinctCounters.Count()); + Assert.Equal(0x1000, countersA.Count); + } + + [Fact] + public void TestOverflowGetCounterA() + { + ConcurrentBag countersA = new ConcurrentBag(); + + Parallel.For(0, 0x4000, + counter => + { + countersA.Add(TensionDev.UUID.UUIDv7.GetFixedBitLengthDedicatedCounterA()); + }); + + IEnumerable distinctCounters = countersA.Select(m => BitConverter.ToInt16(m)).Distinct(); + + Assert.Equal(0x1000, distinctCounters.Count()); + Assert.Equal(0x4000, countersA.Count); + } + + [Fact] + public void TestGetIncreasedClockPrecisionA() + { + DateTime dateTime = DateTime.Now; + byte[] counterA = TensionDev.UUID.UUIDv7.GetIncreasedClockPrecisionA(dateTime); + + Assert.Equal(2, counterA.Length); + } + + [Fact] + public void TestOverflowGetIncreasedClockPrecisionA() + { + ConcurrentBag countersA = new ConcurrentBag(); + + DateTime start = DateTime.Now; + DateTime end = start.AddMilliseconds(1); + + Parallel.For(start.Ticks, end.Ticks , + ticks => + { + DateTime dateTime = new DateTime(ticks, DateTimeKind.Local); + countersA.Add(TensionDev.UUID.UUIDv7.GetIncreasedClockPrecisionA(dateTime)); + }); + + IEnumerable distinctCounters = countersA.Select(m => BitConverter.ToInt16(m)).Distinct(); + + Assert.Equal(0x1000, distinctCounters.Count()); + Assert.Equal(10000, countersA.Count); + } + [Fact] public void TestGetRandomB() { byte[] randB = TensionDev.UUID.UUIDv7.GetRandomB(); - Assert.True(randB.Length == 8); + Assert.Equal(8, randB.Length); } [Fact] diff --git a/XUnitTestProjectUUID/XUnitTestProjectUUID.csproj b/XUnitTestProjectUUID/XUnitTestProjectUUID.csproj index dbcab31..27c8e0c 100644 --- a/XUnitTestProjectUUID/XUnitTestProjectUUID.csproj +++ b/XUnitTestProjectUUID/XUnitTestProjectUUID.csproj @@ -1,19 +1,19 @@  - net6.0 + net8.0 false - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive