146 lines
5.2 KiB
C#
146 lines
5.2 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Security.Cryptography;
|
|
using System.Linq;
|
|
|
|
namespace LibAC.Utilities
|
|
{
|
|
public class UUID
|
|
{
|
|
private readonly byte[] _bytes;
|
|
|
|
public static readonly UUID ZERO = UUID.Generate(0);
|
|
|
|
public UUID(byte[] bytes)
|
|
{
|
|
_bytes = bytes;
|
|
}
|
|
|
|
public static bool Validate(string uuid)
|
|
{
|
|
return uuid.Length == (16 * 2) + 4; // Basic length check
|
|
}
|
|
|
|
public static UUID Parse(string uuid)
|
|
{
|
|
if (Validate(uuid))
|
|
{
|
|
string[] segments = uuid.Split('-');
|
|
if (segments.Length != 5)
|
|
{
|
|
throw new FormatException("Invalid UUID format");
|
|
}
|
|
|
|
string msbString = string.Join("", segments.Take(3));
|
|
string lsbString = string.Join("", segments.Skip(3));
|
|
|
|
byte[] byteArray = new byte[16];
|
|
int i = 0;
|
|
for (i = 0; i < msbString.Length; i += 2)
|
|
{
|
|
byteArray[i / 2] = Convert.ToByte(msbString.Substring(i, 2), 16);
|
|
}
|
|
|
|
for (i = 0; i < lsbString.Length; i += 2)
|
|
{
|
|
byteArray[msbString.Length / 2 + i / 2] = Convert.ToByte(lsbString.Substring(i, 2), 16);
|
|
}
|
|
|
|
return new UUID(byteArray);
|
|
}
|
|
else
|
|
{
|
|
return ZERO;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder hexBuilder = new StringBuilder();
|
|
foreach (byte b in _bytes)
|
|
{
|
|
hexBuilder.Append(b.ToString("x2"));
|
|
}
|
|
|
|
return $"{hexBuilder.ToString().Substring(0, 8)}-{hexBuilder.ToString().Substring(8, 4)}-{hexBuilder.ToString().Substring(12, 4)}-{hexBuilder.ToString().Substring(16, 4)}-{hexBuilder.ToString().Substring(20, 12)}";
|
|
}
|
|
|
|
public static UUID Generate(int version, params object[] parameters)
|
|
{
|
|
if (version != 4 && version != 0 && version != 3 && version != 5)
|
|
{
|
|
return Generate(4);
|
|
}
|
|
|
|
byte[] bytes0 = new byte[16];
|
|
|
|
|
|
switch (version)
|
|
{
|
|
case 0:
|
|
return new UUID(bytes0);
|
|
case 3:
|
|
if (parameters.Length != 2)
|
|
{
|
|
throw new ArgumentException("UUID v3 requires two parameters: [namespace, name]");
|
|
}
|
|
|
|
string namespaceStr = parameters[0] as string;
|
|
string nameStr = parameters[1] as string;
|
|
|
|
using (MD5 md5 = MD5.Create())
|
|
{
|
|
byte[] namespaceBytes = Encoding.UTF8.GetBytes(namespaceStr);
|
|
byte[] nameBytes = Encoding.UTF8.GetBytes(nameStr);
|
|
byte[] combined = namespaceBytes.Concat(nameBytes).ToArray();
|
|
|
|
byte[] hash = md5.ComputeHash(combined);
|
|
|
|
Array.Copy(hash, 0, bytes0, 0, 16);
|
|
bytes0[6] = (byte)(bytes0[6] & 0x0F | 0x30); // Set version to 3
|
|
bytes0[8] = (byte)(bytes0[8] & 0x3F | 0x80); // Set variant to RFC4122
|
|
return new UUID(bytes0);
|
|
}
|
|
case 4:
|
|
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
|
|
{
|
|
byte[] bytes4 = new byte[16];
|
|
rng.GetBytes(bytes4);
|
|
bytes4[6] = (byte)(bytes4[6] & 0x0F | 0x40); // Set version to 4
|
|
bytes4[8] = (byte)(bytes4[8] & 0x3F | 0x80); // Set variant to RFC4122
|
|
return new UUID(bytes4);
|
|
}
|
|
case 5:
|
|
if (parameters.Length != 2)
|
|
{
|
|
throw new ArgumentException("UUID v5 requires two parameters: [namespace, name]");
|
|
}
|
|
|
|
string namespaceStr5 = parameters[0] as string;
|
|
string nameStr5 = parameters[1] as string;
|
|
|
|
using (SHA1 sha1 = SHA1.Create())
|
|
{
|
|
byte[] namespaceBytes5 = Encoding.UTF8.GetBytes(namespaceStr5);
|
|
byte[] nameBytes5 = Encoding.UTF8.GetBytes(nameStr5);
|
|
byte[] combined5 = namespaceBytes5.Concat(nameBytes5).ToArray();
|
|
|
|
byte[] hash = sha1.ComputeHash(combined5);
|
|
|
|
byte[] bytes5 = new byte[16];
|
|
Array.Copy(hash, 0, bytes5, 0, 16);
|
|
bytes5[6] = (byte)(bytes5[6] & 0x0F | 0x50); // Set version to 5
|
|
bytes5[8] = (byte)(bytes5[8] & 0x3F | 0x80); // Set variant to RFC4122
|
|
return new UUID(bytes5);
|
|
}
|
|
default:
|
|
throw new ArgumentException($"Unsupported UUID version: {version}");
|
|
}
|
|
}
|
|
|
|
public byte[] GetBytes()
|
|
{
|
|
return _bytes.Take(16).ToArray();
|
|
}
|
|
}
|
|
}
|