Created a new function to replace parseAndFindUUIDWithFilter. Now it parse the file, evaluate with the filter and write directly to a buffer. There is no UUID that is return, then file are parse again. Should be a huge perf improve. Some bugs with additionData tho, got a name: "<UUID>" =(
276 lines
9.7 KiB
Zig
276 lines
9.7 KiB
Zig
// Fast allocation-free v4 UUIDs.
|
|
// Inspired by the Go implementation at github.com/skeeto/uuid
|
|
|
|
const std = @import("std");
|
|
const crypto = std.crypto;
|
|
const fmt = std.fmt;
|
|
const testing = std.testing;
|
|
|
|
pub const Error = error{InvalidUUID};
|
|
|
|
pub const UUID = struct {
|
|
bytes: [16]u8,
|
|
|
|
pub fn init() UUID {
|
|
var uuid = UUID{ .bytes = undefined };
|
|
|
|
crypto.random.bytes(&uuid.bytes);
|
|
// Version 4
|
|
uuid.bytes[6] = (uuid.bytes[6] & 0x0f) | 0x40;
|
|
// Variant 1
|
|
uuid.bytes[8] = (uuid.bytes[8] & 0x3f) | 0x80;
|
|
return uuid;
|
|
}
|
|
|
|
pub fn compare(self: UUID, other: UUID) bool {
|
|
return std.meta.eql(self.bytes, other.bytes);
|
|
}
|
|
|
|
fn to_string(self: UUID, slice: []u8) void {
|
|
var string: [36]u8 = format_uuid(self);
|
|
std.mem.copyForwards(u8, slice, &string);
|
|
}
|
|
|
|
pub fn format_uuid(self: UUID) [36]u8 {
|
|
var buf: [36]u8 = undefined;
|
|
buf[8] = '-';
|
|
buf[13] = '-';
|
|
buf[18] = '-';
|
|
buf[23] = '-';
|
|
inline for (encoded_pos, 0..) |i, j| {
|
|
buf[i + 0] = hex[self.bytes[j] >> 4];
|
|
buf[i + 1] = hex[self.bytes[j] & 0x0f];
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
pub fn format_bytes(bytes: [16]u8) [36]u8 {
|
|
var buf: [36]u8 = undefined;
|
|
buf[8] = '-';
|
|
buf[13] = '-';
|
|
buf[18] = '-';
|
|
buf[23] = '-';
|
|
inline for (encoded_pos, 0..) |i, j| {
|
|
buf[i + 0] = hex[bytes[j] >> 4];
|
|
buf[i + 1] = hex[bytes[j] & 0x0f];
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// Indices in the UUID string representation for each byte.
|
|
const encoded_pos = [16]u8{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };
|
|
|
|
// Hex
|
|
const hex = "0123456789abcdef";
|
|
|
|
// Hex to nibble mapping.
|
|
const hex_to_nibble = [256]u8{
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
};
|
|
|
|
pub fn format(
|
|
self: UUID,
|
|
comptime layout: []const u8,
|
|
options: fmt.FormatOptions,
|
|
writer: anytype,
|
|
) !void {
|
|
_ = options; // currently unused
|
|
|
|
if (layout.len != 0 and layout[0] != 's')
|
|
@compileError("Unsupported format specifier for UUID type: '" ++ layout ++ "'.");
|
|
|
|
const buf = format_uuid(self);
|
|
try fmt.format(writer, "{s}", .{buf});
|
|
}
|
|
|
|
pub fn parse(buf: []const u8) Error!UUID {
|
|
var uuid = UUID{ .bytes = undefined };
|
|
|
|
if (buf.len != 36 or buf[8] != '-' or buf[13] != '-' or buf[18] != '-' or buf[23] != '-')
|
|
return Error.InvalidUUID;
|
|
|
|
inline for (encoded_pos, 0..) |i, j| {
|
|
const hi = hex_to_nibble[buf[i + 0]];
|
|
const lo = hex_to_nibble[buf[i + 1]];
|
|
if (hi == 0xff or lo == 0xff) {
|
|
return Error.InvalidUUID;
|
|
}
|
|
uuid.bytes[j] = hi << 4 | lo;
|
|
}
|
|
|
|
return uuid;
|
|
}
|
|
};
|
|
|
|
// Zero UUID
|
|
pub const zero: UUID = .{ .bytes = .{0} ** 16 };
|
|
|
|
// TODO: Optimize both
|
|
pub fn OR(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
|
for (0..arr2.items.len) |i| {
|
|
if (!containUUID(arr1.*, arr2.items[i])) {
|
|
try arr1.append(arr2.items[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn AND(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
|
var i: usize = 0;
|
|
for (0..arr1.items.len) |_| {
|
|
if (!containUUID(arr2.*, arr1.items[i])) {
|
|
_ = arr1.orderedRemove(i);
|
|
} else {
|
|
i += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
test "OR & AND" {
|
|
const allocator = std.testing.allocator;
|
|
|
|
var right_arr = std.ArrayList(UUID).init(allocator);
|
|
defer right_arr.deinit();
|
|
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
|
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
|
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
|
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
|
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
|
|
|
var left_arr1 = std.ArrayList(UUID).init(allocator);
|
|
defer left_arr1.deinit();
|
|
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
|
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
|
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
|
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
|
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
|
|
|
var expected_arr1 = std.ArrayList(UUID).init(allocator);
|
|
defer expected_arr1.deinit();
|
|
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
|
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
|
|
|
try AND(&left_arr1, &right_arr);
|
|
try std.testing.expect(compareUUIDArray(left_arr1, expected_arr1));
|
|
|
|
var left_arr2 = std.ArrayList(UUID).init(allocator);
|
|
defer left_arr2.deinit();
|
|
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
|
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
|
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
|
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
|
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
|
|
|
var expected_arr2 = std.ArrayList(UUID).init(allocator);
|
|
defer expected_arr2.deinit();
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
|
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
|
|
|
try OR(&left_arr2, &right_arr);
|
|
|
|
try std.testing.expect(compareUUIDArray(left_arr2, expected_arr2));
|
|
}
|
|
|
|
fn containUUID(arr: std.ArrayList(UUID), value: UUID) bool {
|
|
return for (arr.items) |elem| {
|
|
if (value.compare(elem)) break true;
|
|
} else false;
|
|
}
|
|
|
|
fn compareUUIDArray(arr1: std.ArrayList(UUID), arr2: std.ArrayList(UUID)) bool {
|
|
if (arr1.items.len != arr2.items.len) {
|
|
std.debug.print("Not same array len when comparing UUID. arr1: {d} arr2: {d}\n", .{ arr1.items.len, arr2.items.len });
|
|
return false;
|
|
}
|
|
|
|
for (0..arr1.items.len) |i| {
|
|
if (!containUUID(arr2, arr1.items[i])) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
test "parse and format" {
|
|
const uuids = [_][]const u8{
|
|
"d0cd8041-0504-40cb-ac8e-d05960d205ec",
|
|
"3df6f0e4-f9b1-4e34-ad70-33206069b995",
|
|
"f982cf56-c4ab-4229-b23c-d17377d000be",
|
|
"6b9f53be-cf46-40e8-8627-6b60dc33def8",
|
|
"c282ec76-ac18-4d4a-8a29-3b94f5c74813",
|
|
"00000000-0000-0000-0000-000000000000",
|
|
};
|
|
|
|
for (uuids) |uuid| {
|
|
try testing.expectFmt(uuid, "{}", .{try UUID.parse(uuid)});
|
|
}
|
|
}
|
|
|
|
test "invalid UUID" {
|
|
const uuids = [_][]const u8{
|
|
"3df6f0e4-f9b1-4e34-ad70-33206069b99", // too short
|
|
"3df6f0e4-f9b1-4e34-ad70-33206069b9912", // too long
|
|
"3df6f0e4-f9b1-4e34-ad70_33206069b9912", // missing or invalid group separator
|
|
"zdf6f0e4-f9b1-4e34-ad70-33206069b995", // invalid character
|
|
};
|
|
|
|
for (uuids) |uuid| {
|
|
try testing.expectError(Error.InvalidUUID, UUID.parse(uuid));
|
|
}
|
|
}
|
|
|
|
test "check to_string works" {
|
|
const uuid1 = UUID.init();
|
|
|
|
var string1: [36]u8 = undefined;
|
|
var string2: [36]u8 = undefined;
|
|
|
|
uuid1.to_string(&string1);
|
|
uuid1.to_string(&string2);
|
|
|
|
try testing.expectEqual(string1, string2);
|
|
}
|
|
|
|
test "compare" {
|
|
const uuid1 = UUID.init();
|
|
const uuid2 = UUID.init();
|
|
|
|
try testing.expect(uuid1.compare(uuid1));
|
|
try testing.expect(!uuid1.compare(uuid2));
|
|
}
|