mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-28 17:04:34 +00:00
243 lines
5.3 KiB
Odin
243 lines
5.3 KiB
Odin
package uuid
|
|
|
|
import "base:runtime"
|
|
import "core:time"
|
|
|
|
/*
|
|
Convert a string to a UUID.
|
|
|
|
Inputs:
|
|
- str: A string in the 8-4-4-4-12 format.
|
|
|
|
Returns:
|
|
- id: The converted identifier, or `nil` if there is an error.
|
|
- error: A description of the error, or `nil` if successful.
|
|
*/
|
|
read :: proc "contextless" (str: string) -> (id: Identifier, error: Read_Error) #no_bounds_check {
|
|
// Only exact-length strings are acceptable.
|
|
if len(str) != EXPECTED_LENGTH {
|
|
return {}, .Invalid_Length
|
|
}
|
|
|
|
// Check ahead to see if the separators are in the right places.
|
|
if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
|
|
return {}, .Invalid_Separator
|
|
}
|
|
|
|
read_nibble :: proc "contextless" (nibble: u8) -> u8 {
|
|
switch nibble {
|
|
case '0' ..= '9':
|
|
return nibble - '0'
|
|
case 'A' ..= 'F':
|
|
return nibble - 'A' + 10
|
|
case 'a' ..= 'f':
|
|
return nibble - 'a' + 10
|
|
case:
|
|
// Return an error value.
|
|
return 0xFF
|
|
}
|
|
}
|
|
|
|
index := 0
|
|
octet_index := 0
|
|
|
|
CHUNKS :: [5]int{8, 4, 4, 4, 12}
|
|
|
|
for chunk in CHUNKS {
|
|
for i := index; i < index + chunk; i += 2 {
|
|
high := read_nibble(str[i])
|
|
low := read_nibble(str[i + 1])
|
|
|
|
if high | low > 0xF {
|
|
return {}, .Invalid_Hexadecimal
|
|
}
|
|
|
|
id[octet_index] = low | high << 4
|
|
octet_index += 1
|
|
}
|
|
|
|
index += chunk + 1
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Get the version of a UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- number: The version number.
|
|
*/
|
|
version :: proc "contextless" (id: Identifier) -> (number: int) #no_bounds_check {
|
|
return cast(int)(id[VERSION_BYTE_INDEX] & 0xF0 >> 4)
|
|
}
|
|
|
|
/*
|
|
Get the variant of a UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- variant: The variant type.
|
|
*/
|
|
variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bounds_check {
|
|
switch {
|
|
case id[VARIANT_BYTE_INDEX] & 0x80 == 0:
|
|
return .Reserved_Apollo_NCS
|
|
case id[VARIANT_BYTE_INDEX] & 0xC0 == 0x80:
|
|
return .RFC_4122
|
|
case id[VARIANT_BYTE_INDEX] & 0xE0 == 0xC0:
|
|
return .Reserved_Microsoft_COM
|
|
case id[VARIANT_BYTE_INDEX] & 0xF0 == 0xE0:
|
|
return .Reserved_Future
|
|
case:
|
|
return .Unknown
|
|
}
|
|
}
|
|
|
|
/*
|
|
Get the clock sequence of a version 1 or version 6 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- clock_seq: The 14-bit clock sequence field.
|
|
*/
|
|
clock_seq :: proc "contextless" (id: Identifier) -> (clock_seq: u16) {
|
|
return cast(u16)id[9] | cast(u16)id[8] & 0x3F << 8
|
|
}
|
|
|
|
/*
|
|
Get the node of a version 1 or version 6 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- node: The 48-bit spatially unique identifier.
|
|
*/
|
|
node :: proc "contextless" (id: Identifier) -> (node: [6]u8) {
|
|
mutable_id := id
|
|
runtime.mem_copy_non_overlapping(&node, &mutable_id[10], 6)
|
|
return
|
|
}
|
|
|
|
/*
|
|
Get the raw timestamp of a version 1 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
|
*/
|
|
raw_time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
|
timestamp_octets: [8]u8
|
|
|
|
timestamp_octets[0] = id[0]
|
|
timestamp_octets[1] = id[1]
|
|
timestamp_octets[2] = id[2]
|
|
timestamp_octets[3] = id[3]
|
|
timestamp_octets[4] = id[4]
|
|
timestamp_octets[5] = id[5]
|
|
|
|
timestamp_octets[6] = id[6] << 4 | id[7] >> 4
|
|
timestamp_octets[7] = id[7] & 0xF
|
|
|
|
return cast(u64)transmute(u64le)timestamp_octets
|
|
}
|
|
|
|
|
|
/*
|
|
Get the timestamp of a version 1 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- timestamp: The timestamp of the UUID.
|
|
*/
|
|
time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
|
return time.from_nanoseconds(cast(i64)(raw_time_v1(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
|
}
|
|
|
|
/*
|
|
Get the raw timestamp of a version 6 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
|
*/
|
|
raw_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 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: time.Time) {
|
|
return time.from_nanoseconds(cast(i64)(raw_time_v6(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
|
}
|
|
|
|
/*
|
|
Get the raw timestamp of a version 7 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
|
*/
|
|
raw_time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
|
time_bits := transmute(u128be)id & VERSION_7_TIME_MASK
|
|
return cast(u64)(time_bits >> VERSION_7_TIME_SHIFT)
|
|
}
|
|
|
|
/*
|
|
Get the timestamp of a version 7 UUID.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
|
*/
|
|
time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
|
return time.from_nanoseconds(cast(i64)raw_time_v7(id) * 1e6)
|
|
}
|
|
|
|
/*
|
|
Get the 12-bit counter value of a version 7 UUID.
|
|
|
|
The UUID must have been generated with a counter, otherwise this procedure will
|
|
return random bits.
|
|
|
|
Inputs:
|
|
- id: The identifier.
|
|
|
|
Returns:
|
|
- counter: The 12-bit counter value.
|
|
*/
|
|
counter_v7 :: proc "contextless" (id: Identifier) -> (counter: u16) {
|
|
counter_bits := transmute(u128be)id & VERSION_7_COUNTER_MASK
|
|
return cast(u16)(counter_bits >> VERSION_7_COUNTER_SHIFT)
|
|
}
|