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(); } } }