2022-06-09 05:54:41 +00:00
|
|
|
const Uuid = @This();
|
|
|
|
|
|
|
|
const std = @import("std");
|
|
|
|
|
2022-07-13 03:55:19 +00:00
|
|
|
data: u128,
|
2022-06-09 05:54:41 +00:00
|
|
|
|
2022-07-13 03:55:19 +00:00
|
|
|
pub const Nil = Uuid{ .data = @as(u128, 0) };
|
2022-06-09 05:54:41 +00:00
|
|
|
pub const StringLen = 36;
|
|
|
|
|
|
|
|
pub fn eql(lhs: Uuid, rhs: Uuid) bool {
|
2022-07-13 03:55:19 +00:00
|
|
|
return lhs.data == rhs.data;
|
2022-06-09 05:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn format(value: Uuid, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
2022-07-13 03:55:19 +00:00
|
|
|
const arr = @bitCast([16]u8, value.data);
|
2022-06-09 05:54:41 +00:00
|
|
|
try std.fmt.format(writer, "{x:0>2}{x:0>2}{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}", .{
|
2022-07-13 03:55:19 +00:00
|
|
|
arr[0],
|
|
|
|
arr[1],
|
|
|
|
arr[2],
|
|
|
|
arr[3],
|
|
|
|
arr[4],
|
|
|
|
arr[5],
|
|
|
|
arr[6],
|
|
|
|
arr[7],
|
|
|
|
arr[8],
|
|
|
|
arr[9],
|
|
|
|
arr[10],
|
|
|
|
arr[11],
|
|
|
|
arr[12],
|
|
|
|
arr[13],
|
|
|
|
arr[14],
|
|
|
|
arr[15],
|
2022-06-09 05:54:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-07-13 04:23:50 +00:00
|
|
|
pub fn jsonStringify(value: Uuid, _: std.json.StringifyOptions, writer: anytype) !void {
|
|
|
|
try std.fmt.format(writer, "\"{}\"", .{value});
|
|
|
|
}
|
|
|
|
|
2022-06-09 05:54:41 +00:00
|
|
|
pub const ParseError = error{
|
|
|
|
InvalidCharacter,
|
|
|
|
InvalidLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn parse(str: []const u8) ParseError!Uuid {
|
|
|
|
if (str.len != StringLen) return error.InvalidLength;
|
|
|
|
|
2022-07-13 03:55:19 +00:00
|
|
|
var data: [16]u8 = undefined;
|
2022-06-09 05:54:41 +00:00
|
|
|
var str_i: usize = 0;
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < 16 and str_i < str.len) : ({
|
|
|
|
i += 1;
|
|
|
|
str_i += 2;
|
|
|
|
}) {
|
2022-07-13 03:55:19 +00:00
|
|
|
data[i] = std.fmt.parseInt(u8, str[str_i .. str_i + 2], 16) catch |err| switch (err) {
|
2022-06-09 05:54:41 +00:00
|
|
|
error.InvalidCharacter => return error.InvalidCharacter,
|
|
|
|
else => unreachable,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (i == 3 or i == 5 or i == 7 or i == 9) {
|
|
|
|
if (str[str_i + 2] != '-') return error.InvalidCharacter;
|
|
|
|
str_i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-13 03:55:19 +00:00
|
|
|
return Uuid{ .data = @bitCast(u128, data) };
|
2022-06-09 05:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn randV4(rand: std.rand.Random) Uuid {
|
2022-07-13 03:55:19 +00:00
|
|
|
var ret: [16]u8 = undefined;
|
|
|
|
rand.bytes(&ret);
|
2022-06-09 05:54:41 +00:00
|
|
|
|
|
|
|
// signify that this is a random v4 uuid
|
2022-07-13 03:55:19 +00:00
|
|
|
ret[7] = (0b0100_0000) | (ret[7] & 0b1111);
|
|
|
|
ret[9] = (0b1000_0000) | (ret[9] & 0b11_1111);
|
2022-06-09 05:54:41 +00:00
|
|
|
|
2022-07-13 03:55:19 +00:00
|
|
|
return Uuid{ .data = @bitCast(u128, ret) };
|
2022-06-09 05:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
test "parse uuid" {
|
|
|
|
try std.testing.expectEqual(
|
|
|
|
Uuid.Nil,
|
|
|
|
try Uuid.parse("00000000-0000-0000-0000-000000000000"),
|
|
|
|
);
|
|
|
|
|
|
|
|
try std.testing.expectEqual(
|
|
|
|
Uuid{
|
2022-07-13 03:55:19 +00:00
|
|
|
.data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7),
|
2022-06-09 05:54:41 +00:00
|
|
|
},
|
|
|
|
try Uuid.parse("f75f5160-12d3-42c2-a81d-ad2245b7a74b"),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "format uuid" {
|
|
|
|
try std.testing.expectFmt("00000000-0000-0000-0000-000000000000", "{}", .{Uuid.Nil});
|
|
|
|
|
|
|
|
const uuid = Uuid{
|
2022-07-13 03:55:19 +00:00
|
|
|
.data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7),
|
2022-06-09 05:54:41 +00:00
|
|
|
};
|
|
|
|
try std.testing.expectFmt("f75f5160-12d3-42c2-a81d-ad2245b7a74b", "{}", .{uuid});
|
|
|
|
|
|
|
|
try std.testing.expectError(error.InvalidLength, Uuid.parse("fsdfs"));
|
|
|
|
try std.testing.expectError(error.InvalidCharacter, Uuid.parse("00000000-0000-0000-xxxx-000000000000"));
|
|
|
|
try std.testing.expectError(error.InvalidLength, Uuid.parse("00000000-0000-0000-0000-000000000000fsdfs"));
|
|
|
|
try std.testing.expectError(error.InvalidCharacter, Uuid.parse("00000000-0000x0000-0000-000000000000"));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "roundtrip random uuid" {
|
|
|
|
const test_seed = 12345;
|
|
|
|
var test_prng = std.rand.DefaultPrng.init(test_seed);
|
|
|
|
const uuid = Uuid.randV4(test_prng.random());
|
|
|
|
|
|
|
|
var buf: [36]u8 = undefined;
|
|
|
|
var fbs = std.io.fixedBufferStream(&buf);
|
|
|
|
try Uuid.format(uuid, "", .{}, fbs.writer());
|
|
|
|
|
|
|
|
const parsed = try Uuid.parse(&buf);
|
|
|
|
|
|
|
|
try std.testing.expectEqual(uuid, parsed);
|
|
|
|
}
|