Add version 6 UUID generation

This commit is contained in:
Feoramund
2024-06-22 12:54:39 -04:00
parent 525bfca4ef
commit 9866b54d59
3 changed files with 96 additions and 1 deletions
+46
View File
@@ -204,6 +204,52 @@ generate_v5 :: proc {
generate_v5_string,
}
/*
Generate a version 6 UUID.
Inputs:
- clock_seq: The clock sequence from version 1, now made optional.
If unspecified, it will be replaced with random bits.
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
If one is not provided or available, 48 bits of random state will take its place.
Returns:
- result: The generated UUID.
*/
generate_v6 :: proc(clock_seq: Maybe(u16) = nil, node: Maybe([6]u8) = nil) -> (result: Identifier) {
unix_time_in_hns_intervals := time.to_unix_nanoseconds(time.now()) / 100
timestamp := cast(u128be)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
result |= transmute(Identifier)(timestamp & 0x0FFFFFFF_FFFFF000 << 68)
result |= transmute(Identifier)(timestamp & 0x00000000_00000FFF << 64)
if realized_clock_seq, ok := clock_seq.?; ok {
assert(realized_clock_seq <= 0x3FFF, "The clock sequence can only hold 14 bits of data, therefore no number greater than 16,383.")
result[8] |= cast(u8)(realized_clock_seq & 0x3F00 >> 8)
result[9] = cast(u8)realized_clock_seq
} else {
temporary: [2]u8
bytes_generated := rand.read(temporary[:])
assert(bytes_generated == 2, "RNG failed to generate 2 bytes for UUID v1.")
result[8] |= cast(u8)temporary[0] & 0x3F
result[9] = cast(u8)temporary[1]
}
if realized_node, ok := node.?; ok {
mutable_node := realized_node
mem.copy_non_overlapping(&result[10], &mutable_node[0], 6)
} else {
bytes_generated := rand.read(result[10:])
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
}
result[VERSION_BYTE_INDEX] |= 0x60
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 7 UUID.
+19 -1
View File
@@ -99,7 +99,7 @@ variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bo
}
/*
Get the clock sequence of a version 1 UUID.
Get the clock sequence of a version 1 or version 6 UUID.
Inputs:
- id: The identifier.
@@ -151,6 +151,24 @@ time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
return cast(u64)transmute(u64le)timestamp_octets
}
/*
Get the timestamp of a version 6 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
*/
time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
temporary := transmute(u128be)id
timestamp |= cast(u64)(temporary & 0xFFFFFFFF_FFFF0000_00000000_00000000 >> 68)
timestamp |= cast(u64)(temporary & 0x00000000_00000FFF_00000000_00000000 >> 64)
return timestamp
}
/*
Get the timestamp of a version 7 UUID.
@@ -11,6 +11,7 @@ test_version_and_variant :: proc(t: ^testing.T) {
v3 := uuid.generate_v3(uuid.Namespace_DNS, "")
v4 := uuid.generate_v4()
v5 := uuid.generate_v5(uuid.Namespace_DNS, "")
v6 := uuid.generate_v6()
v7 := uuid.generate_v7()
testing.expect_value(t, uuid.version(v1), 1)
@@ -21,6 +22,8 @@ test_version_and_variant :: proc(t: ^testing.T) {
testing.expect_value(t, uuid.variant(v4), uuid.Variant_Type.RFC_4122)
testing.expect_value(t, uuid.version(v5), 5)
testing.expect_value(t, uuid.variant(v5), uuid.Variant_Type.RFC_4122)
testing.expect_value(t, uuid.version(v6), 6)
testing.expect_value(t, uuid.variant(v6), uuid.Variant_Type.RFC_4122)
testing.expect_value(t, uuid.version(v7), 7)
testing.expect_value(t, uuid.variant(v7), uuid.Variant_Type.RFC_4122)
}
@@ -84,6 +87,34 @@ test_v1 :: proc(t: ^testing.T) {
testing.expect(t, time_bits_c > time_bits_a, "The time bits on the later-generated v1 UUID are lesser than the earlier UUID.")
}
@(test)
test_v6 :: proc(t: ^testing.T) {
CLOCK :: 0x3A1A
v6_a := uuid.generate_v6(CLOCK)
time.sleep(10 * time.Millisecond)
v6_b := uuid.generate_v6(CLOCK)
time.sleep(10 * time.Millisecond)
v6_c := uuid.generate_v6(CLOCK)
testing.expect_value(t, uuid.clock_seq(v6_a), CLOCK)
time_bits_a := uuid.time_v6(v6_a)
time_bits_b := uuid.time_v6(v6_b)
time_bits_c := uuid.time_v6(v6_c)
time_a := time.Time { _nsec = cast(i64)((time_bits_a - uuid.HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100) }
time_b := time.Time { _nsec = cast(i64)((time_bits_b - uuid.HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100) }
time_c := time.Time { _nsec = cast(i64)((time_bits_c - uuid.HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100) }
log.debugf("A: %02x, %i, %v", v6_a, time_bits_a, time_a)
log.debugf("B: %02x, %i, %v", v6_b, time_bits_b, time_b)
log.debugf("C: %02x, %i, %v", v6_c, time_bits_c, time_c)
testing.expect(t, time_bits_b > time_bits_a, "The time bits on the later-generated v6 UUID are lesser than the earlier UUID.")
testing.expect(t, time_bits_c > time_bits_b, "The time bits on the later-generated v6 UUID are lesser than the earlier UUID.")
testing.expect(t, time_bits_c > time_bits_a, "The time bits on the later-generated v6 UUID are lesser than the earlier UUID.")
}
@(test)
test_v7 :: proc(t: ^testing.T) {
v7_a := uuid.generate_v7()