Skip to content

Commit

Permalink
Major Changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Taiizor committed Dec 20, 2024
1 parent a4fe8f1 commit da14a5d
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 19 deletions.
54 changes: 43 additions & 11 deletions demo/UUID.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,39 @@ static async Task Main(string[] args)
Guid implicitToGuid = id; // Implicit conversion from UUID to Guid
Console.WriteLine($"Implicit conversions successful? {implicitFromGuid == id && implicitToGuid == guid}");

Console.WriteLine("\n5. Compact UUID Operations:");
Console.WriteLine("\n5. UUIDv7 Format and Structure:");
Console.WriteLine("Generating multiple UUIDs in the same millisecond to demonstrate ordering:");

// Aynı milisaniye içinde üretilen UUID'ler
List<UUID> uuidsInSameMs = new();
for (int i = 0; i < 5; i++)
{
uuidsInSameMs.Add(UUID.New());
}

Console.WriteLine("\nUUID Format Breakdown:");
foreach (UUID uuid in uuidsInSameMs)
{
string uuidStr = uuid.ToString();
Console.WriteLine($"\nUUID: {uuidStr}");
Console.WriteLine("Structure:");
Console.WriteLine($" Timestamp (48-bit): {uuidStr[..12]}");
Console.WriteLine($" Version (4-bit): {uuid.Version} (indicated by: {uuidStr[12..13]})");
Console.WriteLine($" Counter (12-bit): {uuidStr[13..16]}");
Console.WriteLine($" Variant (2-bit): {uuid.Variant} (indicated in random bits)");
Console.WriteLine($" Random: {uuidStr[16..]}");
Console.WriteLine($" Time: {uuid.Time:yyyy-MM-dd HH:mm:ss.fff}");
}

Console.WriteLine("\nDemonstrating monotonic ordering:");
List<UUID> orderedUuids = uuidsInSameMs.OrderBy(u => u).ToList();
Console.WriteLine("UUIDs in chronological order:");
foreach (UUID uuid in orderedUuids)
{
Console.WriteLine($" {uuid} - Counter: {uuid.ToString()[13..16]}");
}

Console.WriteLine("\n6. Compact UUID Operations:");
Console.WriteLine("Creating compact UUIDs (12 characters):");

// Temel kompakt UUID
Expand All @@ -68,7 +100,7 @@ static async Task Main(string[] args)
Console.WriteLine($"Compact UUID #{i + 1}: {compactId.ToInt64()}");
}

Console.WriteLine("\n6. Binary Operations:");
Console.WriteLine("\n7. Binary Operations:");
// TryWriteBytes example
byte[] byteBuffer = new byte[16];
bool writeSuccess = id.TryWriteBytes(byteBuffer);
Expand All @@ -79,7 +111,7 @@ static async Task Main(string[] args)
byte[] byteArray = id.ToByteArray();
Console.WriteLine($"Direct byte array: {BitConverter.ToString(byteArray).Replace("-", "")}");

Console.WriteLine("\n7. Int64 (Long) Conversions:");
Console.WriteLine("\n8. Int64 (Long) Conversions:");
// Generate multiple UUIDs to demonstrate conversion consistency
UUID[] uuids = new UUID[5];
ArrayExtension.Fill(uuids);
Expand Down Expand Up @@ -114,7 +146,7 @@ static async Task Main(string[] args)
Console.WriteLine($"Second Int64: {long2}");
Console.WriteLine($"Time ordering preserved?: {long2 > long1}");

Console.WriteLine("\n8. Base64 Operations:");
Console.WriteLine("\n9. Base64 Operations:");
string base64 = id.ToBase64();
Console.WriteLine($"UUID -> Base64: {base64}");
UUID fromBase64 = UUID.FromBase64(base64);
Expand All @@ -127,7 +159,7 @@ static async Task Main(string[] args)
Console.WriteLine($"Successfully parsed from Base64: {parsedFromBase64}");
}

Console.WriteLine("\n9. Byte Array Operations:");
Console.WriteLine("\n10. Byte Array Operations:");
byte[] bytes2 = id.ToByteArray();
Console.WriteLine($"UUID -> Bytes: {BitConverter.ToString(bytes2)}");
UUID fromBytes = UUID.FromByteArray(bytes2);
Expand All @@ -147,7 +179,7 @@ static async Task Main(string[] args)
Console.WriteLine($"Successfully wrote to byte array: {BitConverter.ToString(destination)}");
}

