Skip to content

Instantly share code, notes, and snippets.

@nathan130200
Last active November 26, 2023 22:21
Show Gist options
  • Save nathan130200/5b76c689037ad4143622cd07270a0ac6 to your computer and use it in GitHub Desktop.
Save nathan130200/5b76c689037ad4143622cd07270a0ac6 to your computer and use it in GitHub Desktop.
Java's UUID implementation in C#
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);
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
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