Merge pull request 'compiler: use Io.MemoryMap' (#30961) from use-mmap into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30961
This commit is contained in:
Andrew Kelley
2026-01-26 22:58:01 +01:00
8 changed files with 106 additions and 222 deletions
+1 -1
View File
@@ -656,7 +656,7 @@ pub const VTable = struct {
fileMemoryMapCreate: *const fn (?*anyopaque, File, File.MemoryMap.CreateOptions) File.MemoryMap.CreateError!File.MemoryMap,
fileMemoryMapDestroy: *const fn (?*anyopaque, *File.MemoryMap) void,
fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, File.MemoryMap.CreateOptions) File.MemoryMap.SetLengthError!void,
fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, usize) File.MemoryMap.SetLengthError!void,
fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void,
fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void,
+5 -11
View File
@@ -74,6 +74,9 @@ pub fn destroy(mm: *MemoryMap, io: Io) void {
}
pub const SetLengthError = error{
/// Changing the mapping length could not be done atomically. Caller must
/// use `destroy` and `create` to resize the mapping.
OperationUnsupported,
/// One of the following:
/// * The `File.Kind` is not `file`.
/// * The file is not open for reading and read access protections enabled.
@@ -91,17 +94,8 @@ pub const SetLengthError = error{
/// of the file after calling this is unspecified until `write` is called.
///
/// May change the pointer address of `memory`.
///
/// `options` is needed because the mapping may need to be destroyed and
/// re-created. All the same options must be provided except for `len` which is
/// the new length.
///
/// This operation cannot be completed atomically on all operating systems.
/// When this function fails, the `MemoryMap` may be left in an unmapped state,
/// which can be detected by checking if `memory.len` is zero. In such case it
/// is safe to call `destroy` which will have no effect.
pub fn setLength(mm: *MemoryMap, io: Io, options: CreateOptions) SetLengthError!void {
return io.vtable.fileMemoryMapSetLength(io.userdata, mm, options);
pub fn setLength(mm: *MemoryMap, io: Io, new_len: usize) SetLengthError!void {
return io.vtable.fileMemoryMapSetLength(io.userdata, mm, new_len);
}
/// Synchronizes the contents of `memory` from `file`.
+3 -33
View File
@@ -16597,27 +16597,21 @@ fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void {
fn fileMemoryMapSetLength(
userdata: ?*anyopaque,
mm: *File.MemoryMap,
options: File.MemoryMap.CreateOptions,
new_len: usize,
) File.MemoryMap.SetLengthError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const page_size = std.heap.pageSize();
const alignment: Alignment = .fromByteUnits(page_size);
const page_align = std.heap.page_size_min;
const old_memory = mm.memory;
const new_len = options.len;
if (mm.section) |section| {
_ = section;
if (alignment.forward(new_len) == alignment.forward(old_memory.len)) {
mm.memory.len = new_len;
return;
}
switch (native_os) {
.windows => {
_ = windows.ntdll.NtUnmapViewOfSection(windows.current_process, old_memory.ptr);
windows.CloseHandle(section);
mm.section = windows.INVALID_HANDLE_VALUE;
mm.memory = &.{};
},
.wasi => unreachable,
.linux => {
const flags: posix.MREMAP = .{ .MAYMOVE = true };
@@ -16647,31 +16641,7 @@ fn fileMemoryMapSetLength(
mm.memory = new_memory;
return;
},
else => {
switch (posix.errno(posix.system.munmap(old_memory.ptr, old_memory.len))) {
.SUCCESS => {},
else => |e| {
if (builtin.mode == .Debug) std.log.err("failed to unmap {d} bytes at {*}: {t}", .{
old_memory.len, old_memory.ptr, e,
});
// munmap must be infallible, or we cannot design reliable software.
return error.Unexpected;
},
}
mm.memory = &.{};
},
}
if (createFileMap(mm.file, options.protection, mm.offset, options.populate, new_len)) |result| {
mm.* = result;
return;
} else |err| switch (err) {
error.OperationUnsupported,
error.Unseekable,
error.SectionOversize,
error.MappingAlreadyExists,
error.FileLockConflict,
=> return error.Unexpected, // It worked before on the same open file.
else => |e| return e,
else => return error.OperationUnsupported,
}
} else {
const gpa = t.allocator;
+8 -1
View File
@@ -260,7 +260,14 @@ test "memory mapping fallback" {
try testing.expectEqualStrings("this9is9my", mm.memory);
try mm.setLength(io, .{ .len = "this9is9my data123".len });
const new_len = "this9is9my data123".len;
mm.setLength(io, new_len) catch |err| switch (err) {
error.OperationUnsupported => {
mm.destroy(io);
mm = try file.createMemoryMap(io, .{ .len = new_len });
},
else => |e| return e,
};
try mm.read(io);
try testing.expectEqualStrings("this9is9my data123", mm.memory);
+8 -3
View File
@@ -643,9 +643,14 @@ test "memory mapping" {
try expectEqualStrings("this9is9my", mm.memory);
// Cross a page boundary to require an actual remap.
try mm.setLength(io, .{
.len = std.heap.pageSize() * 2,
});
const new_len = std.heap.pageSize() * 2;
mm.setLength(io, new_len) catch |err| switch (err) {
error.OperationUnsupported => {
mm.destroy(io);
mm = try file.createMemoryMap(io, .{ .len = new_len });
},
else => |e| return e,
};
try mm.read(io);
try expectEqualStrings("this9is9my data123\x00\x00", mm.memory[0.."this9is9my data123\x00\x00".len]);
+5 -5
View File
@@ -651,10 +651,10 @@ pub const File = struct {
&coff.mf
else
unreachable;
mf.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
.mode = .read_write,
});
base.file = mf.file;
base.file = mf.memory_map.file;
try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
},
.c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
@@ -729,9 +729,9 @@ pub const File = struct {
else
unreachable;
mf.unmap();
assert(mf.file.handle == f.handle);
mf.file.close(io);
mf.file = undefined;
assert(mf.memory_map.file.handle == f.handle);
mf.memory_map.file.close(io);
mf.memory_map.file = undefined;
base.file = null;
},
.c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
+10 -5
View File
@@ -1691,10 +1691,10 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
}
pub fn identClass(elf: *const Elf) std.elf.CLASS {
return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);
}
pub fn identData(elf: *const Elf) std.elf.DATA {
return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);
}
pub fn targetEndian(elf: *const Elf) std.builtin.Endian {
@@ -2102,7 +2102,7 @@ fn loadObject(
log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
const ident = try r.peek(std.elf.EI.OSABI);
if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
return diags.failParse(path, "bad ident", .{});
try elf.symtab.ensureUnusedCapacity(gpa, 1);
try elf.inputs.ensureUnusedCapacity(gpa, 1);
@@ -2341,7 +2341,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
log.debug("loadDso({f})", .{path.fmtEscapeString()});
const ident = try r.peek(std.elf.EI.NIDENT);
if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
return diags.failParse(path, "bad ident", .{});
const target_endian = elf.targetEndian();
switch (elf.identClass()) {
@@ -3090,9 +3090,14 @@ pub fn flush(
tid: Zcu.PerThread.Id,
prog_node: std.Progress.Node,
) !void {
const comp = elf.base.comp;
_ = arena;
_ = prog_node;
while (try elf.idle(tid)) {}
elf.mf.flush() catch |err| switch (err) {
error.Canceled => |e| return e,
else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
};
}
pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
@@ -3839,7 +3844,7 @@ pub fn printNode(
const line_len = 0x10;
var line_it = std.mem.window(
u8,
elf.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
elf.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
line_len,
line_len,
);
+66 -163
View File
@@ -11,15 +11,13 @@ const linux = std.os.linux;
const windows = std.os.windows;
io: Io,
file: std.Io.File,
flags: packed struct {
block_size: std.mem.Alignment,
copy_file_range_unsupported: bool,
fallocate_punch_hole_unsupported: bool,
fallocate_insert_range_unsupported: bool,
},
section: if (is_windows) windows.HANDLE else void,
contents: []align(std.heap.page_size_min) u8,
memory_map: Io.File.MemoryMap,
nodes: std.ArrayList(Node),
free_ni: Node.Index,
large: std.ArrayList(u64),
@@ -29,26 +27,20 @@ writers: std.SinglyLinkedList,
pub const growth_factor = 4;
pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.LengthError || error{
pub const Error = error{
NotFile,
SystemResources,
IsDir,
Unseekable,
NoSpaceLeft,
} || Io.File.MemoryMap.CreateError || Io.File.MemoryMap.SetLengthError || Io.File.WritePositionalError;
InputOutput,
FileTooBig,
FileBusy,
NonResizable,
};
pub fn init(file: std.Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile {
pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile {
var mf: MappedFile = .{
.io = io,
.file = file,
.flags = undefined,
.section = if (is_windows) windows.INVALID_HANDLE_VALUE else {},
.contents = &.{},
.memory_map = .{
.file = file,
.memory = &.{},
.offset = 0,
.section = null,
},
.nodes = .empty,
.free_ni = .none,
.large = .empty,
@@ -58,61 +50,9 @@ pub fn init(file: std.Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile {
};
errdefer mf.deinit(gpa);
const size: u64, const block_size = stat: {
if (is_windows) {
var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
break :stat .{
try windows.GetFileSizeEx(file.handle),
switch (windows.ntdll.NtQuerySystemInformation(
.SystemBasicInformation,
&sbi,
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
null,
)) {
.SUCCESS => @max(sbi.PageSize, sbi.AllocationGranularity),
else => std.heap.page_size_max,
},
};
}
if (is_linux) {
const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
.{ .major = 30, .minor = 0, .patch = 0 }
else
.{ .major = 2, .minor = 28, .patch = 0 });
const sys = if (use_c) std.c else std.os.linux;
while (true) {
var statx = std.mem.zeroes(linux.Statx);
const rc = sys.statx(
mf.file.handle,
"",
std.posix.AT.EMPTY_PATH,
.{ .TYPE = true, .SIZE = true, .BLOCKS = true },
&statx,
);
switch (sys.errno(rc)) {
.SUCCESS => {
assert(statx.mask.TYPE);
assert(statx.mask.SIZE);
assert(statx.mask.BLOCKS);
if (!std.posix.S.ISREG(statx.mode)) return error.PathAlreadyExists;
break :stat .{ statx.size, @max(std.heap.pageSize(), statx.blksize) };
},
.INTR => continue,
.ACCES => return error.AccessDenied,
.BADF => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
.FAULT => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
.INVAL => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
.LOOP => return error.SymLinkLoop,
.NAMETOOLONG => return error.NameTooLong,
.NOENT => return error.FileNotFound,
.NOTDIR => return error.FileNotFound,
.NOMEM => return error.SystemResources,
else => |err| return std.posix.unexpectedErrno(err),
}
}
}
const stat = try std.posix.fstat(mf.file.handle);
if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists;
break :stat .{ @bitCast(stat.size), @max(std.heap.pageSize(), stat.blksize) };
const stat = try file.stat(io);
if (stat.kind != .file) return error.PathAlreadyExists;
break :stat .{ stat.size, @max(std.heap.pageSize(), stat.block_size) };
};
mf.flags = .{
.block_size = .fromByteUnits(std.math.ceilPowerOfTwoAssert(usize, block_size)),
@@ -348,12 +288,12 @@ pub const Node = extern struct {
pub fn slice(ni: Node.Index, mf: *const MappedFile) []u8 {
const file_loc = ni.fileLocation(mf, true);
return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
}
pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 {
const file_loc = ni.fileLocation(mf, false);
return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
}
pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) !void {
@@ -396,7 +336,7 @@ pub const Node = extern struct {
mf: *MappedFile,
writer_node: std.SinglyLinkedList.Node,
ni: Node.Index,
interface: std.Io.Writer,
interface: Io.Writer,
err: ?Error,
pub fn deinit(w: *Writer) void {
@@ -404,18 +344,18 @@ pub const Node = extern struct {
w.* = undefined;
}
const vtable: std.Io.Writer.VTable = .{
const vtable: Io.Writer.VTable = .{
.drain = drain,
.sendFile = sendFile,
.flush = std.Io.Writer.noopFlush,
.flush = Io.Writer.noopFlush,
.rebase = growingRebase,
};
fn drain(
interface: *std.Io.Writer,
interface: *Io.Writer,
data: []const []const u8,
splat: usize,
) std.Io.Writer.Error!usize {
) Io.Writer.Error!usize {
const pattern = data[data.len - 1];
const splat_len = pattern.len * splat;
const start_len = interface.end;
@@ -442,10 +382,10 @@ pub const Node = extern struct {
}
fn sendFile(
interface: *std.Io.Writer,
file_reader: *std.Io.File.Reader,
limit: std.Io.Limit,
) std.Io.Writer.FileError!usize {
interface: *Io.Writer,
file_reader: *Io.File.Reader,
limit: Io.Limit,
) Io.Writer.FileError!usize {
if (limit == .nothing) return 0;
const pos = file_reader.logicalPos();
const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
@@ -489,10 +429,10 @@ pub const Node = extern struct {
}
fn growingRebase(
interface: *std.Io.Writer,
interface: *Io.Writer,
preserve: usize,
unused_capacity: usize,
) std.Io.Writer.Error!void {
) Io.Writer.Error!void {
_ = preserve;
const total_capacity = interface.end + unused_capacity;
if (interface.buffer.len >= total_capacity) return;
@@ -661,7 +601,8 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
// Resize the entire file
if (ni == Node.Index.root) {
try mf.ensureCapacityForSetLocation(gpa);
try mf.file.setLength(io, new_size);
try mf.memory_map.write(io);
try mf.memory_map.file.setLength(io, new_size);
try mf.ensureTotalCapacity(@intCast(new_size));
ni.setLocationAssumeCapacity(mf, old_offset, new_size);
return;
@@ -685,6 +626,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
node.flags.alignment.order(mf.flags.block_size).compare(.gte))
insert_range: {
try mf.memory_map.write(io);
// Ask the filesystem driver to insert extents into the file without copying any data
const last_offset, const last_size = parent.last.location(mf).resolve(mf);
const last_end = last_offset + last_size;
@@ -696,12 +638,12 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
_, const file_size = Node.Index.root.location(mf).resolve(mf);
while (true) switch (linux.errno(switch (std.math.order(range_file_offset, file_size)) {
.lt => linux.fallocate(
mf.file.handle,
mf.memory_map.file.handle,
linux.FALLOC.FL_INSERT_RANGE,
@intCast(range_file_offset),
@intCast(range_size),
),
.eq => linux.ftruncate(mf.file.handle, @intCast(range_file_offset + range_size)),
.eq => linux.ftruncate(mf.memory_map.file.handle, @intCast(range_file_offset + range_size)),
.gt => unreachable,
})) {
.SUCCESS => {
@@ -908,7 +850,7 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and
size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)
switch (linux.errno(linux.fallocate(
mf.file.handle,
mf.memory_map.file.handle,
linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
@intCast(old_file_offset),
@intCast(size),
@@ -928,24 +870,26 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
.TXTBSY => return error.FileBusy,
else => |e| return std.posix.unexpectedErrno(e),
};
@memset(mf.contents[@intCast(old_file_offset)..][0..@intCast(size)], 0);
@memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);
}
fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
const copy_size = try mf.copyFileRange(mf.file, old_file_offset, new_file_offset, size);
const copy_size = try mf.copyFileRange(mf.memory_map.file, old_file_offset, new_file_offset, size);
if (copy_size < size) @memcpy(
mf.contents[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
mf.contents[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
mf.memory_map.memory[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
mf.memory_map.memory[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
);
}
fn copyFileRange(
mf: *MappedFile,
old_file: std.Io.File,
old_file: Io.File,
old_file_offset: u64,
new_file_offset: u64,
size: u64,
) !u64 {
const io = mf.io;
try mf.memory_map.write(io);
var remaining_size = size;
if (is_linux and !mf.flags.copy_file_range_unsupported) {
var old_file_offset_mut: i64 = @intCast(old_file_offset);
@@ -954,7 +898,7 @@ fn copyFileRange(
const copy_len = linux.copy_file_range(
old_file.handle,
&old_file_offset_mut,
mf.file.handle,
mf.memory_map.file.handle,
&new_file_offset_mut,
@intCast(remaining_size),
0,
@@ -990,82 +934,41 @@ fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void {
}
pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void {
if (mf.contents.len >= new_capacity) return;
if (mf.memory_map.memory.len >= new_capacity) return;
try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / growth_factor);
}
pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {
if (mf.contents.len >= new_capacity) return;
if (mf.memory_map.memory.len >= new_capacity) return;
const io = mf.io;
const aligned_capacity = mf.flags.block_size.forward(new_capacity);
if (!is_linux) mf.unmap() else if (mf.contents.len > 0) {
mf.contents = try std.posix.mremap(
mf.contents.ptr,
mf.contents.len,
aligned_capacity,
.{ .MAYMOVE = true },
null,
);
return;
}
if (is_windows) {
if (mf.section == windows.INVALID_HANDLE_VALUE) switch (windows.ntdll.NtCreateSection(
&mf.section,
.{
.SPECIFIC = .{ .SECTION = .{
.QUERY = true,
.MAP_WRITE = true,
.MAP_READ = true,
.EXTEND_SIZE = true,
} },
.STANDARD = .{ .RIGHTS = .REQUIRED },
},
null,
@constCast(&@as(i64, @intCast(aligned_capacity))),
.{ .READWRITE = true },
.{ .COMMIT = true },
mf.file.handle,
)) {
.SUCCESS => {},
else => return error.MemoryMappingNotSupported,
};
var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null;
var contents_len = aligned_capacity;
switch (windows.ntdll.NtMapViewOfSection(
mf.section,
windows.GetCurrentProcess(),
@ptrCast(&contents_ptr),
null,
0,
null,
&contents_len,
.Unmap,
.{},
.{ .READWRITE = true },
)) {
.SUCCESS => mf.contents = contents_ptr.?[0..contents_len],
else => return error.MemoryMappingNotSupported,
if (mf.memory_map.memory.len > 0) {
if (mf.memory_map.setLength(io, aligned_capacity)) |_| {
return;
} else |err| switch (err) {
error.OperationUnsupported => {},
else => |e| return e,
}
} else mf.contents = try std.posix.mmap(
null,
aligned_capacity,
.{ .READ = true, .WRITE = true },
.{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },
mf.file.handle,
0,
);
unmap(mf);
}
const file = mf.memory_map.file;
mf.memory_map = try .create(io, file, .{ .len = aligned_capacity });
}
pub fn unmap(mf: *MappedFile) void {
if (mf.contents.len == 0) return;
if (is_windows)
_ = windows.ntdll.NtUnmapViewOfSection(windows.GetCurrentProcess(), mf.contents.ptr)
else
std.posix.munmap(mf.contents);
mf.contents = &.{};
if (is_windows and mf.section != windows.INVALID_HANDLE_VALUE) {
windows.CloseHandle(mf.section);
mf.section = windows.INVALID_HANDLE_VALUE;
}
if (mf.memory_map.memory.len == 0) return;
const io = mf.io;
const file = mf.memory_map.file;
mf.memory_map.destroy(io);
mf.memory_map.memory = &.{};
mf.memory_map.file = file;
}
pub fn flush(mf: *MappedFile) Io.File.WritePositionalError!void {
const io = mf.io;
try mf.memory_map.write(io);
}
fn verify(mf: *MappedFile) void {