|
| 1 | +using System.Buffers.Binary; |
| 2 | +using System.Runtime.CompilerServices; |
| 3 | +using System.Runtime.Intrinsics; |
| 4 | +using System.Runtime.InteropServices; |
| 5 | +using System; |
| 6 | + |
| 7 | +namespace DaanV2.UUID; |
| 8 | + |
| 9 | +public static partial class V2 { |
| 10 | + /// <summary> |
| 11 | + /// Generates a Version 2 (DCE Security) UUID using the current time, UID, and MAC address. |
| 12 | + /// </summary> |
| 13 | + [MethodImpl(MethodImplOptions.AggressiveOptimization)] |
| 14 | + public static UUID Generate() { |
| 15 | + // Use current time, current UID, and MAC address |
| 16 | + return Generate(DateTime.UtcNow, GetCurrentUid(), V1.GetMacAddressBytes()); |
| 17 | + } |
| 18 | + |
| 19 | + /// <summary> |
| 20 | + /// Generates a Version 2 UUID with custom timestamp, UID, and MAC address. |
| 21 | + /// </summary> |
| 22 | + [MethodImpl(MethodImplOptions.AggressiveOptimization)] |
| 23 | + public static UUID Generate(DateTime timestamp, uint localIdentifier, ReadOnlySpan<byte> macAddress, byte domain = 0) { |
| 24 | + ulong uuidTimestamp = V1.TimeStamp(timestamp); |
| 25 | + |
| 26 | + Span<byte> data = stackalloc byte[16]; |
| 27 | + |
| 28 | + // Write node (MAC address) |
| 29 | + macAddress.CopyTo(data[10..]); |
| 30 | + |
| 31 | + // Split timestamp |
| 32 | + uint timeLow = (uint)(uuidTimestamp & 0xFFFFFFFF); |
| 33 | + ushort timeMid = (ushort)((uuidTimestamp >> 32) & 0xFFFF); |
| 34 | + ushort timeHi = (ushort)((uuidTimestamp >> 48) & 0x0FFF); // 12 bits for time_hi |
| 35 | + ushort timeHiAndVersion = (ushort)(timeHi | (_Version << 12)); // Set version 2 |
| 36 | + |
| 37 | + BinaryPrimitives.WriteUInt32BigEndian(data, timeLow); |
| 38 | + BinaryPrimitives.WriteUInt16BigEndian(data[4..], timeMid); |
| 39 | + BinaryPrimitives.WriteUInt16BigEndian(data[6..], timeHiAndVersion); |
| 40 | + |
| 41 | + // Per RFC 4122 DCE Security UUID: |
| 42 | + // data[8]: high 2 bits = variant, low 6 bits = high 6 bits of local identifier |
| 43 | + // data[9]: domain (per spec), or low 8 bits of local identifier if domain not used |
| 44 | + data[8] = (byte)(_Variant | ((localIdentifier >> 8) & 0x3F)); |
| 45 | + data[9] = domain; // Store domain in data[9] |
| 46 | + |
| 47 | + var uuid = Vector128.Create<Byte>(data); |
| 48 | + return new UUID(uuid); |
| 49 | + } |
| 50 | + |
| 51 | + /// <summary> |
| 52 | + /// Gets the current user's UID (Linux only). Returns 0 if not available. |
| 53 | + /// </summary> |
| 54 | + private static uint GetCurrentUid() { |
| 55 | + try { |
| 56 | + return (uint)System.Convert.ToInt32(Environment.GetEnvironmentVariable("UID") ?? "0"); |
| 57 | + } catch { |
| 58 | + return 0; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + /// <summary> |
| 63 | + /// Generates a Version 2 UUID from a byte source (not standard, but for API compatibility). |
| 64 | + /// </summary> |
| 65 | + [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] |
| 66 | + public static UUID Generate(ReadOnlySpan<Byte> source) { |
| 67 | + // Not standard for V2, but for API compatibility: hash and overlay version/variant |
| 68 | + Span<byte> data = stackalloc byte[16]; |
| 69 | + System.Security.Cryptography.MD5.HashData(source, data); |
| 70 | + // Set version and variant |
| 71 | + data[6] = (byte)((data[6] & 0x0F) | (_Version << 4)); |
| 72 | + data[8] = (byte)((data[8] & 0x3F) | _Variant); |
| 73 | + |
| 74 | + var uuid = Vector128.Create<Byte>(data); |
| 75 | + return new UUID(uuid); |
| 76 | + } |
| 77 | +} |
0 commit comments