nx_uuid_v1.nx source
↩ module page · 152 lines · 5288 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
26// nx_safety_envelope:
27// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
28// sil_target: SIL1
29// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
30// verdict: NOT_YET_EVALUATED
31
32import "nx_syscalls.nx"
33import "nx_rand.nx"
34import "nx_hex_codec.nx"
35
36// ---- raw binary generators ---------------------------------------
37
38// Fill `out` with a fresh v4 UUID (16 bytes). out must point at
39// at least 16 bytes. Returns 0 on success, negative if rand fails.
40func uuid_v4_bytes(out: *u8) -> i64 {
41 let n: i64 = rand_bytes(out, 16)
42 if n != 16 { return -1 }
43 // Version = 4 in high nibble of byte 6.
44 out[6] = (out[6] & 0x0F) | 0x40
45 // Variant = RFC 4122 (10xx) in high bits of byte 8.
46 out[8] = (out[8] & 0x3F) | 0x80
47 return 0
48}
49
50// Fill `out` with a fresh v7 UUID. Bytes 0..5 = millisecond
51// Unix timestamp (big-endian). Bytes 6..15 = version/variant
52// bits + 74 random bits. Caller supplies `unix_ms`; runtime
53// captures it once instead of per-call to keep the function
54// usable in environments without monotonic clocks.
55func uuid_v7_bytes(unix_ms: i64, out: *u8) -> i64 {
56 // Fill low 10 bytes with randomness first.
57 let rand_part: *u8 = sys_mmap(10)
58 let n: i64 = rand_bytes(rand_part, 10)
59 if n != 10 { return -1 }
60 // Write timestamp big-endian bytes 0..5 (48 bits).
61 out[0] = (unix_ms >> 40) & 0xFF
62 out[1] = (unix_ms >> 32) & 0xFF
63 out[2] = (unix_ms >> 24) & 0xFF
64 out[3] = (unix_ms >> 16) & 0xFF
65 out[4] = (unix_ms >> 8) & 0xFF
66 out[5] = unix_ms & 0xFF
67 // Random bytes 6..15.
68 var i: i64 = 0
69 while i < 10 {
70 out[6 + i] = rand_part[i]
71 i = i + 1
72 }
73 // Version = 7 in high nibble of byte 6.
74 out[6] = (out[6] & 0x0F) | 0x70
75 // Variant = RFC 4122 (10xx) in high bits of byte 8.
76 out[8] = (out[8] & 0x3F) | 0x80
77 return 0
78}
79
80// ---- 36-char hex formatting --------------------------------------
81//
82// Canonical format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
83// (8-4-4-4-12 lowercase hex digits).
84
85func uuid_format(bytes: *u8, out: *u8) -> i64 {
86 // Full hex of 16 bytes = 32 chars; format inserts 4 hyphens
87 // at positions 8, 13, 18, 23.
88 let hex_buf: *u8 = sys_mmap(32)
89 hex_encode(bytes, 16, hex_buf)
90 // Copy with hyphen insertions.
91 var src: i64 = 0
92 var dst: i64 = 0
93 while dst < 36 {
94 if dst == 8 {
95 out[dst] = 0x2D // '-'
96 dst = dst + 1
97 } else {
98 if dst == 13 {
99 out[dst] = 0x2D
100 dst = dst + 1
101 } else {
102 if dst == 18 {
103 out[dst] = 0x2D
104 dst = dst + 1
105 } else {
106 if dst == 23 {
107 out[dst] = 0x2D
108 dst = dst + 1
109 } else {
110 out[dst] = hex_buf[src]
111 src = src + 1
112 dst = dst + 1
113 }
114 }
115 }
116 }
117 }
118 return 36
119}
120
121// Convenience: generate v4 and format directly. `out` gets 36
122// bytes of ASCII hex + hyphens. Returns 36 on success, negative
123// on rand failure.
124func uuid_v4_string(out: *u8) -> i64 {
125 let bytes: *u8 = sys_mmap(16)
126 let rc: i64 = uuid_v4_bytes(bytes)
127 if rc < 0 { return rc }
128 return uuid_format(bytes, out)
129}
130
131func uuid_v7_string(unix_ms: i64, out: *u8) -> i64 {
132 let bytes: *u8 = sys_mmap(16)
133 let rc: i64 = uuid_v7_bytes(unix_ms, bytes)
134 if rc < 0 { return rc }
135 return uuid_format(bytes, out)
136}
137
138// Compile-only smoke.
139func main() -> i64 {
140 let out: *u8 = sys_mmap(64)
141 let rc: i64 = uuid_v4_string(out)
142 if rc != 36 { return 1 }
143 // Position 8 should be '-'.
144 if out[8] != 0x2D { return 2 }
145 if out[13] != 0x2D { return 3 }
146 if out[18] != 0x2D { return 4 }
147 if out[23] != 0x2D { return 5 }
148 // Byte 14 (first char of 3rd group) encodes version; should
149 // have the v4 bit (high nibble == '4' = 0x34).
150 if out[14] != 0x34 { return 6 }
151 return 0
152}