fediglam/src/util/Uuid.zig

137 lines
3.9 KiB
Zig

const Uuid = @This();
const std = @import("std");
data: u128,
pub const nil = Uuid{ .data = @as(u128, 0) };
pub const string_len = 36;
pub fn eql(lhs: ?Uuid, rhs: ?Uuid) bool {
if (lhs == null and rhs == null) return true;
if (lhs == null or rhs == null) return false;
return lhs.?.data == rhs.?.data;
}
pub fn toCharArray(value: Uuid) [string_len]u8 {
var buf: [string_len]u8 = undefined;
_ = std.fmt.bufPrint(&buf, "{}", .{value}) catch unreachable;
return buf;
}
pub fn toCharArrayZ(value: Uuid) [string_len + 1:0]u8 {
var buf: [string_len + 1:0]u8 = undefined;
_ = std.fmt.bufPrintZ(&buf, "{}", .{value}) catch unreachable;
return buf;
}
pub fn format(value: Uuid, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
const arr = @bitCast([16]u8, value.data);
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}", .{
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],
});
}
pub const JsonParseAs = []const u8;
pub fn jsonStringify(value: Uuid, _: std.json.StringifyOptions, writer: anytype) !void {
try std.fmt.format(writer, "\"{}\"", .{value});
}
pub const ParseError = error{
InvalidCharacter,
InvalidLength,
};
pub fn parse(str: []const u8) ParseError!Uuid {
if (str.len != string_len and (str.len != string_len + 1 or str[str.len - 1] != 0)) return error.InvalidLength;
var data: [16]u8 = undefined;
var str_i: usize = 0;
var i: usize = 0;
while (i < 16 and str_i < str.len) : ({
i += 1;
str_i += 2;
}) {
data[i] = std.fmt.parseInt(u8, str[str_i .. str_i + 2], 16) catch |err| switch (err) {
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;
}
}
return Uuid{ .data = @bitCast(u128, data) };
}
pub fn randV4(rand: std.rand.Random) Uuid {
var ret: [16]u8 = undefined;
rand.bytes(&ret);
// signify that this is a random v4 uuid
ret[7] = (0b0100_0000) | (ret[7] & 0b1111);
ret[9] = (0b1000_0000) | (ret[9] & 0b11_1111);
return Uuid{ .data = @bitCast(u128, ret) };
}
test "parse uuid" {
try std.testing.expectEqual(
Uuid.nil,
try Uuid.parse("00000000-0000-0000-0000-000000000000"),
);
try std.testing.expectEqual(
Uuid{
.data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7),
},
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{
.data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7),
};
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);
}