code wiki / (root) / nx_uuid.nx

nx_uuid.nx source

↩ module page · 155 lines · 5535 B

1// nx_uuid.nx -- RFC 4122 UUIDv4 + RFC 9562 UUIDv7. 2// 3// Used by: 4// - Distributed log correlation (request IDs) 5// - Database row primary keys (when an external system needs 6// them before insert) 7// - Cache key generation 8// 9// UUIDv4 is fully random (122 bits of entropy + 6 fixed bits). 10// UUIDv7 is timestamp-prefixed (48 bits Unix-ms timestamp + 74 bits 11// of entropy + 6 fixed bits) -- sortable by creation time, which 12// makes B-tree primary keys 100x faster on insert than v4. 13// 14// Both are 128-bit values rendered as 15// "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" where M = version digit 16// (4 or 7) and N's high 2 bits are 10 (RFC variant marker). 17// 18// We accept any nx_rng for the entropy source. v7 uses 19// gettimeofday() for the timestamp. 20// 21// Pairs with nx_random (entropy) + nx_strconv (hex output). 22 23// nx_safety_envelope: 24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 25// sil_target: SIL1 26// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 27// verdict: NOT_YET_EVALUATED 28 29import "syscalls.nx" 30import "nx_random.nx" 31const K_MAGIC_12345: i64 = 12345 32 33// Render byte array (16 bytes) into a 36-char canonical UUID 34// string: 8-4-4-4-12 hex with hyphens. Buf must be >= 37 bytes. 35func nx_uuid_render(b: *u8, out: *u8) -> i64 { 36 let hex: *u8 = sys_mmap(16) 37 hex[0]=0x30; hex[1]=0x31; hex[2]=0x32; hex[3]=0x33; hex[4]=0x34 38 hex[5]=0x35; hex[6]=0x36; hex[7]=0x37; hex[8]=0x38; hex[9]=0x39 39 hex[10]=0x61; hex[11]=0x62; hex[12]=0x63; hex[13]=0x64; hex[14]=0x65; hex[15]=0x66 40 41 var src: i64 = 0 42 var dst: i64 = 0 43 var hyphens_done: i64 = 0 44 while src < 16 { 45 out[dst] = hex[(b[src] >> 4) & 0xF] 46 out[dst + 1] = hex[b[src] & 0xF] 47 dst = dst + 2 48 src = src + 1 49 // Insert hyphens after byte indices 4, 6, 8, 10. 50 if hyphens_done == 0 { 51 if src == 4 { out[dst] = 0x2D; dst = dst + 1; hyphens_done = 1 } 52 } else { 53 if hyphens_done == 1 { 54 if src == 6 { out[dst] = 0x2D; dst = dst + 1; hyphens_done = 2 } 55 } else { 56 if hyphens_done == 2 { 57 if src == 8 { out[dst] = 0x2D; dst = dst + 1; hyphens_done = 3 } 58 } else { 59 if hyphens_done == 3 { 60 if src == 10 { out[dst] = 0x2D; dst = dst + 1; hyphens_done = 4 } 61 } 62 } 63 } 64 } 65 } 66 out[dst] = 0 67 return dst 68} 69 70// Generate a UUIDv4 into b[0..16]. Bytes 6 (high nibble) gets 0x4 71// (version), byte 8 (high 2 bits) gets 0b10 (variant). 72func nx_uuid_v4(rng: *NxRng, b: *u8) -> i64 { 73 nx_rng_fill(rng, b, 16) 74 b[6] = (b[6] & 0x0F) | 0x40 75 b[8] = (b[8] & 0x3F) | 0x80 76 return 0 77} 78 79// Generate a UUIDv7 into b[0..16]. 80// bytes [0..6) = unix_ms timestamp (48 bits, big-endian) 81// byte 6: high nibble = 7 (version), low nibble = random 82// bytes 7..8: random 83// byte 8: high 2 bits = 10 (variant), low 6 bits = random 84// bytes 9..16: random 85// 86// We read the timestamp via clock_gettime(CLOCK_REALTIME) and 87// convert to milliseconds. 88func nx_uuid_v7(rng: *NxRng, b: *u8) -> i64 { 89 // Fill with entropy first. 90 nx_rng_fill(rng, b, 16) 91 92 // gettimeofday: { tv_sec: i64, tv_usec: i64 } 93 let tv_raw: *u8 = sys_mmap(32) 94 let tv: *i64 = tv_raw as *i64 95 __syscall(169, tv_raw, 0, 0, 0, 0, 0) 96 let sec: i64 = tv[0] 97 let usec: i64 = tv[1] 98 let ms: i64 = sec * 1000 + usec / 1000 99 100 // Big-endian 48-bit ms into bytes [0..6). 101 b[0] = (ms >> 40) & 0xFF 102 b[1] = (ms >> 32) & 0xFF 103 b[2] = (ms >> 24) & 0xFF 104 b[3] = (ms >> 16) & 0xFF 105 b[4] = (ms >> 8) & 0xFF 106 b[5] = ms & 0xFF 107 108 // Version + variant. 109 b[6] = (b[6] & 0x0F) | 0x70 110 b[8] = (b[8] & 0x3F) | 0x80 111 return 0 112} 113 114// ---- self-test --------------------------------------------------- 115 116func main() -> i64 { 117 let rng: *NxRng = nx_rng_new(K_MAGIC_12345) 118 let bytes: *u8 = sys_mmap(16) 119 120 // v4: byte 6 high nibble == 4, byte 8 high two bits == 10. 121 nx_uuid_v4(rng, bytes) 122 if ((bytes[6] >> 4) & 0xF) != 4 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 123 if ((bytes[8] >> 6) & 0x3) != 2 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 124 125 // v7: byte 6 high nibble == 7, byte 8 high two bits == 10. 126 nx_uuid_v7(rng, bytes) 127 if ((bytes[6] >> 4) & 0xF) != 7 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 128 if ((bytes[8] >> 6) & 0x3) != 2 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 129 130 // Render. Should be 36 chars + NUL. 131 let txt: *u8 = sys_mmap(64) 132 let n: i64 = nx_uuid_render(bytes, txt) 133 if n != 36 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 134 if txt[8] != 0x2D { return __syscall(93, 6, 0, 0, 0, 0, 0) } 135 if txt[13] != 0x2D { return __syscall(93, 7, 0, 0, 0, 0, 0) } 136 if txt[18] != 0x2D { return __syscall(93, 8, 0, 0, 0, 0, 0) } 137 if txt[23] != 0x2D { return __syscall(93, 9, 0, 0, 0, 0, 0) } 138 if txt[36] != 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 139 140 // Position 14 in canonical form is the version digit -> '7' for v7. 141 if txt[14] != 0x37 { return __syscall(93, 11, 0, 0, 0, 0, 0) } // '7' 142 143 // Two consecutive v7 calls should produce different bytes (entropy). 144 let bytes2: *u8 = sys_mmap(16) 145 nx_uuid_v7(rng, bytes2) 146 var same: i64 = 1 147 var i: i64 = 0 148 while i < 16 { 149 if bytes[i] != bytes2[i] { same = 0; i = 16 } 150 else { i = i + 1 } 151 } 152 if same == 1 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 153 154 return 0 155}