# Note: The code might not be as pretty it could be, since it's written
# in a way that maximizes performance. Methods are inlined and loops are avoided.
extends Node
const BYTE_MASK: int = 0b11111111
static func uuidbin():
randomize()
# 16 random bytes with the bytes on index 6 and 8 modified
return [
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
((randi() & BYTE_MASK) & 0x0f) | 0x40,
randi() & BYTE_MASK,
((randi() & BYTE_MASK) & 0x3f) | 0x80,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
randi() & BYTE_MASK,
]
static func uuidbinrng(rng: RandomNumberGenerator):
rng.randomize()
return [
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
((rng.randi() & BYTE_MASK) & 0x0f) | 0x40,
rng.randi() & BYTE_MASK,
((rng.randi() & BYTE_MASK) & 0x3f) | 0x80,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
rng.randi() & BYTE_MASK,
]
static func v4():
# 16 random bytes with the bytes on index 6 and 8 modified
var b = uuidbin()
return (
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
% [
# low
b[0],
b[1],
b[2],
b[3],
# mid
b[4],
b[5],
# hi
b[6],
b[7],
# clock
b[8],
b[9],
# clock
b[10],
b[11],
b[12],
b[13],
b[14],
b[15]
]
)
static func v4_rng(rng: RandomNumberGenerator):
# 16 random bytes with the bytes on index 6 and 8 modified
var b = uuidbinrng(rng)
return (
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
% [
# low
b[0],
b[1],
b[2],
b[3],
# mid
b[4],
b[5],
# hi
b[6],
b[7],
# clock
b[8],
b[9],
# clock
b[10],
b[11],
b[12],
b[13],
b[14],
b[15]
]
)
var _uuid: Array
func _init(rng := RandomNumberGenerator.new()) -> void:
_uuid = uuidbinrng(rng)
func as_array() -> Array:
return _uuid.duplicate()
func as_dict(big_endian := true) -> Dictionary:
if big_endian:
return {
"low": (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8) + _uuid[3],
"mid": (_uuid[4] << 8) + _uuid[5],
"hi": (_uuid[6] << 8) + _uuid[7],
"clock": (_uuid[8] << 8) + _uuid[9],
"node":
(
(_uuid[10] << 40)
+ (_uuid[11] << 32)
+ (_uuid[12] << 24)
+ (_uuid[13] << 16)
+ (_uuid[14] << 8)
+ _uuid[15]
)
}
else:
return {
"low": _uuid[0] + (_uuid[1] << 8) + (_uuid[2] << 16) + (_uuid[3] << 24),
"mid": _uuid[4] + (_uuid[5] << 8),
"hi": _uuid[6] + (_uuid[7] << 8),
"clock": _uuid[8] + (_uuid[9] << 8),
"node":
(
_uuid[10]
+ (_uuid[11] << 8)
+ (_uuid[12] << 16)
+ (_uuid[13] << 24)
+ (_uuid[14] << 32)
+ (_uuid[15] << 40)
)
}
func as_string() -> String:
return (
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
% [
# low
_uuid[0],
_uuid[1],
_uuid[2],
_uuid[3],
# mid
_uuid[4],
_uuid[5],
# hi
_uuid[6],
_uuid[7],
# clock
_uuid[8],
_uuid[9],
# node
_uuid[10],
_uuid[11],
_uuid[12],
_uuid[13],
_uuid[14],
_uuid[15]
]
)
func is_equal(other) -> bool:
# Godot Engine compares Array recursively
# There's no need for custom comparison here.
return _uuid == other._uuid