Skip to content

Instantly share code, notes, and snippets.

@notcancername
Last active January 12, 2026 13:55
Show Gist options
  • Select an option

  • Save notcancername/be83162943394d3396441949d5c5955c to your computer and use it in GitHub Desktop.

Select an option

Save notcancername/be83162943394d3396441949d5c5955c to your computer and use it in GitHub Desktop.
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