uuid.nx source
↩ module page · 146 lines · 5168 B
1// uuid.nx -- UUID v4 (random) + v7 (time-ordered) generators.
2//
3// RFC 4122 (v4) + draft-ietf-uuidrev-rfc4122bis (v7). 16-byte
4// identifier, formatted as 36-char hex string with hyphens.
5//
6// v4 = 122 random bits + 6 deterministic version/variant bits.
7// Use when you don't need lexicographic time-ordering.
8//
9// v7 = 48-bit Unix ms timestamp prefix + 74 random bits + 6
10// version/variant bits. Sorts lexicographically by creation
11// time, which is huge for database indexes -- rows cluster
12// instead of scattering, and clustered B-trees stay warm.
13// Use in preference to v4 for new systems.
14//
15// Invariants:
16// U1 Every byte of the non-version/variant space is from
17// /dev/urandom (via rand.nx). No PRNG state persists; each
18// UUID is an independent sample.
19// U2 Version + variant bits exactly match the RFC specs at
20// byte positions 6 (version) and 8 (variant).
21// U3 v7 timestamp is milliseconds since epoch, big-endian in
22// bytes 0..5. Enables lexicographic time-ordering.
23// U4 Formatting is deterministic: lowercase hex, no prefix,
24// exact dash placement at 8-4-4-4-12 positions.
25
26import "syscalls.nx"
27import "rand.nx"
28import "hex.nx"
29
30// ---- raw binary generators ---------------------------------------
31
32// Fill `out` with a fresh v4 UUID (16 bytes). out must point at
33// at least 16 bytes. Returns 0 on success, negative if rand fails.
34func uuid_v4_bytes(out: *u8) -> i64 {
35 let n: i64 = rand_bytes(out, 16)
36 if n != 16 { return -1 }
37 // Version = 4 in high nibble of byte 6.
38 out[6] = (out[6] & 0x0F) | 0x40
39 // Variant = RFC 4122 (10xx) in high bits of byte 8.
40 out[8] = (out[8] & 0x3F) | 0x80
41 return 0
42}
43
44// Fill `out` with a fresh v7 UUID. Bytes 0..5 = millisecond
45// Unix timestamp (big-endian). Bytes 6..15 = version/variant
46// bits + 74 random bits. Caller supplies `unix_ms`; runtime
47// captures it once instead of per-call to keep the function
48// usable in environments without monotonic clocks.
49func uuid_v7_bytes(unix_ms: i64, out: *u8) -> i64 {
50 // Fill low 10 bytes with randomness first.
51 let rand_part: *u8 = sys_mmap(10)
52 let n: i64 = rand_bytes(rand_part, 10)
53 if n != 10 { return -1 }
54 // Write timestamp big-endian bytes 0..5 (48 bits).
55 out[0] = (unix_ms >> 40) & 0xFF
56 out[1] = (unix_ms >> 32) & 0xFF
57 out[2] = (unix_ms >> 24) & 0xFF
58 out[3] = (unix_ms >> 16) & 0xFF
59 out[4] = (unix_ms >> 8) & 0xFF
60 out[5] = unix_ms & 0xFF
61 // Random bytes 6..15.
62 var i: i64 = 0
63 while i < 10 {
64 out[6 + i] = rand_part[i]
65 i = i + 1
66 }
67 // Version = 7 in high nibble of byte 6.
68 out[6] = (out[6] & 0x0F) | 0x70
69 // Variant = RFC 4122 (10xx) in high bits of byte 8.
70 out[8] = (out[8] & 0x3F) | 0x80
71 return 0
72}
73
74// ---- 36-char hex formatting --------------------------------------
75//
76// Canonical format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
77// (8-4-4-4-12 lowercase hex digits).
78
79func uuid_format(bytes: *u8, out: *u8) -> i64 {
80 // Full hex of 16 bytes = 32 chars; format inserts 4 hyphens
81 // at positions 8, 13, 18, 23.
82 let hex_buf: *u8 = sys_mmap(32)
83 hex_encode(bytes, 16, hex_buf)
84 // Copy with hyphen insertions.
85 var src: i64 = 0
86 var dst: i64 = 0
87 while dst < 36 {
88 if dst == 8 {
89 out[dst] = 0x2D // '-'
90 dst = dst + 1
91 } else {
92 if dst == 13 {
93 out[dst] = 0x2D
94 dst = dst + 1
95 } else {
96 if dst == 18 {
97 out[dst] = 0x2D
98 dst = dst + 1
99 } else {
100 if dst == 23 {
101 out[dst] = 0x2D
102 dst = dst + 1
103 } else {
104 out[dst] = hex_buf[src]
105 src = src + 1
106 dst = dst + 1
107 }
108 }
109 }
110 }
111 }
112 return 36
113}
114
115// Convenience: generate v4 and format directly. `out` gets 36
116// bytes of ASCII hex + hyphens. Returns 36 on success, negative
117// on rand failure.
118func uuid_v4_string(out: *u8) -> i64 {
119 let bytes: *u8 = sys_mmap(16)
120 let rc: i64 = uuid_v4_bytes(bytes)
121 if rc < 0 { return rc }
122 return uuid_format(bytes, out)
123}
124
125func uuid_v7_string(unix_ms: i64, out: *u8) -> i64 {
126 let bytes: *u8 = sys_mmap(16)
127 let rc: i64 = uuid_v7_bytes(unix_ms, bytes)
128 if rc < 0 { return rc }
129 return uuid_format(bytes, out)
130}
131
132// Compile-only smoke.
133func main() -> i64 {
134 let out: *u8 = sys_mmap(64)
135 let rc: i64 = uuid_v4_string(out)
136 if rc != 36 { return 1 }
137 // Position 8 should be '-'.
138 if out[8] != 0x2D { return 2 }
139 if out[13] != 0x2D { return 3 }
140 if out[18] != 0x2D { return 4 }
141 if out[23] != 0x2D { return 5 }
142 // Byte 14 (first char of 3rd group) encodes version; should
143 // have the v4 bit (high nibble == '4' = 0x34).
144 if out[14] != 0x34 { return 6 }
145 return 0
146}