From 41fd343508880ffdfbc83c7b053237da09199f02 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 01:05:41 -0500 Subject: [PATCH 1/7] std: fix path joining on UEFI UEFI uses `\` for paths exclusively. This changes std.fs.path to use `\` for UEFI path joining. Also adds a few tests regarding it, specifically in making sure double-separators do not result from path joining, as the UEFI spec says to convert any that result from joining into single separators (UEFI Spec Version 2.7, pg. 448). --- lib/std/fs/path.zig | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 323f974255..94593fe7cb 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -12,13 +12,13 @@ const fs = std.fs; const process = std.process; const native_os = builtin.target.os.tag; -pub const sep_windows = '\\'; +pub const sep_windows_uefi = '\\'; pub const sep_posix = '/'; -pub const sep = if (native_os == .windows) sep_windows else sep_posix; +pub const sep = if (native_os == .windows or native_os == .uefi) sep_windows_uefi else sep_posix; -pub const sep_str_windows = "\\"; +pub const sep_str_windows_uefi = "\\"; pub const sep_str_posix = "/"; -pub const sep_str = if (native_os == .windows) sep_str_windows else sep_str_posix; +pub const sep_str = if (native_os == .windows or native_os == .uefi) sep_str_windows_uefi else sep_str_posix; pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; @@ -26,11 +26,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite /// Returns if the given byte is a valid path separator pub fn isSep(byte: u8) bool { - if (native_os == .windows) { - return byte == '/' or byte == '\\'; - } else { - return byte == '/'; - } + return switch (native_os) { + .windows => byte == '/' or byte == '\\', + .uefi => byte == '\\', + else => byte == '/', + }; } /// This is different from mem.join in that the separator will not be repeated if @@ -110,13 +110,24 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 { return out[0 .. out.len - 1 :0]; } +fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void { + const uefiIsSep = struct { + fn isSep(byte: u8) bool { + return byte == '\\'; + } + }.isSep; + const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, uefiIsSep, paths, zero); + defer testing.allocator.free(actual); + try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); +} + fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void { const windowsIsSep = struct { fn isSep(byte: u8) bool { return byte == '/' or byte == '\\'; } }.isSep; - const actual = try joinSepMaybeZ(testing.allocator, sep_windows, windowsIsSep, paths, zero); + const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, windowsIsSep, paths, zero); defer testing.allocator.free(actual); try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); } @@ -158,6 +169,11 @@ test "join" { zero, ); + try testJoinMaybeZUefi(&[_][]const u8{ "EFI", "Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero); + try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero); + try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero); + try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot\\", "\\bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero); + try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero); try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero); @@ -588,7 +604,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { result[0] = asciiUpper(result[0]); // Remove the trailing slash if present, eg. if the cwd is a root // directory. - if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) { + if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows_uefi) { result_index -= 1; } } @@ -625,7 +641,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { break; } } else { - result[result_index] = sep_windows; + result[result_index] = sep_windows_uefi; result_index += 1; mem.copy(u8, result[result_index..], component); result_index += component.len; From b6e1613e58bd490310bf03c1ae5d625ccf819371 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 01:14:28 -0500 Subject: [PATCH 2/7] std/os/uefi: Move FileInfo guid from FileProtocol to FileInfo The GUID on FileProtocol was for EFI_FILE_INFO, FileProtocol itself doesn't have a GUID, there are only those for the requested information types. --- lib/std/os/uefi/protocols/file_protocol.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/std/os/uefi/protocols/file_protocol.zig b/lib/std/os/uefi/protocols/file_protocol.zig index ba4fad9c75..ce779ca58b 100644 --- a/lib/std/os/uefi/protocols/file_protocol.zig +++ b/lib/std/os/uefi/protocols/file_protocol.zig @@ -127,15 +127,6 @@ pub const FileProtocol = extern struct { return self._flush(self); } - pub const guid align(8) = Guid{ - .time_low = 0x09576e92, - .time_mid = 0x6d3f, - .time_high_and_version = 0x11d2, - .clock_seq_high_and_reserved = 0x8e, - .clock_seq_low = 0x39, - .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, - }; - pub const efi_file_mode_read: u64 = 0x0000000000000001; pub const efi_file_mode_write: u64 = 0x0000000000000002; pub const efi_file_mode_create: u64 = 0x8000000000000000; @@ -171,4 +162,13 @@ pub const FileInfo = extern struct { pub const efi_file_directory: u64 = 0x0000000000000010; pub const efi_file_archive: u64 = 0x0000000000000020; pub const efi_file_valid_attr: u64 = 0x0000000000000037; + + pub const guid align(8) = Guid{ + .time_low = 0x09576e92, + .time_mid = 0x6d3f, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x39, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; }; From 608fceffc4975e5aabbb5060b81fa933c6bdf46c Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 01:16:47 -0500 Subject: [PATCH 3/7] std/os/uefi: Add FileSystemInfo --- lib/std/os/uefi/protocols.zig | 1 + lib/std/os/uefi/protocols/file_protocol.zig | 22 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/std/os/uefi/protocols.zig b/lib/std/os/uefi/protocols.zig index 353c628a05..4192b4a545 100644 --- a/lib/std/os/uefi/protocols.zig +++ b/lib/std/os/uefi/protocols.zig @@ -14,6 +14,7 @@ pub const MessagingDevicePath = @import("protocols/device_path_protocol.zig").Me pub const SimpleFileSystemProtocol = @import("protocols/simple_file_system_protocol.zig").SimpleFileSystemProtocol; pub const FileProtocol = @import("protocols/file_protocol.zig").FileProtocol; pub const FileInfo = @import("protocols/file_protocol.zig").FileInfo; +pub const FileSystemInfo = @import("protocols/file_protocol.zig").FileSystemInfo; pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey; pub const KeyData = @import("protocols/simple_text_input_ex_protocol.zig").KeyData; diff --git a/lib/std/os/uefi/protocols/file_protocol.zig b/lib/std/os/uefi/protocols/file_protocol.zig index ce779ca58b..3b53f471a9 100644 --- a/lib/std/os/uefi/protocols/file_protocol.zig +++ b/lib/std/os/uefi/protocols/file_protocol.zig @@ -172,3 +172,25 @@ pub const FileInfo = extern struct { .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, }; }; + +pub const FileSystemInfo = extern struct { + size: u64, + read_only: bool, + volume_size: u64, + free_space: u64, + block_size: u32, + _volume_label: u16, + + pub fn getVolumeLabel(self: *const FileSystemInfo) [*:0]const u16 { + return @ptrCast([*:0]const u16, &self._volume_label); + } + + pub const guid align(8) = Guid{ + .time_low = 0x09576e93, + .time_mid = 0x6d3f, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x39, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; +}; From 73e4571b4c10fdbfe04eb9b339b288b719c3469c Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 01:29:35 -0500 Subject: [PATCH 4/7] std/os/uefi: Add methods next() and size() to DevicePathProtocol These are used for more easily dealing with a series of Device Path nodes. --- .../uefi/protocols/device_path_protocol.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig index df59498822..9b7a47475c 100644 --- a/lib/std/os/uefi/protocols/device_path_protocol.zig +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -15,6 +15,25 @@ pub const DevicePathProtocol = packed struct { .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, }; + /// Returns the next DevicePathProtocol node in the sequence, if any. + pub fn next(self: *DevicePathProtocol) ?*DevicePathProtocol { + if (self.type == .End and @intToEnum(EndDevicePath.Subtype, self.subtype) == .EndEntire) + return null; + + return @ptrCast(*DevicePathProtocol, @ptrCast([*]u8, self) + self.length); + } + + /// Calculates the total length of the device path structure in bytes, including the end of device path node. + pub fn size(self: *DevicePathProtocol) usize { + var node = self; + + while (node.next()) |next_node| { + node = next_node; + } + + return (@ptrToInt(node) + node.length) - @ptrToInt(self); + } + pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath { return switch (self.type) { .Hardware => blk: { From c78a108d10838d67064e63bf7cb8b9128239a36c Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 02:51:52 -0500 Subject: [PATCH 5/7] std/os/uefi: Add create_file_device_path This allows users to add file paths to device paths, which is often used in methods like `boot_services.loadImage` and `boot_services.startImage`, which take a device path with an additional file path appended to locate the image. --- .../uefi/protocols/device_path_protocol.zig | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig index 9b7a47475c..df3812451c 100644 --- a/lib/std/os/uefi/protocols/device_path_protocol.zig +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -1,4 +1,7 @@ -const uefi = @import("std").os.uefi; +const std = @import("std"); +const mem = std.mem; +const uefi = std.os.uefi; +const Allocator = mem.Allocator; const Guid = uefi.Guid; pub const DevicePathProtocol = packed struct { @@ -34,6 +37,40 @@ pub const DevicePathProtocol = packed struct { return (@ptrToInt(node) + node.length) - @ptrToInt(self); } + /// Creates a file device path from the existing device path and a file path. + pub fn create_file_device_path(self: *DevicePathProtocol, allocator: Allocator, path: [:0]const u16) !*DevicePathProtocol { + var path_size = self.size(); + + // 2 * (path.len + 1) for the path and its null terminator, which are u16s + // DevicePathProtocol for the extra node before the end + var buf = try allocator.alloc(u8, path_size + 2 * (path.len + 1) + @sizeOf(DevicePathProtocol)); + + mem.copy(u8, buf, @ptrCast([*]const u8, self)[0..path_size]); + + // Pointer to the copy of the end node of the current chain, which is - 4 from the buffer + // as the end node itself is 4 bytes (type: u8 + subtype: u8 + length: u16). + var new = @ptrCast(*MediaDevicePath.FilePathDevicePath, buf.ptr + path_size - 4); + + new.type = .Media; + new.subtype = .FilePath; + new.length = @sizeOf(MediaDevicePath.FilePathDevicePath) + 2 * (@intCast(u16, path.len) + 1); + + // The same as new.getPath(), but not const as we're filling it in. + var ptr = @ptrCast([*:0]u16, @alignCast(2, @ptrCast([*]u8, new)) + @sizeOf(MediaDevicePath.FilePathDevicePath)); + + for (path) |s, i| + ptr[i] = s; + + ptr[path.len] = 0; + + var end = @ptrCast(*EndDevicePath.EndEntireDevicePath, @ptrCast(*DevicePathProtocol, new).next().?); + end.type = .End; + end.subtype = .EndEntire; + end.length = @sizeOf(EndDevicePath.EndEntireDevicePath); + + return @ptrCast(*DevicePathProtocol, buf.ptr); + } + pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath { return switch (self.type) { .Hardware => blk: { From b65a88416974e697f1cf2402ca7550f6225f8d39 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Tue, 11 Jan 2022 03:59:48 -0500 Subject: [PATCH 6/7] std/os/uefi: Add pool_allocator and raw_pool_allocator --- lib/std/os/uefi.zig | 7 ++ lib/std/os/uefi/pool_allocator.zig | 153 +++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 lib/std/os/uefi/pool_allocator.zig diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index b4582c121d..30f7f8a41b 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -7,6 +7,13 @@ pub const protocols = @import("uefi/protocols.zig"); pub const Status = @import("uefi/status.zig").Status; pub const tables = @import("uefi/tables.zig"); +/// The memory type to allocate when using the pool +/// Defaults to .LoaderData, the default data allocation type +/// used by UEFI applications to allocate pool memory. +pub var efi_pool_memory_type: tables.MemoryType = .LoaderData; +pub const pool_allocator = @import("uefi/pool_allocator.zig").pool_allocator; +pub const raw_pool_allocator = @import("uefi/pool_allocator.zig").raw_pool_allocator; + /// The EFI image's handle that is passed to its entry point. pub var handle: Handle = undefined; diff --git a/lib/std/os/uefi/pool_allocator.zig b/lib/std/os/uefi/pool_allocator.zig new file mode 100644 index 0000000000..3294621a18 --- /dev/null +++ b/lib/std/os/uefi/pool_allocator.zig @@ -0,0 +1,153 @@ +const std = @import("std"); + +const mem = std.mem; +const uefi = std.os.uefi; + +const assert = std.debug.assert; + +const Allocator = mem.Allocator; + +const UefiPoolAllocator = struct { + fn getHeader(ptr: [*]u8) *[*]align(8) u8 { + return @intToPtr(*[*]align(8) u8, @ptrToInt(ptr) - @sizeOf(usize)); + } + + fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 { + var unaligned_ptr: [*]align(8) u8 = undefined; + + if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &unaligned_ptr) != .Success) + return null; + + const unaligned_addr = @ptrToInt(unaligned_ptr); + const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment); + + var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); + getHeader(aligned_ptr).* = unaligned_ptr; + + return aligned_ptr; + } + + fn alignedFree(ptr: [*]u8) void { + _ = uefi.system_table.boot_services.?.freePool(getHeader(ptr).*); + } + + fn alloc( + _: *anyopaque, + len: usize, + ptr_align: u29, + len_align: u29, + ret_addr: usize, + ) Allocator.Error![]u8 { + _ = ret_addr; + + assert(len > 0); + assert(std.math.isPowerOfTwo(ptr_align)); + + var ptr = alignedAlloc(len, ptr_align) orelse return error.OutOfMemory; + + if (len_align == 0) + return ptr[0..len]; + + return ptr[0..mem.alignBackwardAnyAlign(len, len_align)]; + } + + fn resize( + _: *anyopaque, + buf: []u8, + buf_align: u29, + new_len: usize, + len_align: u29, + ret_addr: usize, + ) ?usize { + _ = buf_align; + _ = ret_addr; + + return if (new_len <= buf.len) mem.alignAllocLen(buf.len, new_len, len_align) else null; + } + + fn free( + _: *anyopaque, + buf: []u8, + buf_align: u29, + ret_addr: usize, + ) void { + _ = buf_align; + _ = ret_addr; + alignedFree(buf.ptr); + } +}; + +/// Supports the full Allocator interface, including alignment. +/// For a direct call of `allocatePool`, see `raw_pool_allocator`. +pub const pool_allocator = Allocator{ + .ptr = undefined, + .vtable = &pool_allocator_vtable, +}; + +const pool_allocator_vtable = Allocator.VTable{ + .alloc = UefiPoolAllocator.alloc, + .resize = UefiPoolAllocator.resize, + .free = UefiPoolAllocator.free, +}; + +/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. +pub const raw_pool_allocator = Allocator{ + .ptr = undefined, + .vtable = &raw_pool_allocator_table, +}; + +const raw_pool_allocator_table = Allocator.VTable{ + .alloc = uefi_alloc, + .resize = uefi_resize, + .free = uefi_free, +}; + +fn uefi_alloc( + _: *anyopaque, + len: usize, + ptr_align: u29, + len_align: u29, + ret_addr: usize, +) Allocator.Error![]u8 { + _ = len_align; + _ = ret_addr; + + std.debug.assert(ptr_align <= 8); + + var ptr: [*]align(8) u8 = undefined; + + if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &ptr) != .Success) { + return error.OutOfMemory; + } + + return ptr[0..len]; +} + +fn uefi_resize( + _: *anyopaque, + buf: []u8, + old_align: u29, + new_len: usize, + len_align: u29, + ret_addr: usize, +) ?usize { + _ = old_align; + _ = ret_addr; + + if (new_len <= buf.len) { + return mem.alignAllocLen(buf.len, new_len, len_align); + } + + return null; +} + +fn uefi_free( + _: *anyopaque, + buf: []u8, + buf_align: u29, + ret_addr: usize, +) void { + _ = buf_align; + _ = ret_addr; + _ = uefi.system_table.boot_services.?.freePool(@alignCast(8, buf.ptr)); +} From 3d89ff51300454467a39427b938110afe2d1039c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Jan 2022 11:00:19 -0700 Subject: [PATCH 7/7] std.fs.path: revert recent public API change 41fd343508880ffdfbc83c7b053237da09199f02 made a breaking change to the public API; this commit reverts the API changes but keeps the improved logic. --- lib/std/fs/path.zig | 22 ++++++++++++++-------- lib/std/os/uefi.zig | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 94593fe7cb..fbf5bd99ee 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -12,13 +12,19 @@ const fs = std.fs; const process = std.process; const native_os = builtin.target.os.tag; -pub const sep_windows_uefi = '\\'; +pub const sep_windows = '\\'; pub const sep_posix = '/'; -pub const sep = if (native_os == .windows or native_os == .uefi) sep_windows_uefi else sep_posix; +pub const sep = switch (native_os) { + .windows, .uefi => sep_windows, + else => sep_posix, +}; -pub const sep_str_windows_uefi = "\\"; +pub const sep_str_windows = "\\"; pub const sep_str_posix = "/"; -pub const sep_str = if (native_os == .windows or native_os == .uefi) sep_str_windows_uefi else sep_str_posix; +pub const sep_str = switch (native_os) { + .windows, .uefi => sep_str_windows, + else => sep_str_posix, +}; pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; @@ -116,7 +122,7 @@ fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: boo return byte == '\\'; } }.isSep; - const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, uefiIsSep, paths, zero); + const actual = try joinSepMaybeZ(testing.allocator, sep_windows, uefiIsSep, paths, zero); defer testing.allocator.free(actual); try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); } @@ -127,7 +133,7 @@ fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: return byte == '/' or byte == '\\'; } }.isSep; - const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, windowsIsSep, paths, zero); + const actual = try joinSepMaybeZ(testing.allocator, sep_windows, windowsIsSep, paths, zero); defer testing.allocator.free(actual); try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual); } @@ -604,7 +610,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { result[0] = asciiUpper(result[0]); // Remove the trailing slash if present, eg. if the cwd is a root // directory. - if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows_uefi) { + if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) { result_index -= 1; } } @@ -641,7 +647,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { break; } } else { - result[result_index] = sep_windows_uefi; + result[result_index] = sep_windows; result_index += 1; mem.copy(u8, result[result_index..], component); result_index += component.len; diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 30f7f8a41b..4c034ed456 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -8,7 +8,7 @@ pub const Status = @import("uefi/status.zig").Status; pub const tables = @import("uefi/tables.zig"); /// The memory type to allocate when using the pool -/// Defaults to .LoaderData, the default data allocation type +/// Defaults to .LoaderData, the default data allocation type /// used by UEFI applications to allocate pool memory. pub var efi_pool_memory_type: tables.MemoryType = .LoaderData; pub const pool_allocator = @import("uefi/pool_allocator.zig").pool_allocator;