Last active
November 26, 2023 22:21
-
-
Save nathan130200/5b76c689037ad4143622cd07270a0ac6 to your computer and use it in GitHub Desktop.
Java's UUID implementation in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
var sampleInput = "TestUuid/.NET"; | |
var expcetedUUIDStr = "e0198edc-d96b-3d2f-83ee-9a0614f93187"; | |
var uuidInstance = Uuid.FromName(Encoding.UTF8.GetBytes(sampleInput)); | |
var currentUUIDStr = uuidInstance.ToString(); | |
// test readonly array | |
var buf1 = uuidInstance.ToByteArray(); | |
var buf = uuidInstance.ToByteArray(); | |
buf[0] = 251; | |
var maxSize = Math.Min(expcetedUUIDStr.Length, currentUUIDStr.Length); | |
int i; | |
for (i = 0; i < maxSize; i++) | |
{ | |
var ch = currentUUIDStr[i]; | |
var ch2 = ch == expcetedUUIDStr[i] ? "==" : "!="; | |
Console.ForegroundColor = ch == expcetedUUIDStr[i] ? ConsoleColor.Green : ConsoleColor.Red; | |
Console.WriteLine("{0,-2} : {1} {2} {3}", i, ch, ch2, expcetedUUIDStr[i]); | |
} | |
Console.ForegroundColor = ConsoleColor.White; | |
Console.WriteLine(); | |
Console.WriteLine(" INPUT = " + expcetedUUIDStr); | |
Console.WriteLine(" OUTPUT = " + currentUUIDStr); | |
// compare same hash code based in java UUID's hashcode method | |
Console.WriteLine("HASH CODE = " + (uuidInstance.GetHashCode() == -1369106318)); | |
Console.ReadKey(true); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0 : e == e | |
1 : 0 == 0 | |
2 : 1 == 1 | |
3 : 9 == 9 | |
4 : 8 == 8 | |
5 : e == e | |
6 : d == d | |
7 : c == c | |
8 : - == - | |
9 : d == d | |
10 : 9 == 9 | |
11 : 6 == 6 | |
12 : b == b | |
13 : - == - | |
14 : 3 == 3 | |
15 : d == d | |
16 : 2 == 2 | |
17 : f == f | |
18 : - == - | |
19 : 8 == 8 | |
20 : 3 == 3 | |
21 : e == e | |
22 : e == e | |
23 : - == - | |
24 : 9 == 9 | |
25 : a == a | |
26 : 0 == 0 | |
27 : 6 == 6 | |
28 : 1 == 1 | |
29 : 4 == 4 | |
30 : f == f | |
31 : 9 == 9 | |
32 : 3 == 3 | |
33 : 1 == 1 | |
34 : 8 == 8 | |
35 : 7 == 7 | |
INPUT = e0198edc-d96b-3d2f-83ee-9a0614f93187 | |
OUTPUT = e0198edc-d96b-3d2f-83ee-9a0614f93187 | |
HASH CODE = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Buffers.Binary; | |
using System.Diagnostics; | |
using System.Security.Cryptography; | |
using System.Text; | |
namespace System; | |
public readonly struct Uuid : IParsable<Uuid>, IEquatable<Uuid> | |
{ | |
public static Uuid Empty => new(); | |
[DebuggerBrowsable(DebuggerBrowsableState.Never)] | |
private readonly byte[] _buffer = new byte[16]; | |
public Uuid() | |
{ | |
} | |
internal Uuid(byte[] buffer) | |
{ | |
if (buffer.Length != MD5.HashSizeInBytes) | |
throw new ArgumentOutOfRangeException(nameof(buffer), "Buffer size is not valid for a well-formed UUID"); | |
buffer.CopyTo(_buffer, 0); | |
} | |
public static Uuid NewRandom() | |
{ | |
var buf = RandomNumberGenerator.GetBytes(16); | |
buf[6] &= 0x0f; | |
buf[6] |= 0x40; | |
buf[8] &= 0x3f; | |
buf[8] |= 0x80; | |
return new Uuid(buf); | |
} | |
public static Uuid FromName(byte[] value) | |
{ | |
var buf = new byte[MD5.HashSizeInBytes]; | |
MD5.HashData(value, buf); | |
buf[6] &= 0x0f; | |
buf[6] |= 0x30; | |
buf[8] &= 0x3f; | |
buf[8] |= 0x80; | |
return new Uuid(buf); | |
} | |
static bool IParsable<Uuid>.TryParse(string? s, IFormatProvider? provider, out Uuid result) | |
=> TryParse(s, out result); | |
static Uuid IParsable<Uuid>.Parse(string? s, IFormatProvider? provider) | |
=> Parse(s); | |
/// <summary> | |
/// Tries to parse given string into a UUID instance. | |
/// </summary> | |
/// <param name="str">The string to parse.</param> | |
/// <param name="result">When this method returns, contains the result of successfully parsing s or an undefined value on failure.</param> | |
/// <returns>true if s was successfully parsed; otherwise, false.</returns> | |
public static bool TryParse(string? str, out Uuid result) | |
{ | |
result = Empty; | |
if (string.IsNullOrWhiteSpace(str)) | |
return false; | |
if (!str.Contains('-')) | |
return false; | |
var components = str.Split('-', StringSplitOptions.RemoveEmptyEntries) | |
.Select(str => Convert.ToInt64(str, 16)) | |
.ToArray(); | |
if (components.Length != 5) | |
return false; | |
var msb = components[0]; | |
msb <<= 16; | |
msb |= components[1]; | |
msb <<= 16; | |
msb |= components[2]; | |
var lsb = components[3]; | |
lsb <<= 48; | |
lsb |= components[4]; | |
result = new Uuid | |
{ | |
MostSignificantBits = msb, | |
LeastSignificantBits = lsb, | |
}; | |
return true; | |
} | |
/// <summary> | |
/// Parse given string into a UUID instance | |
/// </summary> | |
/// <param name="str">The string to parse.</param> | |
/// <returns> The result of parsing <paramref name="str"/>.</returns> | |
/// <exception cref="FormatException">When the given string is not in correct format.</exception> | |
/// <exception cref="ArgumentNullException">When the given string is null or empty.</exception> | |
public static Uuid Parse(string? str) | |
{ | |
ArgumentException.ThrowIfNullOrEmpty(str); | |
if (!TryParse(str, out Uuid result)) | |
throw new FormatException("Invalid UUID format."); | |
return result; | |
} | |
/// <summary> | |
/// Returns the most significant 64 bits of this UUID's 128 bit value. | |
/// </summary> | |
public long MostSignificantBits | |
{ | |
get => BinaryPrimitives.ReadInt64BigEndian(_buffer.AsSpan(0, 8)); | |
set => BinaryPrimitives.WriteInt64BigEndian(_buffer.AsSpan(0, 8), value); | |
} | |
/// <summary> | |
/// Returns the least significant 64 bits of this UUID's 128 bit value. | |
/// </summary> | |
public long LeastSignificantBits | |
{ | |
get => BinaryPrimitives.ReadInt64BigEndian(_buffer.AsSpan(8, 8)); | |
set => BinaryPrimitives.WriteInt64BigEndian(_buffer.AsSpan(8, 8), value); | |
} | |
/// <summary> | |
/// The version number associated with this <see cref="Uuid"/>. | |
/// </summary> | |
/// <remarks> | |
/// <list type="table"> | |
/// <item><c>1</c>: Time based</item> | |
/// <item><c>2</c>: DCE Security</item> | |
/// <item><c>3</c>: Name-based</item> | |
/// <item><c>4</c>: Randomly generated</item> | |
/// </list> | |
/// </remarks> | |
public byte Version | |
=> (byte)(_buffer[6] & 0x0f); | |
/// <summary> | |
/// The variant number associated with this <see cref="Uuid"/>. | |
/// The variant number describes the layout of the <see cref="Uuid"/>. | |
/// </summary> | |
/// <remarks> | |
/// <list type="table"> | |
/// <item><c>0</c>: Reserved for NCS backward compatibility</item> | |
/// <item><c>1</c>: The Leach-Salz variant (used by this class)</item> | |
/// <item><c>2</c>: Reserved, Microsoft Corporation backward compatibility</item> | |
/// <item><c>3</c>: Reserved for future definition</item> | |
/// </list> | |
/// </remarks> | |
public byte Variant | |
{ | |
get | |
{ | |
var variantBits = _buffer[8] >> 5 & 0x07; | |
if ((variantBits & 0x04) == 0) | |
return 0; | |
else if ((variantBits & 0x02) == 0) | |
return 1; | |
else if ((variantBits & 0x01) == 0) | |
return 2; | |
else | |
return 3; | |
} | |
} | |
/// <summary> | |
/// Returns a string representation of this <see cref="Uuid"/> | |
/// </summary> | |
public override string ToString() | |
{ | |
var sb = new StringBuilder(Convert.ToHexString(_buffer).ToLowerInvariant()); | |
sb.Insert(8, '-'); | |
sb.Insert(13, '-'); | |
sb.Insert(18, '-'); | |
sb.Insert(23, '-'); | |
return sb.ToString(); | |
} | |
public override bool Equals(object? obj) | |
=> obj is Uuid other && Equals(other); | |
public bool Equals(Uuid other) | |
{ | |
if (other._buffer?.Length != 16) | |
return false; | |
for (int i = 0; i < MD5.HashSizeInBytes; i++) | |
{ | |
if (_buffer[i] != other._buffer[i]) | |
return false; | |
} | |
return true; | |
} | |
public readonly override int GetHashCode() | |
{ | |
long val = MostSignificantBits ^ LeastSignificantBits; | |
return ((int)(val >> 32)) ^ (int)val; | |
} | |
public readonly byte[] ToByteArray() | |
{ | |
var result = new byte[MD5.HashSizeInBytes]; | |
_buffer.CopyTo(result, 0); | |
return result; | |
} | |
public static bool operator ==(Uuid lhs, Uuid rhs) | |
=> lhs.Equals(rhs); | |
public static bool operator !=(Uuid lhs, Uuid rhs) | |
=> !(lhs == rhs); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment