Last active
January 12, 2026 13:55
-
-
Save notcancername/be83162943394d3396441949d5c5955c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const std = @import("std"); | |
| pub const Uuid = packed union { | |
| /// general parts usually found in UUIDs | |
| pub const Parts = packed struct(u128) { | |
| _pad0: u48 = 0, | |
| version_bits: u4, | |
| _pad1: u12 = 0, | |
| variant_bits: u2 = 0b10, | |
| _pad2: u62 = 0, | |
| }; | |
| pub const Stringified = packed struct(u128) { | |
| a: u48, | |
| b: u16, | |
| c: u16, | |
| d: u16, | |
| e: u32, | |
| }; | |
| /// Time-based UUID with MAC address | |
| pub const V1 = packed struct(u128) { | |
| time_low: u32, | |
| time_mid: u16, | |
| version: u4 = 1, | |
| time_high: u12, | |
| variant: u2 = 0b10, | |
| clock_seq: u14, | |
| node: u48, | |
| }; | |
| /// X/Open DCE Security UUID | |
| pub const V2 = packed struct(u128) { | |
| local_id: u32, | |
| time_mid: u16, | |
| version: u4 = 2, | |
| time_high: u12, | |
| variant: u2 = 0b10, | |
| clock_seq_low: u6, | |
| local_domain: u8, | |
| node: u48, | |
| }; | |
| /// Name-based (MD5). Superseded by V5 because MD5 is insecure. | |
| pub const V3 = packed struct(u128) { | |
| md5_high: u48, | |
| version: u4 = 3, | |
| md5_mid: u12, | |
| variant: u2 = 0b10, | |
| md5_low: u62, | |
| }; | |
| /// Random UUID. | |
| pub const V4 = packed struct(u128) { | |
| rand_a: u48, | |
| version: u4 = 4, | |
| rand_b: u12, | |
| variant: u2 = 0b10, | |
| rand_c: u62, | |
| }; | |
| /// Name-based (SHA-1). Use V8 with SHA-256 for more collision resistance. | |
| pub const V5 = packed struct(u128) { | |
| sha1_high: u48, | |
| version: u4 = 5, | |
| sha1_mid: u12, | |
| variant: u2 = 0b10, | |
| sha1_low: u62, | |
| }; | |
| /// Time-based UUID with MAC address, but reordered for databases. | |
| pub const V6 = packed struct(u128) { | |
| time_high: u32, | |
| time_mid: u16, | |
| version: u4 = 6, | |
| time_low: u12, | |
| variant: u2 = 0b10, | |
| clock_seq: u14, | |
| node: u48, | |
| }; | |
| /// Unix Epoch time (ms) | |
| pub const V7 = packed struct(u128) { | |
| unix_ts_ms: u48, | |
| version: u4 = 7, | |
| rand_a: u12, | |
| variant: u2 = 0b10, | |
| rand_b: u62, | |
| }; | |
| /// Custom UUID. What's in it? Who knows! | |
| pub const V8 = packed struct(u128) { | |
| custom_a: u48, | |
| version: u4 = 8, | |
| custom_b: u12, | |
| variant: u2 = 0b10, | |
| custom_c: u62, | |
| }; | |
| i: u128, | |
| p: Parts, | |
| s: Stringified, | |
| v1: V1, | |
| v2: V2, | |
| v3: V3, | |
| v4: V4, | |
| v5: V5, | |
| v6: V6, | |
| v7: V7, | |
| v8: V8, | |
| pub fn bytes(uuid: Uuid) [16]u8 { | |
| return std.mem.toBytes(std.mem.nativeTo(u128, uuid.i, .big)); | |
| } | |
| pub fn format(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| const s = uuid.s; | |
| try writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>4}-{x:0>12}", .{ s.e, s.d, s.c, s.b, s.a }); | |
| } | |
| pub fn prettyPrint(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| const s = uuid.s; | |
| try writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>4}-{x:0>12}", .{ s.e, s.d, s.c, s.b, s.a }); | |
| if (uuid.i == nil.i) { | |
| try writer.print(" (nil)", .{}); | |
| return; | |
| } | |
| if (uuid.i == max.i) { | |
| try writer.print(" (max)", .{}); | |
| return; | |
| } | |
| if (uuid.p.variant_bits != 0b10) { | |
| try writer.print(" (unknown variant bits 0x{x})", .{@as(u4, @truncate(uuid.i >> 64))}); | |
| return; | |
| } | |
| switch (uuid.p.version_bits) { | |
| 1 => try writer.print(" (v1, {})", .{uuid.v1}), | |
| 2 => try writer.print(" (v2, {})", .{uuid.v2}), | |
| 3 => try writer.print(" (v3, {})", .{uuid.v3}), | |
| 4 => try writer.print(" (v4, {})", .{uuid.v4}), | |
| 5 => try writer.print(" (v5, {})", .{uuid.v5}), | |
| 6 => try writer.print(" (v6, {})", .{uuid.v6}), | |
| 7 => try writer.print(" (v7, {})", .{uuid.v7}), | |
| 8 => try writer.print(" (v8, {})", .{uuid.v8}), | |
| else => try writer.print("(unknown version {d})", .{uuid.p.version_bits}), | |
| } | |
| } | |
| pub fn formatUpper(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| const s = uuid.s; | |
| try writer.print("{X:0>8}-{X:0>4}-{X:0>4}-{X:0>4}-{x:0>12}", .{ s.e, s.d, s.c, s.b, s.a }); | |
| } | |
| pub fn formatDense(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| try writer.print("{x:0>32}", .{uuid.i}); | |
| } | |
| pub fn formatDenseUpper(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| try writer.print("{X:0>32}", .{uuid.i}); | |
| } | |
| pub fn formatDecimal(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| const s = uuid.s; | |
| try writer.print("{d}", .{ s.e, s.d, s.c, s.b, s.a }); | |
| } | |
| pub fn formatBinary(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| try writer.writeInt(u128, uuid.i, .big); | |
| } | |
| pub fn formatBinaryLittle(uuid: Uuid, writer: *std.Io.Writer) !void { | |
| try writer.writeInt(u128, uuid.i, .little); | |
| } | |
| pub fn fromBinary(by: *const [16]u8) Uuid { | |
| return .{.i = std.mem.readInt(u128, by, .little)}; | |
| } | |
| pub fn fromBinaryLittle(by: *const [16]u8) Uuid { | |
| return .{.i = std.mem.readInt(u128, by, .little)}; | |
| } | |
| pub fn fromText(text: *const [32 + 4]u8) !Uuid { | |
| return .{.s = .{ | |
| .e = try std.fmt.parseInt(u32, text[0..8], 16), | |
| .d = try std.fmt.parseInt(u16, text[9..][0..4], 16), | |
| .c = try std.fmt.parseInt(u16, text[14..][0..4], 16), | |
| .b = try std.fmt.parseInt(u16, text[19..][0..4], 16), | |
| .a = try std.fmt.parseInt(u48, text[24..][0..12], 16), | |
| }}; | |
| } | |
| /// Generates a random UUID v4 using the | |
| /// cryptographically secure generator. | |
| pub fn generateRandom(io: std.Io) Uuid { | |
| var source = std.Random.IoSource{ .io = io }; | |
| return generateRandomWithGenerator(source.interface()); | |
| } | |
| /// Generates a random UUID v4 with a user-supplied `rng`. | |
| pub fn generateRandomWithGenerator(rng: std.Random) Uuid { | |
| return .{ .v4 = .{ | |
| .rand_a = rng.int(u48), | |
| .rand_b = rng.int(u12), | |
| .rand_c = rng.int(u62), | |
| } }; | |
| } | |
| /// Generates a UUID V5 from a byte string name. | |
| /// Note that SHA-1 is cryptographically broken, | |
| /// use generateFromNameSecure if you need | |
| /// collision resistance instead. | |
| pub fn generateFromName(namespace: Uuid, name: []const u8) Uuid { | |
| var sha1: [20]u8 = undefined; | |
| { | |
| var h = std.crypto.hash.Sha1.init(.{}); | |
| h.update(std.mem.asBytes(&std.mem.nativeTo(u128, namespace.i, .big))); | |
| h.update(name); | |
| h.final(&sha1); | |
| } | |
| return .{ .v5 = .{ | |
| .sha1_high = std.mem.readInt(u48, sha1[0..6], .big), | |
| .sha1_mid = @truncate(std.mem.readInt(u16, sha1[6..8], .big)), | |
| .sha1_low = @truncate(std.mem.readInt(u64, sha1[8..16], .big)), | |
| } }; | |
| } | |
| /// Generates a UUID V8 from a byte string name. Uses 122 bits of SHA-256. | |
| pub fn generateFromNameSecure(namespace: Uuid, name: []const u8) Uuid { | |
| var sha2: [32]u8 = undefined; | |
| { | |
| var h = std.crypto.hash.sha2.Sha256.init(.{}); | |
| h.update(&namespace.bytes()); | |
| h.update(name); | |
| h.final(&sha2); | |
| } | |
| return .{ | |
| .v8 = .{ | |
| .custom_a = std.mem.readInt(u48, sha2[0..6], .big), | |
| .custom_b = @truncate(std.mem.readInt(u16, sha2[6..8], .big)), | |
| .custom_c = @truncate(std.mem.readInt(u64, sha2[8..16], .big)), | |
| } | |
| }; | |
| } | |
| /// Generates a UUID V7 from the current wall clock time and randomness. | |
| pub fn generateFromTime(io: std.Io) std.Io.Clock.Error!Uuid { | |
| var source = std.Random.IoSource{ .io = io }; | |
| const now = try std.Io.Clock.real.now(io); | |
| return generateFromTimestampWithRng(now, source.interface()); | |
| } | |
| /// Generates a UUID V7 from the current time and random values. | |
| pub fn generateFromTimestampWithRng(now: std.Io.Timestamp, rng: std.Random) Uuid { | |
| return .{.v7 = .{ | |
| .unix_ts_ms = @intCast(now.toMilliseconds()), | |
| .rand_a = rng.int(u12), | |
| .rand_b = rng.int(u62), | |
| }}; | |
| } | |
| test generateFromName { | |
| const dns_namespace: Uuid = .{ .s = .{ | |
| .e = 0x6ba7b810, | |
| .d = 0x9dad, | |
| .c = 0x11d1, | |
| .b = 0x80b4, | |
| .a = 0x00c04fd430c8, | |
| } }; | |
| const expected: Uuid = .{ .v5 = .{ | |
| .sha1_high = 0x2ed6657de927, | |
| .version = 5, | |
| .sha1_mid = 0x68b, | |
| .variant = 0b10, | |
| .sha1_low = 0x15e12665a8aea6a2, | |
| } }; | |
| const u = generateFromName(dns_namespace, "www.example.com"); | |
| try std.testing.expectEqualDeep(expected.v5, u.v5); | |
| } | |
| test generateRandomWithGenerator { | |
| var r = std.Random.DefaultPrng.init(std.testing.random_seed); | |
| const u = generateRandomWithGenerator(r.random()); | |
| try std.testing.expect(u.p.version_bits == 4); | |
| try std.testing.expect(u.p.variant_bits == 0b10); | |
| } | |
| test generateRandom { | |
| const u = generateRandom(std.testing.io); | |
| try std.testing.expect(u.p.version_bits == 4); | |
| try std.testing.expect(u.p.variant_bits == 0b10); | |
| } | |
| test generateFromTime { | |
| const u = try generateFromTime(std.testing.io); | |
| try std.testing.expect(u.p.version_bits == 7); | |
| try std.testing.expect(u.p.variant_bits == 0b10); | |
| const now = try std.Io.Clock.real.now(std.testing.io); | |
| try std.testing.expect(now.subDuration(.fromMilliseconds(u.v7.unix_ts_ms)).toSeconds() < 2 * 60); | |
| } | |
| test "format and parse round-trip" { | |
| const x = try generateFromTime(std.testing.io); | |
| const y = generateRandom(std.testing.io); | |
| const z = generateFromName(try fromText("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), "www.example.com"); | |
| const x_str = try std.fmt.allocPrint(std.testing.allocator, "{f}", .{x}); | |
| defer std.testing.allocator.free(x_str); | |
| const y_str = try std.fmt.allocPrint(std.testing.allocator, "{f}", .{y}); | |
| defer std.testing.allocator.free(y_str); | |
| const z_str = try std.fmt.allocPrint(std.testing.allocator, "{f}", .{z}); | |
| defer std.testing.allocator.free(z_str); | |
| try std.testing.expectEqual(x.i, (try fromText(x_str[0..36])).i); | |
| try std.testing.expectEqual(y.i, (try fromText(y_str[0..36])).i); | |
| try std.testing.expectEqual(z.i, (try fromText(z_str[0..36])).i); | |
| } | |
| pub const nil: Uuid = .{ .i = 0 }; | |
| pub const max: Uuid = .{ .i = std.math.maxInt(u128) }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment