import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:libac_flutter/nbt/Stream.dart'; class UUID { late final int LSB; late final int MSB; UUID(int msb, int lsb) { MSB = msb.abs(); LSB = lsb.abs(); } UUID.fromBytes(List bytes) { int msb = 0; int lsb = 0; int i = 0; for (i = 0; i < 8; ++i) { msb = msb << 8 | bytes[i] & 255; } for (i = 8; i < 16; ++i) { lsb = lsb << 8 | bytes[i] & 255; } this.MSB = msb; this.LSB = lsb; } static final UUID ZERO = UUID.generate(0); /// Validates whether the given [uuid] is a valid UUID. static bool validate(String uuid) { if (uuid.length == ((16 * 2) + 4)) { return true; // Likely is true. This is just a surface level check } else return false; } /// Parses the given [uuid] string and returns a UUID object. static UUID parse(String uuid) { if (validate(uuid)) { final segments = uuid.split('-'); if (segments.length != 5) { throw const FormatException('Invalid UUID format'); } final msbString = segments.sublist(0, 3).join(''); final lsbString = segments.sublist(3, 5).join(''); final msb = int.parse(msbString, radix: 16); final lsb = int.parse(lsbString, radix: 16); return UUID(msb, lsb); } else return UUID.ZERO; } @override String toString() { final msbHex = MSB.toRadixString(16).padLeft(16, '0'); final lsbHex = LSB.toRadixString(16).padLeft(16, '0'); return '${msbHex.substring(0, 8)}-${msbHex.substring(8, 12)}-${msbHex.substring(12, 16)}-${lsbHex.substring(0, 4)}-${lsbHex.substring(4, 16)}'; } /// Returns the Most Significant Bits (MSB) long value of the UUID. int getMostSignificantBits() => MSB; /// Returns the Least Significant Bits (LSB) long value of the UUID. int getLeastSignificantBits() => LSB; /// Factory method to generate UUID of the specific version. factory UUID.generate(int version, {List? parameters}) { List params = []; if (parameters == null) { if (version != 4) { return UUID.generate(4); } } else params = parameters!; switch (version) { case 0: return UUID(0, 0); case 3: { if (params.length != 2) throw Exception( "UUID v3 requires two parameters, [namespace,name]"); String namespace = params[0] as String; String name = params[1] as String; ByteLayer layer = ByteLayer(); if (!namespace.isEmpty) { final namespaceBytes = utf8.encode(namespace); layer.writeBytes(namespaceBytes); } if (!name.isEmpty) { final nameBytes = utf8.encode(name); layer.writeBytes(nameBytes); } var bytes = md5.convert(List.from(layer.bytes)).bytes; layer.clear(); layer.writeBytes(bytes); layer.unsetSetBit(6, 0x0F, 0x30); print( "Existing bit at position 8: ${layer.getBit(8).toRadixString(2)}:${layer.getBit(8)}"); layer.unsetSetBit(8, 0x3F, 0x80); print( "New bit at position 8: ${layer.getBit(8).toRadixString(2)}:${layer.getBit(8)}"); layer.resetPosition(); String csv = ""; for (int byte in layer.readBytes(16)) { csv += "0x${byte.toRadixString(16)}, "; } print("BYTES CSV: ${csv}"); layer.resetPosition(); var msb = layer.readUnsignedLong(); var lsb = layer.readUnsignedLong(); if (msb < 0) print("Most significant bit is negative! ${msb}"); if (lsb < 0) print("Least significant bit is negative! ${lsb}"); print("Forming UUID using MSB-LSB - ${msb} - ${lsb}"); layer.resetPosition(); return UUID.fromBytes(layer.readBytes(16)); return UUID(msb, lsb); } case 4: { ByteLayer layer = ByteLayer(); final random = Random.secure(); layer.writeLong( (random.nextInt(0xFFFFFFFF) << 32) | random.nextInt(0xFFFFFFFF)); layer.writeLong( (random.nextInt(0xFFFFFFFF) << 32) | random.nextInt(0xFFFFFFFF)); layer.unsetSetBit(6, 0x0F, 0x40); layer.unsetSetBit(8, 0x3F, 0x80); layer.resetPosition(); return UUID(layer.readUnsignedLong(), layer.readUnsignedLong()); } case 5: { ByteLayer layer = ByteLayer(); if (params.length != 2) throw Exception( "UUID v5 requires two parameters, [namespace,name]"); String namespace = params[0] as String; String name = params[1] as String; if (!namespace.isEmpty) { final namespaceBytes = utf8.encode(namespace); layer.writeBytes(namespaceBytes); } if (!name.isEmpty) { final nameBytes = utf8.encode(name); layer.writeBytes(nameBytes); } final hashBytes = sha1.convert(List.from(layer.bytes)).bytes; layer.clear(); layer.writeBytes(hashBytes); layer.unsetSetBit(6, 0x0F, 0x50); layer.unsetSetBit(8, 0x3F, 0x80); layer.resetPosition(); return UUID(layer.readUnsignedLong(), layer.readUnsignedLong()); } default: throw ArgumentError('Unsupported UUID version: $version'); } } }