mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
std.File.MemoryMap updates
- change offset to u64 - make len non-optional - make write take a file_size parameter - std.Io.Threaded: introduce disable_memory_mapping flag to force it to take the fallback path. Additionally: - introduce BlockSize to File.Stat. On Windows, based on cached call to NtQuerySystemInformation. On unsupported OS's, set to 1. - support File.NLink on Windows. this was available the whole time, we just didn't see the field at first. - remove EBADF / INVALID_HANDLE from reading/writing file error sets
This commit is contained in:
+1
-1
@@ -658,7 +658,7 @@ pub const VTable = struct {
|
||||
fileMemoryMapDestroy: *const fn (?*anyopaque, *File.MemoryMap) void,
|
||||
fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, n: usize) File.MemoryMap.SetLengthError!void,
|
||||
fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void,
|
||||
fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void,
|
||||
fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap, file_size: u64) File.WritePositionalError!void,
|
||||
|
||||
processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File,
|
||||
processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize,
|
||||
|
||||
@@ -22,6 +22,7 @@ pub const INode = std.posix.ino_t;
|
||||
pub const NLink = std.posix.nlink_t;
|
||||
pub const Uid = std.posix.uid_t;
|
||||
pub const Gid = std.posix.gid_t;
|
||||
pub const BlockSize = u32;
|
||||
|
||||
pub const Kind = enum {
|
||||
block_device,
|
||||
@@ -65,6 +66,14 @@ pub const Stat = struct {
|
||||
mtime: Io.Timestamp,
|
||||
/// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
|
||||
ctime: Io.Timestamp,
|
||||
/// Smallest chunk length in bytes appropriate for optimal I/O. This will
|
||||
/// be set to `1` for operating systems or file systems that do not
|
||||
/// recognize this concept. Not always a power of two. When creating a
|
||||
/// `MemoryMap`, the mapping length must be a multiple of this value.
|
||||
///
|
||||
/// On Windows, this is whichever is larger: PageSize or
|
||||
/// AllocationGranularity.
|
||||
block_size: BlockSize,
|
||||
};
|
||||
|
||||
pub fn stdout() File {
|
||||
|
||||
@@ -10,10 +10,11 @@ const File = Io.File;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
file: File,
|
||||
/// Byte index inside `file` where `memory` starts.
|
||||
offset: usize,
|
||||
/// Byte index inside `file` where `memory` starts. Page-aligned.
|
||||
offset: u64,
|
||||
/// Memory that may or may not remain consistent with file contents. Use `read`
|
||||
/// and `write` to ensure synchronization points.
|
||||
/// and `write` to ensure synchronization points. No minimum alignment on the
|
||||
/// pointer is guaranteed, but the length is page-aligned.
|
||||
memory: []u8,
|
||||
/// Tells whether it is memory-mapped or file operations. On Windows this also
|
||||
/// has a section handle.
|
||||
@@ -22,10 +23,10 @@ section: ?Section,
|
||||
pub const Section = if (is_windows) std.os.windows.HANDLE else void;
|
||||
|
||||
pub const CreateError = error{
|
||||
/// A file descriptor refers to a non-regular file. Or a file mapping was requested,
|
||||
/// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
|
||||
/// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
|
||||
/// Or `PROT_WRITE` is set, but the file is append-only.
|
||||
/// One of the following:
|
||||
/// * The `File.Kind` is not `file`.
|
||||
/// * The file is not open for reading and read access protections enabled.
|
||||
/// * The file is not open for writing and write access protections enabled.
|
||||
AccessDenied,
|
||||
/// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
|
||||
/// a filesystem that was mounted no-exec.
|
||||
@@ -36,6 +37,12 @@ pub const CreateError = error{
|
||||
} || Allocator.Error || File.ReadPositionalError;
|
||||
|
||||
pub const CreateOptions = struct {
|
||||
/// Size of the mapping, in bytes. If this is longer than the file size, it
|
||||
/// will be filled with zeroes.
|
||||
///
|
||||
/// Asserted to be a multiple of page size which can be obtained via
|
||||
/// `std.heap.pageSize`.
|
||||
len: usize,
|
||||
/// 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 },
|
||||
@@ -45,12 +52,9 @@ pub const CreateOptions = struct {
|
||||
undefined_contents: bool = false,
|
||||
/// Prefault the pages.
|
||||
populate: bool = true,
|
||||
/// Byte index of file to start from.
|
||||
/// Asserted to be a multiple of page size which can be obtained via
|
||||
/// `std.heap.pageSize`.
|
||||
offset: u64 = 0,
|
||||
/// `null` indicates to map the entire file. If mapping the entire file is
|
||||
/// desired and the file size is known, it is more efficient to populate
|
||||
/// the value here.
|
||||
len: ?usize = null,
|
||||
};
|
||||
|
||||
/// To release the resources associated with the returned `MemoryMap`, call
|
||||
@@ -73,8 +77,15 @@ pub const SetLengthError = error{
|
||||
/// of the file after calling this is unspecified until `write` is called.
|
||||
///
|
||||
/// May change the pointer address of `memory`.
|
||||
pub fn setLength(mm: *MemoryMap, io: Io, n: usize) File.SetLengthError!void {
|
||||
return io.vtable.fileMemoryMapSetLength(io.userdata, mm, n);
|
||||
pub fn setLength(
|
||||
mm: *MemoryMap,
|
||||
io: Io,
|
||||
/// New size of the mapping, in bytes. If this is longer than the file
|
||||
/// size, it will be filled with zeroes. Asserted to be a multiple of page
|
||||
/// size which can be obtained with `std.heap.pageSize`.
|
||||
new_length: usize,
|
||||
) File.SetLengthError!void {
|
||||
return io.vtable.fileMemoryMapSetLength(io.userdata, mm, new_length);
|
||||
}
|
||||
|
||||
/// Synchronizes the contents of `memory` from `file`.
|
||||
@@ -83,6 +94,10 @@ pub fn read(mm: *MemoryMap, io: Io) File.ReadPositionalError!void {
|
||||
}
|
||||
|
||||
/// Synchronizes the contents of `memory` to `file`.
|
||||
pub fn write(mm: *MemoryMap, io: Io) File.WritePositionalError!void {
|
||||
return io.vtable.fileMemoryMapWrite(io.userdata, mm);
|
||||
///
|
||||
/// Size of the mapping may be longer than the file size, so the `file_size`
|
||||
/// argument is used to avoid writing too many bytes. If `file_size` is not
|
||||
/// handy, use `File.length` to get it.
|
||||
pub fn write(mm: *MemoryMap, io: Io, file_size: u64) File.WritePositionalError!void {
|
||||
return io.vtable.fileMemoryMapWrite(io.userdata, mm, file_size);
|
||||
}
|
||||
|
||||
+79
-32
@@ -57,6 +57,7 @@ use_sendfile: UseSendfile = .default,
|
||||
use_copy_file_range: UseCopyFileRange = .default,
|
||||
use_fcopyfile: UseFcopyfile = .default,
|
||||
use_fchmodat2: UseFchmodat2 = .default,
|
||||
disable_memory_mapping: bool,
|
||||
|
||||
stderr_writer: File.Writer = .{
|
||||
.io = undefined,
|
||||
@@ -75,6 +76,13 @@ random_file: RandomFile = .{},
|
||||
|
||||
csprng: Csprng = .{},
|
||||
|
||||
system_basic_information: SystemBasicInformation = .{},
|
||||
|
||||
const SystemBasicInformation = if (!is_windows) struct {} else struct {
|
||||
buffer: windows.SYSTEM_BASIC_INFORMATION = undefined,
|
||||
initialized: std.atomic.Value(bool) = .{ .raw = false },
|
||||
};
|
||||
|
||||
pub const Csprng = struct {
|
||||
rng: std.Random.DefaultCsprng = .{
|
||||
.state = undefined,
|
||||
@@ -1220,6 +1228,8 @@ pub const InitOptions = struct {
|
||||
/// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH").
|
||||
/// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath`
|
||||
environ: process.Environ,
|
||||
/// If set to `true`, `File.MemoryMap` APIs will always take the fallback path.
|
||||
disable_memory_mapping: bool = false,
|
||||
};
|
||||
|
||||
/// Related:
|
||||
@@ -1247,6 +1257,7 @@ pub fn init(
|
||||
.argv0 = options.argv0,
|
||||
.environ = .{ .process_environ = options.environ },
|
||||
.worker_threads = init_single_threaded.worker_threads,
|
||||
.disable_memory_mapping = options.disable_memory_mapping,
|
||||
};
|
||||
|
||||
const cpu_count = std.Thread.getCpuCount();
|
||||
@@ -1263,6 +1274,7 @@ pub fn init(
|
||||
.argv0 = options.argv0,
|
||||
.environ = .{ .process_environ = options.environ },
|
||||
.worker_threads = .init(null),
|
||||
.disable_memory_mapping = options.disable_memory_mapping,
|
||||
};
|
||||
|
||||
if (posix.Sigaction != void) {
|
||||
@@ -1299,6 +1311,7 @@ pub const init_single_threaded: Threaded = .{
|
||||
.argv0 = .empty,
|
||||
.environ = .{},
|
||||
.worker_threads = .init(null),
|
||||
.disable_memory_mapping = false,
|
||||
};
|
||||
|
||||
var global_single_threaded_instance: Threaded = .init_single_threaded;
|
||||
@@ -2935,7 +2948,11 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
|
||||
|
||||
fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
const block_size: u32 = if (t.systemBasicInformation()) |sbi|
|
||||
@intCast(@max(sbi.PageSize, sbi.AllocationGranularity))
|
||||
else
|
||||
std.heap.page_size_max;
|
||||
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
var info: windows.FILE.ALL_INFORMATION = undefined;
|
||||
@@ -2997,10 +3014,31 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
|
||||
.atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
|
||||
.mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
|
||||
.ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
|
||||
.nlink = 0,
|
||||
.nlink = info.StandardInformation.NumberOfLinks,
|
||||
.block_size = block_size,
|
||||
};
|
||||
}
|
||||
|
||||
fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
|
||||
if (!t.system_basic_information.initialized.load(.acquire)) {
|
||||
t.mutex.lock();
|
||||
defer t.mutex.unlock();
|
||||
|
||||
switch (windows.ntdll.NtQuerySystemInformation(
|
||||
.SystemBasicInformation,
|
||||
&t.system_basic_information.buffer,
|
||||
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
else => return null,
|
||||
}
|
||||
|
||||
t.system_basic_information.initialized.store(true, .release);
|
||||
}
|
||||
return &t.system_basic_information.buffer;
|
||||
}
|
||||
|
||||
fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
|
||||
if (builtin.link_libc) return fileStatPosix(userdata, file);
|
||||
|
||||
@@ -8008,10 +8046,10 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u
|
||||
syscall.finish();
|
||||
return 0;
|
||||
},
|
||||
.NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer),
|
||||
.NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected,
|
||||
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
|
||||
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected,
|
||||
// TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
|
||||
// a handle to a directory.
|
||||
.INVALID_FUNCTION => return syscall.fail(error.IsDir),
|
||||
@@ -8158,10 +8196,10 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []
|
||||
syscall.finish();
|
||||
return 0;
|
||||
},
|
||||
.NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer),
|
||||
.NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected,
|
||||
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
|
||||
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected,
|
||||
// TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
|
||||
// a handle to a directory.
|
||||
.INVALID_FUNCTION => return syscall.fail(error.IsDir),
|
||||
@@ -8842,7 +8880,7 @@ fn writeFilePositionalWindows(
|
||||
.NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
|
||||
.NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
|
||||
.NO_DATA => return syscall.fail(error.BrokenPipe),
|
||||
.INVALID_HANDLE => return syscall.fail(error.NotOpenForWriting),
|
||||
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free
|
||||
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
|
||||
@@ -12470,6 +12508,7 @@ const linux_statx_request: std.os.linux.STATX = .{
|
||||
.INO = true,
|
||||
.SIZE = true,
|
||||
.NLINK = true,
|
||||
.BLOCKS = true,
|
||||
};
|
||||
|
||||
const linux_statx_check: std.os.linux.STATX = .{
|
||||
@@ -12481,6 +12520,7 @@ const linux_statx_check: std.os.linux.STATX = .{
|
||||
.INO = true,
|
||||
.SIZE = true,
|
||||
.NLINK = true,
|
||||
.BLOCKS = false,
|
||||
};
|
||||
|
||||
fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
|
||||
@@ -12499,6 +12539,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
|
||||
},
|
||||
.mtime = .{ .nanoseconds = @intCast(@as(i128, stx.mtime.sec) * std.time.ns_per_s + stx.mtime.nsec) },
|
||||
.ctime = .{ .nanoseconds = @intCast(@as(i128, stx.ctime.sec) * std.time.ns_per_s + stx.ctime.nsec) },
|
||||
.block_size = if (stx.mask.BLOCKS) stx.blksize else 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12547,6 +12588,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {
|
||||
.atime = timestampFromPosix(&atime),
|
||||
.mtime = timestampFromPosix(&mtime),
|
||||
.ctime = timestampFromPosix(&ctime),
|
||||
.block_size = st.blksize,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12568,6 +12610,7 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat {
|
||||
.atime = .fromNanoseconds(st.atim),
|
||||
.mtime = .fromNanoseconds(st.mtim),
|
||||
.ctime = .fromNanoseconds(st.ctim),
|
||||
.block_size = 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16127,28 +16170,29 @@ fn fileMemoryMapCreate(
|
||||
) File.MemoryMap.CreateError!File.MemoryMap {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
const offset = options.offset;
|
||||
const len = options.len;
|
||||
|
||||
const page_size = std.heap.pageSize();
|
||||
const aligned_len: usize = options.len.?; // TODO query if necessary
|
||||
assert(std.mem.isAligned(len, std.heap.page_size_min));
|
||||
|
||||
if (createFileMap(file, options.protection, offset, options.populate, aligned_len)) |result| {
|
||||
return result;
|
||||
} else |err| switch (err) {
|
||||
error.Unseekable, error.Canceled => |e| return e,
|
||||
else => {
|
||||
if (builtin.mode == .Debug)
|
||||
std.log.warn("memory mapping failed with {t}, falling back to file operations", .{err});
|
||||
},
|
||||
if (!t.disable_memory_mapping) {
|
||||
if (createFileMap(file, options.protection, offset, options.populate, len)) |result| {
|
||||
return result;
|
||||
} else |err| switch (err) {
|
||||
error.Unseekable, error.Canceled, error.AccessDenied => |e| return e,
|
||||
else => {
|
||||
if (builtin.mode == .Debug)
|
||||
std.log.warn("memory mapping failed with {t}, falling back to file operations", .{err});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const gpa = t.allocator;
|
||||
const alignment: Alignment = .fromByteUnits(page_size);
|
||||
const memory = m: {
|
||||
const ptr = gpa.rawAlloc(aligned_len, alignment, @returnAddress()) orelse
|
||||
const ptr = gpa.rawAlloc(len, .@"1", @returnAddress()) orelse
|
||||
return error.OutOfMemory;
|
||||
break :m ptr[0..aligned_len];
|
||||
break :m ptr[0..len];
|
||||
};
|
||||
errdefer gpa.rawFree(memory, alignment, @returnAddress());
|
||||
errdefer gpa.rawFree(memory, .@"1", @returnAddress());
|
||||
|
||||
if (!options.undefined_contents) try mmSyncRead(file, memory, offset);
|
||||
|
||||
@@ -16180,12 +16224,13 @@ const CreateFileMapError = error{
|
||||
OutOfMemory,
|
||||
MappingAlreadyExists,
|
||||
Unseekable,
|
||||
FileLockConflict,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
fn createFileMap(
|
||||
file: File,
|
||||
protection: std.process.MemoryProtection,
|
||||
offset: usize,
|
||||
offset: u64,
|
||||
populate: bool,
|
||||
aligned_len: usize,
|
||||
) CreateFileMapError!File.MemoryMap {
|
||||
@@ -16212,7 +16257,7 @@ fn createFileMap(
|
||||
file.handle,
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
.FILE_LOCK_CONFLICT => return error.FileLocked,
|
||||
.FILE_LOCK_CONFLICT => return error.FileLockConflict,
|
||||
.INVALID_FILE_FOR_SECTION => return error.OperationUnsupported,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
@@ -16318,9 +16363,7 @@ fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void {
|
||||
}
|
||||
} else {
|
||||
const gpa = t.allocator;
|
||||
const page_size = std.heap.pageSize();
|
||||
const alignment: Alignment = .fromByteUnits(page_size);
|
||||
gpa.rawFree(memory, alignment, @returnAddress());
|
||||
gpa.rawFree(memory, .@"1", @returnAddress());
|
||||
}
|
||||
mm.* = undefined;
|
||||
}
|
||||
@@ -16331,6 +16374,7 @@ fn fileMemoryMapSetLength(
|
||||
new_len: usize,
|
||||
) File.MemoryMap.SetLengthError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
assert(std.mem.isAligned(new_len, std.heap.page_size_min));
|
||||
if (mm.section) |section| switch (native_os) {
|
||||
.windows => {
|
||||
_ = section;
|
||||
@@ -16366,12 +16410,10 @@ fn fileMemoryMapSetLength(
|
||||
},
|
||||
} else {
|
||||
const gpa = t.allocator;
|
||||
const page_size = std.heap.pageSize();
|
||||
const alignment: Alignment = .fromByteUnits(page_size);
|
||||
if (gpa.rawRemap(mm.memory, alignment, new_len, @returnAddress())) |new_ptr| {
|
||||
if (gpa.rawRemap(mm.memory, .@"1", new_len, @returnAddress())) |new_ptr| {
|
||||
mm.memory = new_ptr[0..new_len];
|
||||
} else {
|
||||
const new_ptr = gpa.rawAlloc(new_len, alignment, @returnAddress()) orelse
|
||||
const new_ptr = gpa.rawAlloc(new_len, .@"1", @returnAddress()) orelse
|
||||
return error.OutOfMemory;
|
||||
const copy_len = @min(new_len, mm.memory.len);
|
||||
@memcpy(new_ptr[0..copy_len], mm.memory[0..copy_len]);
|
||||
@@ -16387,11 +16429,16 @@ fn fileMemoryMapRead(userdata: ?*anyopaque, mm: *File.MemoryMap) File.ReadPositi
|
||||
return mmSyncRead(mm.file, mm.memory, mm.offset);
|
||||
}
|
||||
|
||||
fn fileMemoryMapWrite(userdata: ?*anyopaque, mm: *File.MemoryMap) File.WritePositionalError!void {
|
||||
fn fileMemoryMapWrite(
|
||||
userdata: ?*anyopaque,
|
||||
mm: *File.MemoryMap,
|
||||
file_size: u64,
|
||||
) File.WritePositionalError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
if (mm.section != null) return;
|
||||
return mmSyncWrite(mm.file, mm.memory, mm.offset);
|
||||
const offset = mm.offset;
|
||||
return mmSyncWrite(mm.file, mm.memory[0..@intCast(file_size - offset)], offset);
|
||||
}
|
||||
|
||||
fn mmSyncRead(file: File, memory: []u8, offset: u64) File.ReadPositionalError!void {
|
||||
|
||||
+9
-17
@@ -605,31 +605,23 @@ test "memory mapping" {
|
||||
});
|
||||
|
||||
{
|
||||
var file = try tmp.dir.openFile(io, "blah.txt", .{});
|
||||
var file = try tmp.dir.openFile(io, "blah.txt", .{ .mode = .read_write });
|
||||
defer file.close(io);
|
||||
|
||||
var mm = try file.createMemoryMap(io, .{});
|
||||
const stat = try file.stat(io);
|
||||
const aligned_len = std.mem.alignForward(usize, @intCast(stat.size), std.heap.pageSize());
|
||||
|
||||
var mm = try file.createMemoryMap(io, .{ .len = aligned_len });
|
||||
defer mm.destroy(io);
|
||||
|
||||
try expectEqualStrings("this is my data123", mm.memory);
|
||||
mm.memory[5] = '9';
|
||||
mm.memory[8] = '9';
|
||||
try expectEqualStrings("this is my data123", std.mem.sliceTo(mm.memory, 0));
|
||||
mm.memory[4] = '9';
|
||||
mm.memory[7] = '9';
|
||||
|
||||
try mm.write(io);
|
||||
try mm.write(io, stat.size);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -212,7 +212,7 @@ pub const nlink_t = switch (native_os) {
|
||||
.wasi => c_ulonglong,
|
||||
// https://github.com/SerenityOS/serenity/blob/b98f537f117b341788023ab82e0c11ca9ae29a57/Kernel/API/POSIX/sys/types.h#L45
|
||||
.freebsd, .serenity => u64,
|
||||
.openbsd, .netbsd, .dragonfly, .illumos => u32,
|
||||
.openbsd, .netbsd, .dragonfly, .illumos, .windows => u32,
|
||||
.haiku => i32,
|
||||
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => u16,
|
||||
else => u0,
|
||||
|
||||
@@ -55,6 +55,7 @@ else switch (native_os) {
|
||||
pub const gid_t = void;
|
||||
pub const mode_t = u0;
|
||||
pub const nlink_t = u0;
|
||||
pub const blksize_t = void;
|
||||
pub const ino_t = void;
|
||||
pub const IFNAMESIZE = {};
|
||||
pub const SIG = void;
|
||||
|
||||
Reference in New Issue
Block a user