reading.odin 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package uuid
  2. import "base:runtime"
  3. import "core:time"
  4. /*
  5. Convert a string to a UUID.
  6. Inputs:
  7. - str: A string in the 8-4-4-4-12 format.
  8. Returns:
  9. - id: The converted identifier, or `nil` if there is an error.
  10. - error: A description of the error, or `nil` if successful.
  11. */
  12. read :: proc "contextless" (str: string) -> (id: Identifier, error: Read_Error) #no_bounds_check {
  13. // Only exact-length strings are acceptable.
  14. if len(str) != EXPECTED_LENGTH {
  15. return {}, .Invalid_Length
  16. }
  17. // Check ahead to see if the separators are in the right places.
  18. if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
  19. return {}, .Invalid_Separator
  20. }
  21. read_nibble :: proc "contextless" (nibble: u8) -> u8 {
  22. switch nibble {
  23. case '0' ..= '9':
  24. return nibble - '0'
  25. case 'A' ..= 'F':
  26. return nibble - 'A' + 10
  27. case 'a' ..= 'f':
  28. return nibble - 'a' + 10
  29. case:
  30. // Return an error value.
  31. return 0xFF
  32. }
  33. }
  34. index := 0
  35. octet_index := 0
  36. CHUNKS :: [5]int{8, 4, 4, 4, 12}
  37. for chunk in CHUNKS {
  38. for i := index; i < index + chunk; i += 2 {
  39. high := read_nibble(str[i])
  40. low := read_nibble(str[i + 1])
  41. if high | low > 0xF {
  42. return {}, .Invalid_Hexadecimal
  43. }
  44. id[octet_index] = low | high << 4
  45. octet_index += 1
  46. }
  47. index += chunk + 1
  48. }
  49. return
  50. }
  51. /*
  52. Get the version of a UUID.
  53. Inputs:
  54. - id: The identifier.
  55. Returns:
  56. - number: The version number.
  57. */
  58. version :: proc "contextless" (id: Identifier) -> (number: int) #no_bounds_check {
  59. return cast(int)(id[VERSION_BYTE_INDEX] & 0xF0 >> 4)
  60. }
  61. /*
  62. Get the variant of a UUID.
  63. Inputs:
  64. - id: The identifier.
  65. Returns:
  66. - variant: The variant type.
  67. */
  68. variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bounds_check {
  69. switch {
  70. case id[VARIANT_BYTE_INDEX] & 0x80 == 0:
  71. return .Reserved_Apollo_NCS
  72. case id[VARIANT_BYTE_INDEX] & 0xC0 == 0x80:
  73. return .RFC_4122
  74. case id[VARIANT_BYTE_INDEX] & 0xE0 == 0xC0:
  75. return .Reserved_Microsoft_COM
  76. case id[VARIANT_BYTE_INDEX] & 0xF0 == 0xE0:
  77. return .Reserved_Future
  78. case:
  79. return .Unknown
  80. }
  81. }
  82. /*
  83. Get the clock sequence of a version 1 or version 6 UUID.
  84. Inputs:
  85. - id: The identifier.
  86. Returns:
  87. - clock_seq: The 14-bit clock sequence field.
  88. */
  89. clock_seq :: proc "contextless" (id: Identifier) -> (clock_seq: u16) {
  90. return cast(u16)id[9] | cast(u16)id[8] & 0x3F << 8
  91. }
  92. /*
  93. Get the node of a version 1 or version 6 UUID.
  94. Inputs:
  95. - id: The identifier.
  96. Returns:
  97. - node: The 48-bit spatially unique identifier.
  98. */
  99. node :: proc "contextless" (id: Identifier) -> (node: [6]u8) {
  100. mutable_id := id
  101. runtime.mem_copy_non_overlapping(&node, &mutable_id[10], 6)
  102. return
  103. }
  104. /*
  105. Get the raw timestamp of a version 1 UUID.
  106. Inputs:
  107. - id: The identifier.
  108. Returns:
  109. - timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
  110. */
  111. raw_time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
  112. timestamp_octets: [8]u8
  113. timestamp_octets[0] = id[0]
  114. timestamp_octets[1] = id[1]
  115. timestamp_octets[2] = id[2]
  116. timestamp_octets[3] = id[3]
  117. timestamp_octets[4] = id[4]
  118. timestamp_octets[5] = id[5]
  119. timestamp_octets[6] = id[6] << 4 | id[7] >> 4
  120. timestamp_octets[7] = id[7] & 0xF
  121. return cast(u64)transmute(u64le)timestamp_octets
  122. }
  123. /*
  124. Get the timestamp of a version 1 UUID.
  125. Inputs:
  126. - id: The identifier.
  127. Returns:
  128. - timestamp: The timestamp of the UUID.
  129. */
  130. time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
  131. return time.from_nanoseconds(cast(i64)(raw_time_v1(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
  132. }
  133. /*
  134. Get the raw timestamp of a version 6 UUID.
  135. Inputs:
  136. - id: The identifier.
  137. Returns:
  138. - timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
  139. */
  140. raw_time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
  141. temporary := transmute(u128be)id
  142. timestamp |= cast(u64)(temporary & 0xFFFFFFFF_FFFF0000_00000000_00000000 >> 68)
  143. timestamp |= cast(u64)(temporary & 0x00000000_00000FFF_00000000_00000000 >> 64)
  144. return timestamp
  145. }
  146. /*
  147. Get the timestamp of a version 6 UUID.
  148. Inputs:
  149. - id: The identifier.
  150. Returns:
  151. - timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
  152. */
  153. time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
  154. return time.from_nanoseconds(cast(i64)(raw_time_v6(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
  155. }
  156. /*
  157. Get the raw timestamp of a version 7 UUID.
  158. Inputs:
  159. - id: The identifier.
  160. Returns:
  161. - timestamp: The timestamp, in milliseconds since the UNIX epoch.
  162. */
  163. raw_time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
  164. time_bits := transmute(u128be)id & VERSION_7_TIME_MASK
  165. return cast(u64)(time_bits >> VERSION_7_TIME_SHIFT)
  166. }
  167. /*
  168. Get the timestamp of a version 7 UUID.
  169. Inputs:
  170. - id: The identifier.
  171. Returns:
  172. - timestamp: The timestamp, in milliseconds since the UNIX epoch.
  173. */
  174. time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
  175. return time.from_nanoseconds(cast(i64)raw_time_v7(id) * 1e6)
  176. }
  177. /*
  178. Get the 12-bit counter value of a version 7 UUID.
  179. The UUID must have been generated with a counter, otherwise this procedure will
  180. return random bits.
  181. Inputs:
  182. - id: The identifier.
  183. Returns:
  184. - counter: The 12-bit counter value.
  185. */
  186. counter_v7 :: proc "contextless" (id: Identifier) -> (counter: u16) {
  187. counter_bits := transmute(u128be)id & VERSION_7_COUNTER_MASK
  188. return cast(u16)(counter_bits >> VERSION_7_COUNTER_SHIFT)
  189. }