| 1 | const MemoryMap = @This(); |
| 2 | |
| 3 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; |
| 5 | const is_windows = native_os == .windows; |
| 6 | |
| 7 | const std = @import("../../std.zig"); |
| 8 | const Io = std.Io; |
| 9 | const File = Io.File; |
| 10 | const Allocator = std.mem.Allocator; |
| 11 | |
| 12 | file: File, |
| 13 | /// Byte index inside `file` where `memory` starts. Page-aligned. |
| 14 | offset: u64, |
| 15 | /// Memory that may or may not remain consistent with file contents. Use `read` |
| 16 | /// and `write` to ensure synchronization points. Length has no alignment |
| 17 | /// requirement. |
| 18 | memory: []align(std.heap.page_size_min) u8, |
| 19 | /// Tells whether it is memory-mapped or file operations. On Windows this also |
| 20 | /// has a section handle. |
| 21 | section: ?Section, |
| 22 | |
| 23 | pub const Section = if (is_windows) std.os.windows.HANDLE else void; |
| 24 | |
| 25 | pub const CreateError = error{ |
| 26 | /// One of the following: |
| 27 | /// * The `File.Kind` is not `file`. |
| 28 | /// * The file is not open for reading and read access protections enabled. |
| 29 | /// * The file is not open for writing and write access protections enabled. |
| 30 | AccessDenied, |
| 31 | /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on |
| 32 | /// a filesystem that was mounted no-exec. |
| 33 | PermissionDenied, |
| 34 | LockedMemoryLimitExceeded, |
| 35 | ProcessFdQuotaExceeded, |
| 36 | SystemFdQuotaExceeded, |
| 37 | } || Allocator.Error || File.ReadPositionalError; |
| 38 | |
| 39 | pub const CreateOptions = struct { |
| 40 | /// Size of the mapping, in bytes. If this is longer than the file size, |
| 41 | /// `memory` beyond the file end will be filled with zeroes and it is |
| 42 | /// unspecified whether, after calling `write`, the file length will be |
| 43 | /// set to `len` or remain unchanged. |
| 44 | /// |
| 45 | /// This value has no minimum alignment requirement, but may gain |
| 46 | /// efficiency benefits from being a multiple of `File.Stat.block_size`. |
| 47 | len: usize, |
| 48 | /// When this has read set to false, bytes that are not modified before a |
| 49 | /// sync may have the original file contents, or may be set to zero. |
| 50 | protection: std.process.MemoryProtection = .{ .read = true, .write = true }, |
| 51 | /// If set to `true`, allows bytes observed before calling `read` to be |
| 52 | /// undefined, and bytes unwritten before calling `write` to write |
| 53 | /// undefined memory to the file. |
| 54 | undefined_contents: bool = false, |
| 55 | /// Prefault the pages. If this option is unsupported, it is silently |
| 56 | /// ignored. Aside from custom Io implementations, this option is only |
| 57 | /// supported on Linux. |
| 58 | populate: bool = true, |
| 59 | /// Asserted to be a multiple of page size which can be obtained via |
| 60 | /// `std.heap.pageSize`. |
| 61 | offset: u64 = 0, |
| 62 | }; |
| 63 | |
| 64 | /// To release the resources associated with the returned `MemoryMap`, call |
| 65 | /// `destroy`. |
| 66 | pub fn create(io: Io, file: File, options: CreateOptions) CreateError!MemoryMap { |
| 67 | return io.vtable.fileMemoryMapCreate(io.userdata, file, options); |
| 68 | } |
| 69 | |
| 70 | /// If `write` is not called before this function, changes to `memory` may or may |
| 71 | /// not be synchronized to `file`. |
| 72 | pub fn destroy(mm: *MemoryMap, io: Io) void { |
| 73 | io.vtable.fileMemoryMapDestroy(io.userdata, mm); |
| 74 | } |
| 75 | |
| 76 | pub 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, |
| 80 | /// One of the following: |
| 81 | /// * The `File.Kind` is not `file`. |
| 82 | /// * The file is not open for reading and read access protections enabled. |
| 83 | /// * The file is not open for writing and write access protections enabled. |
| 84 | AccessDenied, |
| 85 | /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on |
| 86 | /// a filesystem that was mounted no-exec. |
| 87 | PermissionDenied, |
| 88 | LockedMemoryLimitExceeded, |
| 89 | ProcessFdQuotaExceeded, |
| 90 | SystemFdQuotaExceeded, |
| 91 | } || Allocator.Error || File.SetLengthError; |
| 92 | |
| 93 | /// Change the size of the mapping. This does not sync the contents. The size |
| 94 | /// of the file after calling this is unspecified until `write` is called. |
| 95 | /// |
| 96 | /// May change the pointer address of `memory`. |
| 97 | pub fn setLength(mm: *MemoryMap, io: Io, new_len: usize) SetLengthError!void { |
| 98 | return io.vtable.fileMemoryMapSetLength(io.userdata, mm, new_len); |
| 99 | } |
| 100 | |
| 101 | /// Synchronizes the contents of `memory` from `file`. |
| 102 | pub fn read(mm: *MemoryMap, io: Io) File.ReadPositionalError!void { |
| 103 | return io.vtable.fileMemoryMapRead(io.userdata, mm); |
| 104 | } |
| 105 | |
| 106 | /// Synchronizes the contents of `memory` to `file`. |
| 107 | /// |
| 108 | /// If `memory.len` is greater than file size, the bytes beyond the end of the |
| 109 | /// file may be dropped, or they may be written, extending the size of the |
| 110 | /// file. |
| 111 | pub fn write(mm: *MemoryMap, io: Io) File.WritePositionalError!void { |
| 112 | return io.vtable.fileMemoryMapWrite(io.userdata, mm); |
| 113 | } |