Console.WriteLine("\n10. Comparison Operations:");
Console.WriteLine("\n11. Comparison Operations:");
UUID id1 = new(); // Using parameterless constructor
await Task.Delay(1); // Wait to ensure different timestamp
UUID id2 = UUID.New();
Expand All @@ -159,7 +191,7 @@ static async Task Main(string[] args)
Console.WriteLine($"UUID1 > UUID2: {id1 > id2}");
Console.WriteLine($"UUID1 >= UUID2: {id1 >= id2}");

Console.WriteLine("\n11. Array Extension Methods:");
Console.WriteLine("\n12. Array Extension Methods:");
// Generate array of UUIDs
UUID[] generatedArray = ArrayExtension.Generate(5);
Console.WriteLine("Generated array of 5 UUIDs:");
Expand Down Expand Up @@ -198,7 +230,7 @@ static async Task Main(string[] args)
}
}

Console.WriteLine("\n12. Sorting and Thread Safety:");
Console.WriteLine("\n13. Sorting and Thread Safety:");
List<UUID> ids = new();
for (int i = 0; i < 5; i++)
{
Expand All @@ -219,7 +251,7 @@ static async Task Main(string[] args)
Console.WriteLine($" {uuid} - Time: {uuid.Time:yyyy-MM-dd HH:mm:ss.fff}");
}

Console.WriteLine("\n13. Thread-Safe UUID Generation:");
Console.WriteLine("\n14. Thread-Safe UUID Generation:");
HashSet<UUID> set = new();
List<Task> tasks = new();

Expand All @@ -244,7 +276,7 @@ static async Task Main(string[] args)
await Task.WhenAll(tasks);
Console.WriteLine($"Generated {set.Count} unique UUIDs across multiple threads");

Console.WriteLine("\n14. Comparing UUID and Guid initialization behaviors:\n");
Console.WriteLine("\n15. Comparing UUID and Guid initialization behaviors:\n");

// UUID initialization - always creates a unique identifier
UUID uuid3 = new();
Expand Down Expand Up @@ -280,7 +312,7 @@ static async Task Main(string[] args)
Console.WriteLine($"Are they equal? {newGuid1 == newGuid2}");
Console.WriteLine($"Is first empty? {newGuid1 == default}");

Console.WriteLine("\n15. Bulk UUID Generation Performance:");
Console.WriteLine("\n16. Bulk UUID Generation Performance:");
const int batchSize = 1000;
Console.WriteLine($"Generating {batchSize} UUIDs in batch...");

Expand Down
105 changes: 97 additions & 8 deletions src/UUID/Constructor/UUID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ namespace System
/// <summary>
/// UUID represents a modern and efficient unique identifier implementation,
/// designed for high performance and enhanced security in distributed systems.
/// This implementation follows UUIDv7 principles (draft-ietf-uuidrev-rfc4122bis).
/// This implementation follows UUIDv7 standard (draft-ietf-uuidrev-rfc4122bis).
/// </summary>
/// <remarks>
/// This implementation provides:
/// - Time-based ordering: Identifiers are sortable by creation time (48-bit timestamp)
/// - Security: Uses cryptographically secure random numbers
/// - Performance: Optimized for high-performance scenarios
/// - Compatibility: Full integration with .NET ecosystem
/// - Time-based ordering: 48-bit Unix timestamp (milliseconds)
/// - Monotonic counter: 12-bit sequence counter for same-millisecond ordering
/// - Version bits: 4 bits indicating UUIDv7
/// - Variant bits: 2 bits as per RFC 4122
/// - Random bits: Remaining bits for uniqueness
/// - Thread Safety: All operations are thread-safe
/// </remarks>
public readonly partial struct UUID(ulong timestamp, ulong random) : IEquatable<UUID>, IComparable<UUID>, IComparable
Expand All @@ -27,6 +28,49 @@ public readonly partial struct UUID(ulong timestamp, ulong random) : IEquatable<
/// </remarks>
private const int SIZE = 16;

