mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
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:
+1
-1
@@ -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,
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user