code wiki / (root) / uuid.nx

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}