From a70e0061579a4365da0092554470ffeadb4594a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 14 Jan 2026 15:11:37 -0800 Subject: [PATCH] std: add unit test for memory mapping --- lib/std/Io/File.zig | 4 ++++ lib/std/Io/File/MemoryMap.zig | 9 ++++++++ lib/std/Io/Threaded.zig | 3 +-- lib/std/Io/test.zig | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index e622a6dd3cd457aa8351757f5c8729b4d09416b0..312729867683768d3d16a738fd618cb5db2f85f2 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -784,6 +784,10 @@ pub fn hardLink( return io.vtable.fileHardLink(io.userdata, file, new_dir, new_sub_path, options); } +pub fn createMemoryMap(file: File, io: Io, options: MemoryMap.CreateOptions) MemoryMap.CreateError!MemoryMap { + return .create(io, file, options); +} + test { _ = Reader; _ = Writer; diff --git a/lib/std/Io/File/MemoryMap.zig b/lib/std/Io/File/MemoryMap.zig index 848a8837d12b8553c0189b4cf9e4fbd23fd644c3..1f210f4c186e7d821b5e26b4ce5bb47b520bdee3 100644 --- a/lib/std/Io/File/MemoryMap.zig +++ b/lib/std/Io/File/MemoryMap.zig @@ -36,7 +36,14 @@ pub const CreateError = error{ } || Allocator.Error || File.ReadPositionalError; pub const CreateOptions = struct { + /// When this has read set to false, bytes that are not modified before a + /// sync may have the original file contents, or may be set to zero. protection: std.process.MemoryProtection = .{ .read = true, .write = true }, + /// If set to `true`, allows bytes observed before calling `read` to be + /// undefined, and bytes unwritten before calling `write` to write + /// undefined memory to the file. + undefined_contents: bool = false, + /// Prefault the pages. populate: bool = true, /// Byte index of file to start from. offset: u64 = 0, @@ -46,6 +53,8 @@ pub const CreateOptions = struct { len: ?usize = null, }; +/// To release the resources associated with the returned `MemoryMap`, call +/// `destroy`. pub fn create(io: Io, file: File, options: CreateOptions) CreateError!MemoryMap { return io.vtable.fileMemoryMapCreate(io.userdata, file, options); } diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 09d69682cee9c03c09a7d76638481ca6b2d35ed0..e48ce886a28d572a50a9281bafeb338a5a613148 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -16150,8 +16150,7 @@ fn fileMemoryMapCreate( }; errdefer gpa.rawFree(memory, alignment, @returnAddress()); - // If the mapping does not have read permissions, no need to populate the contents. - if (options.protection.read) try mmSyncRead(file, memory, offset); + if (!options.undefined_contents) try mmSyncRead(file, memory, offset); return .{ .file = file, diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index da928c610259963916657863495ddfd5ed65434a..aff2aec048a27b657dcc0bf5ec2e38f0edbbb1eb 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -592,3 +592,44 @@ test "randomSecure" { // that two sets of 50 bytes were equal. try expect(!mem.eql(u8, &buf_a, &buf_b)); } + +test "memory mapping" { + const io = testing.io; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + try tmp.dir.writeFile(io, .{ + .sub_path = "blah.txt", + .data = "this is my data123", + }); + + { + var file = try tmp.dir.openFile(io, "blah.txt", .{}); + defer file.close(io); + + var mm = try file.createMemoryMap(io, .{}); + defer mm.destroy(io); + + try expectEqualStrings("this is my data123", mm.memory); + mm.memory[5] = '9'; + mm.memory[8] = '9'; + + try mm.write(io); + } + + var buffer: [100]u8 = undefined; + const updated_contents = try tmp.dir.readFile(io, "blah.txt", &buffer); + try expectEqualStrings("this9is9my data123", updated_contents); + + var file = try tmp.dir.openFile(io, "blah.txt", .{}); + defer file.close(io); + + var mm = try file.createMemoryMap(io, .{ + .protection = .{ .read = true }, + .offset = 2, + }); + defer mm.destroy(io); + + try expectEqualStrings("is9is9my data123", mm.memory); +} -- 2.54.0