authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-22 19:41:13-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-22 21:25:53-08:00
log499ba5d55c00b9d32691dc9ff49db49ba6bbded6
tree084bffcbb487962e05341ea5761858eced39e30d
parent193c747b03da8ec7af55cb5fcb53f8f1bee39d5c

compiler: use Io.MemoryMap

Also make setLength return error.OperationUnsupported when it cannot be done atomically.

8 files changed, 93 insertions(+), 209 deletions(-)

lib/std/Io.zig+1-1
......@@ -656,7 +656,7 @@ pub const VTable = struct {
656656
657657 fileMemoryMapCreate: *const fn (?*anyopaque, File, File.MemoryMap.CreateOptions) File.MemoryMap.CreateError!File.MemoryMap,
658658 fileMemoryMapDestroy: *const fn (?*anyopaque, *File.MemoryMap) void,
659 fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, File.MemoryMap.CreateOptions) File.MemoryMap.SetLengthError!void,
659 fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, usize) File.MemoryMap.SetLengthError!void,
660660 fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void,
661661 fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void,
662662
lib/std/Io/File/MemoryMap.zig+5-11
......@@ -74,6 +74,9 @@ pub fn destroy(mm: *MemoryMap, io: Io) void {
7474}
7575
7676pub const SetLengthError = error{
77 /// Changing the mapping length could not be done atomically. Caller must
78 /// use `destroy` and `create` to resize the mapping.
79 OperationUnsupported,
7780 /// One of the following:
7881 /// * The `File.Kind` is not `file`.
7982 /// * The file is not open for reading and read access protections enabled.
......@@ -91,17 +94,8 @@ pub const SetLengthError = error{
9194/// of the file after calling this is unspecified until `write` is called.
9295///
9396/// May change the pointer address of `memory`.
94///
95/// `options` is needed because the mapping may need to be destroyed and
96/// re-created. All the same options must be provided except for `len` which is
97/// the new length.
98///
99/// This operation cannot be completed atomically on all operating systems.
100/// When this function fails, the `MemoryMap` may be left in an unmapped state,
101/// which can be detected by checking if `memory.len` is zero. In such case it
102/// is safe to call `destroy` which will have no effect.
103pub fn setLength(mm: *MemoryMap, io: Io, options: CreateOptions) SetLengthError!void {
104 return io.vtable.fileMemoryMapSetLength(io.userdata, mm, options);
97pub fn setLength(mm: *MemoryMap, io: Io, new_len: usize) SetLengthError!void {
98 return io.vtable.fileMemoryMapSetLength(io.userdata, mm, new_len);
10599}
106100
107101/// Synchronizes the contents of `memory` from `file`.
lib/std/Io/Threaded.zig+3-33
......@@ -16466,27 +16466,21 @@ fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void {
1646616466fn fileMemoryMapSetLength(
1646716467 userdata: ?*anyopaque,
1646816468 mm: *File.MemoryMap,
16469 options: File.MemoryMap.CreateOptions,
16469 new_len: usize,
1647016470) File.MemoryMap.SetLengthError!void {
1647116471 const t: *Threaded = @ptrCast(@alignCast(userdata));
1647216472 const page_size = std.heap.pageSize();
1647316473 const alignment: Alignment = .fromByteUnits(page_size);
1647416474 const page_align = std.heap.page_size_min;
1647516475 const old_memory = mm.memory;
16476 const new_len = options.len;
1647716476
1647816477 if (mm.section) |section| {
16478 _ = section;
1647916479 if (alignment.forward(new_len) == alignment.forward(old_memory.len)) {
1648016480 mm.memory.len = new_len;
1648116481 return;
1648216482 }
1648316483 switch (native_os) {
16484 .windows => {
16485 _ = windows.ntdll.NtUnmapViewOfSection(windows.current_process, old_memory.ptr);
16486 windows.CloseHandle(section);
16487 mm.section = windows.INVALID_HANDLE_VALUE;
16488 mm.memory = &.{};
16489 },
1649016484 .wasi => unreachable,
1649116485 .linux => {
1649216486 const flags: posix.MREMAP = .{ .MAYMOVE = true };
......@@ -16516,31 +16510,7 @@ fn fileMemoryMapSetLength(
1651616510 mm.memory = new_memory;
1651716511 return;
1651816512 },
16519 else => {
16520 switch (posix.errno(posix.system.munmap(old_memory.ptr, old_memory.len))) {
16521 .SUCCESS => {},
16522 else => |e| {
16523 if (builtin.mode == .Debug) std.log.err("failed to unmap {d} bytes at {*}: {t}", .{
16524 old_memory.len, old_memory.ptr, e,
16525 });
16526 // munmap must be infallible, or we cannot design reliable software.
16527 return error.Unexpected;
16528 },
16529 }
16530 mm.memory = &.{};
16531 },
16532 }
16533 if (createFileMap(mm.file, options.protection, mm.offset, options.populate, new_len)) |result| {
16534 mm.* = result;
16535 return;
16536 } else |err| switch (err) {
16537 error.OperationUnsupported,
16538 error.Unseekable,
16539 error.SectionOversize,
16540 error.MappingAlreadyExists,
16541 error.FileLockConflict,
16542 => return error.Unexpected, // It worked before on the same open file.
16543 else => |e| return e,
16513 else => return error.OperationUnsupported,
1654416514 }
1654516515 } else {
1654616516 const gpa = t.allocator;
lib/std/Io/Threaded/test.zig+8-1
......@@ -260,7 +260,14 @@ test "memory mapping fallback" {
260260
261261 try testing.expectEqualStrings("this9is9my", mm.memory);
262262
263 try mm.setLength(io, .{ .len = "this9is9my data123".len });
263 const new_len = "this9is9my data123".len;
264 mm.setLength(io, new_len) catch |err| switch (err) {
265 error.OperationUnsupported => {
266 mm.destroy(io);
267 mm = try file.createMemoryMap(io, .{ .len = new_len });
268 },
269 else => |e| return e,
270 };
264271 try mm.read(io);
265272
266273 try testing.expectEqualStrings("this9is9my data123", mm.memory);
lib/std/Io/test.zig+8-3
......@@ -643,9 +643,14 @@ test "memory mapping" {
643643 try expectEqualStrings("this9is9my", mm.memory);
644644
645645 // Cross a page boundary to require an actual remap.
646 try mm.setLength(io, .{
647 .len = std.heap.pageSize() * 2,
648 });
646 const new_len = std.heap.pageSize() * 2;
647 mm.setLength(io, new_len) catch |err| switch (err) {
648 error.OperationUnsupported => {
649 mm.destroy(io);
650 mm = try file.createMemoryMap(io, .{ .len = new_len });
651 },
652 else => |e| return e,
653 };
649654 try mm.read(io);
650655
651656 try expectEqualStrings("this9is9my data123\x00\x00", mm.memory[0.."this9is9my data123\x00\x00".len]);
src/link.zig+5-5
......@@ -651,10 +651,10 @@ pub const File = struct {
651651 &coff.mf
652652 else
653653 unreachable;
654 mf.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
654 mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{
655655 .mode = .read_write,
656656 });
657 base.file = mf.file;
657 base.file = mf.memory_map.file;
658658 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
659659 },
660660 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
......@@ -729,9 +729,9 @@ pub const File = struct {
729729 else
730730 unreachable;
731731 mf.unmap();
732 assert(mf.file.handle == f.handle);
733 mf.file.close(io);
734 mf.file = undefined;
732 assert(mf.memory_map.file.handle == f.handle);
733 mf.memory_map.file.close(io);
734 mf.memory_map.file = undefined;
735735 base.file = null;
736736 },
737737 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
src/link/Elf2.zig+10-5
......@@ -1691,10 +1691,10 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
16911691}
16921692
16931693pub fn identClass(elf: *const Elf) std.elf.CLASS {
1694 return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
1694 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);
16951695}
16961696pub fn identData(elf: *const Elf) std.elf.DATA {
1697 return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
1697 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);
16981698}
16991699
17001700pub fn targetEndian(elf: *const Elf) std.builtin.Endian {
......@@ -2102,7 +2102,7 @@ fn loadObject(
21022102 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
21032103 const ident = try r.peek(std.elf.EI.OSABI);
21042104 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2105 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
2105 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
21062106 return diags.failParse(path, "bad ident", .{});
21072107 try elf.symtab.ensureUnusedCapacity(gpa, 1);
21082108 try elf.inputs.ensureUnusedCapacity(gpa, 1);
......@@ -2341,7 +2341,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
23412341 log.debug("loadDso({f})", .{path.fmtEscapeString()});
23422342 const ident = try r.peek(std.elf.EI.NIDENT);
23432343 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2344 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
2344 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
23452345 return diags.failParse(path, "bad ident", .{});
23462346 const target_endian = elf.targetEndian();
23472347 switch (elf.identClass()) {
......@@ -3090,9 +3090,14 @@ pub fn flush(
30903090 tid: Zcu.PerThread.Id,
30913091 prog_node: std.Progress.Node,
30923092) !void {
3093 const comp = elf.base.comp;
30933094 _ = arena;
30943095 _ = prog_node;
30953096 while (try elf.idle(tid)) {}
3097 elf.mf.flush() catch |err| switch (err) {
3098 error.Canceled => |e| return e,
3099 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
3100 };
30963101}
30973102
30983103pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
......@@ -3839,7 +3844,7 @@ pub fn printNode(
38393844 const line_len = 0x10;
38403845 var line_it = std.mem.window(
38413846 u8,
3842 elf.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
3847 elf.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
38433848 line_len,
38443849 line_len,
38453850 );
src/link/MappedFile.zig+53-150
......@@ -11,15 +11,13 @@ const linux = std.os.linux;
1111const windows = std.os.windows;
1212
1313io: Io,
14file: Io.File,
1514flags: packed struct {
1615 block_size: std.mem.Alignment,
1716 copy_file_range_unsupported: bool,
1817 fallocate_punch_hole_unsupported: bool,
1918 fallocate_insert_range_unsupported: bool,
2019},
21section: if (is_windows) windows.HANDLE else void,
22contents: []align(std.heap.page_size_min) u8,
20memory_map: Io.File.MemoryMap,
2321nodes: std.ArrayList(Node),
2422free_ni: Node.Index,
2523large: std.ArrayList(u64),
......@@ -29,26 +27,20 @@ writers: std.SinglyLinkedList,
2927
3028pub const growth_factor = 4;
3129
32pub const Error = Io.File.MemoryMap.CreateError || Io.File.LengthError || error{
30pub const Error = error{
3331 NotFile,
34 SystemResources,
35 IsDir,
36 Unseekable,
37 NoSpaceLeft,
38
39 InputOutput,
40 FileTooBig,
41 FileBusy,
42 NonResizable,
43};
32} || Io.File.MemoryMap.CreateError || Io.File.MemoryMap.SetLengthError || Io.File.WritePositionalError;
4433
4534pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile {
4635 var mf: MappedFile = .{
4736 .io = io,
48 .file = file,
4937 .flags = undefined,
50 .section = if (is_windows) windows.INVALID_HANDLE_VALUE else {},
51 .contents = &.{},
38 .memory_map = .{
39 .file = file,
40 .memory = &.{},
41 .offset = 0,
42 .section = null,
43 },
5244 .nodes = .empty,
5345 .free_ni = .none,
5446 .large = .empty,
......@@ -58,61 +50,9 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile {
5850 };
5951 errdefer mf.deinit(gpa);
6052 const size: u64, const block_size = stat: {
61 if (is_windows) {
62 var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
63 break :stat .{
64 try windows.GetFileSizeEx(file.handle),
65 switch (windows.ntdll.NtQuerySystemInformation(
66 .SystemBasicInformation,
67 &sbi,
68 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
69 null,
70 )) {
71 .SUCCESS => @max(sbi.PageSize, sbi.AllocationGranularity),
72 else => std.heap.page_size_max,
73 },
74 };
75 }
76 if (is_linux) {
77 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
78 .{ .major = 30, .minor = 0, .patch = 0 }
79 else
80 .{ .major = 2, .minor = 28, .patch = 0 });
81 const sys = if (use_c) std.c else std.os.linux;
82 while (true) {
83 var statx = std.mem.zeroes(linux.Statx);
84 const rc = sys.statx(
85 mf.file.handle,
86 "",
87 std.posix.AT.EMPTY_PATH,
88 .{ .TYPE = true, .SIZE = true, .BLOCKS = true },
89 &statx,
90 );
91 switch (sys.errno(rc)) {
92 .SUCCESS => {
93 assert(statx.mask.TYPE);
94 assert(statx.mask.SIZE);
95 assert(statx.mask.BLOCKS);
96 if (!std.posix.S.ISREG(statx.mode)) return error.PathAlreadyExists;
97 break :stat .{ statx.size, @max(std.heap.pageSize(), statx.blksize) };
98 },
99 .INTR => continue,
100 .ACCES => return error.AccessDenied,
101 .BADF => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
102 .FAULT => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
103 .INVAL => if (std.debug.runtime_safety) unreachable else return error.Unexpected,
104 .LOOP => return error.SymLinkLoop,
105 .NAMETOOLONG => return error.NameTooLong,
106 .NOENT => return error.FileNotFound,
107 .NOTDIR => return error.FileNotFound,
108 .NOMEM => return error.SystemResources,
109 else => |err| return std.posix.unexpectedErrno(err),
110 }
111 }
112 }
113 const stat = try std.posix.fstat(mf.file.handle);
114 if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists;
115 break :stat .{ @bitCast(stat.size), @max(std.heap.pageSize(), stat.blksize) };
53 const stat = try file.stat(io);
54 if (stat.kind != .file) return error.PathAlreadyExists;
55 break :stat .{ stat.size, @max(std.heap.pageSize(), stat.block_size) };
11656 };
11757 mf.flags = .{
11858 .block_size = .fromByteUnits(std.math.ceilPowerOfTwoAssert(usize, block_size)),
......@@ -348,12 +288,12 @@ pub const Node = extern struct {
348288
349289 pub fn slice(ni: Node.Index, mf: *const MappedFile) []u8 {
350290 const file_loc = ni.fileLocation(mf, true);
351 return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
291 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
352292 }
353293
354294 pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 {
355295 const file_loc = ni.fileLocation(mf, false);
356 return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
296 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
357297 }
358298
359299 pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) !void {
......@@ -661,7 +601,8 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
661601 // Resize the entire file
662602 if (ni == Node.Index.root) {
663603 try mf.ensureCapacityForSetLocation(gpa);
664 try mf.file.setLength(io, new_size);
604 try mf.memory_map.write(io);
605 try mf.memory_map.file.setLength(io, new_size);
665606 try mf.ensureTotalCapacity(@intCast(new_size));
666607 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
667608 return;
......@@ -685,6 +626,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
685626 if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
686627 node.flags.alignment.order(mf.flags.block_size).compare(.gte))
687628 insert_range: {
629 try mf.memory_map.write(io);
688630 // Ask the filesystem driver to insert extents into the file without copying any data
689631 const last_offset, const last_size = parent.last.location(mf).resolve(mf);
690632 const last_end = last_offset + last_size;
......@@ -696,12 +638,12 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
696638 _, const file_size = Node.Index.root.location(mf).resolve(mf);
697639 while (true) switch (linux.errno(switch (std.math.order(range_file_offset, file_size)) {
698640 .lt => linux.fallocate(
699 mf.file.handle,
641 mf.memory_map.file.handle,
700642 linux.FALLOC.FL_INSERT_RANGE,
701643 @intCast(range_file_offset),
702644 @intCast(range_size),
703645 ),
704 .eq => linux.ftruncate(mf.file.handle, @intCast(range_file_offset + range_size)),
646 .eq => linux.ftruncate(mf.memory_map.file.handle, @intCast(range_file_offset + range_size)),
705647 .gt => unreachable,
706648 })) {
707649 .SUCCESS => {
......@@ -908,7 +850,7 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
908850 if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and
909851 size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)
910852 switch (linux.errno(linux.fallocate(
911 mf.file.handle,
853 mf.memory_map.file.handle,
912854 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
913855 @intCast(old_file_offset),
914856 @intCast(size),
......@@ -928,14 +870,14 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
928870 .TXTBSY => return error.FileBusy,
929871 else => |e| return std.posix.unexpectedErrno(e),
930872 };
931 @memset(mf.contents[@intCast(old_file_offset)..][0..@intCast(size)], 0);
873 @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);
932874}
933875
934876fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
935 const copy_size = try mf.copyFileRange(mf.file, old_file_offset, new_file_offset, size);
877 const copy_size = try mf.copyFileRange(mf.memory_map.file, old_file_offset, new_file_offset, size);
936878 if (copy_size < size) @memcpy(
937 mf.contents[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
938 mf.contents[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
879 mf.memory_map.memory[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
880 mf.memory_map.memory[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
939881 );
940882}
941883
......@@ -946,6 +888,8 @@ fn copyFileRange(
946888 new_file_offset: u64,
947889 size: u64,
948890) !u64 {
891 const io = mf.io;
892 try mf.memory_map.write(io);
949893 var remaining_size = size;
950894 if (is_linux and !mf.flags.copy_file_range_unsupported) {
951895 var old_file_offset_mut: i64 = @intCast(old_file_offset);
......@@ -954,7 +898,7 @@ fn copyFileRange(
954898 const copy_len = linux.copy_file_range(
955899 old_file.handle,
956900 &old_file_offset_mut,
957 mf.file.handle,
901 mf.memory_map.file.handle,
958902 &new_file_offset_mut,
959903 @intCast(remaining_size),
960904 0,
......@@ -990,82 +934,41 @@ fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void {
990934}
991935
992936pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void {
993 if (mf.contents.len >= new_capacity) return;
937 if (mf.memory_map.memory.len >= new_capacity) return;
994938 try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / growth_factor);
995939}
996940
997941pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {
998 if (mf.contents.len >= new_capacity) return;
942 if (mf.memory_map.memory.len >= new_capacity) return;
943 const io = mf.io;
999944 const aligned_capacity = mf.flags.block_size.forward(new_capacity);
1000 if (!is_linux) mf.unmap() else if (mf.contents.len > 0) {
1001 mf.contents = try std.posix.mremap(
1002 mf.contents.ptr,
1003 mf.contents.len,
1004 aligned_capacity,
1005 .{ .MAYMOVE = true },
1006 null,
1007 );
1008 return;
1009 }
1010 if (is_windows) {
1011 if (mf.section == windows.INVALID_HANDLE_VALUE) switch (windows.ntdll.NtCreateSection(
1012 &mf.section,
1013 .{
1014 .SPECIFIC = .{ .SECTION = .{
1015 .QUERY = true,
1016 .MAP_WRITE = true,
1017 .MAP_READ = true,
1018 .EXTEND_SIZE = true,
1019 } },
1020 .STANDARD = .{ .RIGHTS = .REQUIRED },
1021 },
1022 null,
1023 @constCast(&@as(i64, @intCast(aligned_capacity))),
1024 .{ .READWRITE = true },
1025 .{ .COMMIT = true },
1026 mf.file.handle,
1027 )) {
1028 .SUCCESS => {},
1029 else => return error.MemoryMappingNotSupported,
1030 };
1031 var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null;
1032 var contents_len = aligned_capacity;
1033 switch (windows.ntdll.NtMapViewOfSection(
1034 mf.section,
1035 windows.GetCurrentProcess(),
1036 @ptrCast(&contents_ptr),
1037 null,
1038 0,
1039 null,
1040 &contents_len,
1041 .Unmap,
1042 .{},
1043 .{ .READWRITE = true },
1044 )) {
1045 .SUCCESS => mf.contents = contents_ptr.?[0..contents_len],
1046 else => return error.MemoryMappingNotSupported,
945
946 if (mf.memory_map.memory.len > 0) {
947 if (mf.memory_map.setLength(io, aligned_capacity)) |_| {
948 return;
949 } else |err| switch (err) {
950 error.OperationUnsupported => {},
951 else => |e| return e,
1047952 }
1048 } else mf.contents = try std.posix.mmap(
1049 null,
1050 aligned_capacity,
1051 .{ .READ = true, .WRITE = true },
1052 .{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },
1053 mf.file.handle,
1054 0,
1055 );
953 unmap(mf);
954 }
955
956 const file = mf.memory_map.file;
957 mf.memory_map = try .create(io, file, .{ .len = aligned_capacity });
1056958}
1057959
1058960pub fn unmap(mf: *MappedFile) void {
1059 if (mf.contents.len == 0) return;
1060 if (is_windows)
1061 _ = windows.ntdll.NtUnmapViewOfSection(windows.GetCurrentProcess(), mf.contents.ptr)
1062 else
1063 std.posix.munmap(mf.contents);
1064 mf.contents = &.{};
1065 if (is_windows and mf.section != windows.INVALID_HANDLE_VALUE) {
1066 windows.CloseHandle(mf.section);
1067 mf.section = windows.INVALID_HANDLE_VALUE;
1068 }
961 if (mf.memory_map.memory.len == 0) return;
962 const io = mf.io;
963 const file = mf.memory_map.file;
964 mf.memory_map.destroy(io);
965 mf.memory_map.memory = &.{};
966 mf.memory_map.file = file;
967}
968
969pub fn flush(mf: *MappedFile) Io.File.WritePositionalError!void {
970 const io = mf.io;
971 try mf.memory_map.write(io);
1069972}
1070973
1071974fn verify(mf: *MappedFile) void {