/// <summary>
/// Thread-safe counter for monotonic sequence within same millisecond.
/// Used to ensure unique and ordered UUIDs when multiple are generated
/// in the same millisecond.
/// </summary>
private static int _counter;

/// <summary>
/// Gets the variant number of this UUID.
/// </summary>
/// <remarks>
/// Returns 2 for RFC 4122 variant UUIDs.
/// This is used to indicate the layout of bits in the UUID.
/// </remarks>
public byte Variant => VARIANT;

/// <summary>
/// Gets the version number of this UUID.
/// </summary>
/// <remarks>
/// Returns 7 for UUIDv7 (time-ordered with additional random bits).
/// The version number indicates how the UUID was generated.
/// </remarks>
public byte Version => VERSION;

/// <summary>
/// RFC 4122 variant identifier (2).
/// Used to indicate the layout of bits in the UUID.
/// </summary>
private const byte VARIANT = 0x02;

/// <summary>
/// UUID version identifier (7).
/// Indicates this is a Version 7 UUID (time-ordered).
/// </summary>
private const byte VERSION = 0x07;

/// <summary>
/// Stores the timestamp of the last generated UUID.
/// Used for maintaining monotonic ordering within the same millisecond.
/// </summary>
private static long _lastTimestamp;

/// <summary>
/// Gets the random component of the UUID.
/// </summary>
Expand All @@ -42,6 +86,12 @@ public readonly partial struct UUID(ulong timestamp, ulong random) : IEquatable<
/// </summary>
internal readonly ulong _timestamp = timestamp;

/// <summary>
/// Lock object for thread-safe counter operations.
/// Ensures monotonic ordering of UUIDs generated within the same millisecond.
/// </summary>
private static readonly object _counterLock = new();

/// <summary>
/// Characters used in Base32 encoding.
/// </summary>
Expand Down Expand Up @@ -132,10 +182,43 @@ public static UUID NewCompactWithTime(long timestamp)
/// </remarks>
private static ulong GenerateTimestamp()
{
ushort random = (ushort)_rng.Value!.Next(ushort.MaxValue);
long unixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
int counter = GetMonotonicCounter(unixMs);

return (((ulong)unixMs & 0x0000_FFFF_FFFF_FFFF) << 16) | random;
// Format: 48 bits timestamp + 4 bits version + 12 bits counter
ulong timestamp = ((ulong)unixMs & 0x0000_FFFF_FFFF_FFFF) << 16;
timestamp |= (ulong)VERSION << 12;
timestamp |= (uint)counter;

return timestamp;
}

/// <summary>
/// Gets a monotonically increasing counter for UUIDs generated within the same millisecond.
/// </summary>
/// <param name="timestamp">The current timestamp in milliseconds</param>
/// <returns>A 12-bit counter value that ensures monotonic ordering</returns>
/// <remarks>
/// This method ensures that UUIDs generated within the same millisecond
/// maintain a strict ordering through the use of a 12-bit counter.
/// The counter resets when moving to a new millisecond.
/// </remarks>
private static int GetMonotonicCounter(long timestamp)
{
lock (_counterLock)
{
if (timestamp > _lastTimestamp)
{
_counter = 0;
_lastTimestamp = timestamp;
}
else if (timestamp == _lastTimestamp)
{
_counter = (_counter + 1) & 0xFFF; // 12-bit counter
}

return _counter;
}
}

/// <summary>
Expand All @@ -148,7 +231,12 @@ private static ulong GenerateTimestamp()
/// </remarks>
private static ulong GenerateRandom()
{
return ((ulong)_rng.Value!.Next() << 32) | (uint)_rng.Value!.Next();
ulong random = ((ulong)_rng.Value!.Next() << 32) | (uint)_rng.Value!.Next();

// Set the variant bits
random = (random & 0x3FFF_FFFF_FFFF_FFFF) | ((ulong)VARIANT << 62);

return random;
}

/// <summary>
Expand Down Expand Up @@ -313,6 +401,7 @@ public string ToBase32()
public string ToBase64()
{
byte[] bytes = new byte[SIZE];

TryWriteBytes(bytes);

return Convert.ToBase64String(bytes);
Expand Down
Loading

0 comments on commit da14a5d

Please sign in to comment.