From 248a0bfa5f21ed09311887e3a8d9c79b6ec94fe4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 17 Jan 2024 22:41:22 +0000 Subject: [PATCH] Add `virtual.map_file` --- core/mem/virtual/file.odin | 47 ++++++++++++++++++++++ core/mem/virtual/virtual_bsd.odin | 4 ++ core/mem/virtual/virtual_darwin.odin | 17 ++++++++ core/mem/virtual/virtual_linux.odin | 18 +++++++++ core/mem/virtual/virtual_windows.odin | 56 ++++++++++++++++++++++++++- 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 core/mem/virtual/file.odin diff --git a/core/mem/virtual/file.odin b/core/mem/virtual/file.odin new file mode 100644 index 000000000..c76b02626 --- /dev/null +++ b/core/mem/virtual/file.odin @@ -0,0 +1,47 @@ +package mem_virtual + +import "core:os" + +Mapped_File_Error :: enum { + None, + Open_Failure, + Stat_Failure, + Negative_Size, + Too_Large_Size, + Map_Failure, +} + +Mapped_File_Flag :: enum u32 { + Read, + Write, +} +Mapped_File_Flags :: distinct bit_set[Mapped_File_Flag; u32] + +map_file :: proc{ + map_file_from_path, + map_file_from_file_descriptor, +} + +map_file_from_path :: proc(filename: string, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + fd, err := os.open(filename, os.O_RDWR) + if err != 0 { + return nil, .Open_Failure + } + defer os.close(fd) + + return map_file_from_file_descriptor(uintptr(fd), flags) +} + +map_file_from_file_descriptor :: proc(fd: uintptr, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + size, os_err := os.file_size(os.Handle(fd)) + if os_err != 0 { + return nil, .Stat_Failure + } + if size < 0 { + return nil, .Negative_Size + } + if size != i64(int(size)) { + return nil, .Too_Large_Size + } + return _map_file(fd, size, flags) +} diff --git a/core/mem/virtual/virtual_bsd.odin b/core/mem/virtual/virtual_bsd.odin index 103e48074..b10c46c64 100644 --- a/core/mem/virtual/virtual_bsd.odin +++ b/core/mem/virtual/virtual_bsd.odin @@ -22,3 +22,7 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) _platform_memory_init :: proc() { } + +_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + return nil, .Map_Failure +} diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin index bd1336cba..831b73d73 100644 --- a/core/mem/virtual/virtual_darwin.odin +++ b/core/mem/virtual/virtual_darwin.odin @@ -146,3 +146,20 @@ _platform_memory_init :: proc() { // is power of two assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } + + +_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + prot, mflags: c.int + if .Read in flags { + prot |= PROT_READ + } + if .Write in flags { + prot |= PROT_WRITE + } + mflags |= MAP_SHARED + addr := _mmap(nil, c.size_t(size), prot, mflags, i32(fd), 0) + if addr == nil { + return nil, .Map_Failure + } + return ([^]byte)(addr)[:size], nil +} diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index b927f2877..cbe3e544f 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -48,3 +48,21 @@ _platform_memory_init :: proc() { // is power of two assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } + + +_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + prot: linux.Mem_Protection + if .Read in flags { + prot += {.READ} + } + if .Write in flags { + prot += {.WRITE} + } + + flags := linux.Map_Flags{.SHARED} + addr, errno := linux.mmap(0, uint(size), prot, flags, linux.Fd(fd), offset=0) + if addr == nil || error != nil { + return nil, .Map_Failure + } + return ([^]byte)(addr)[:size], nil +} diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 5e97296af..7c25951c4 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -2,6 +2,8 @@ //+private package mem_virtual +import "core:os" + foreign import Kernel32 "system:Kernel32.lib" LPSYSTEM_INFO :: ^SYSTEM_INFO @@ -50,6 +52,11 @@ PAGE_WRITECOPY :: 0x08 PAGE_TARGETS_INVALID :: 0x40000000 PAGE_TARGETS_NO_UPDATE :: 0x40000000 +SECTION_MAP_WRITE :: 0x0002 +SECTION_MAP_READ :: 0x0004 +FILE_MAP_WRITE :: SECTION_MAP_WRITE +FILE_MAP_READ :: SECTION_MAP_READ + ERROR_INVALID_ADDRESS :: 487 ERROR_COMMITMENT_LIMIT :: 1455 @@ -60,9 +67,24 @@ foreign Kernel32 { VirtualFree :: proc(lpAddress: rawptr, dwSize: uint, dwFreeType: u32) -> b32 --- VirtualProtect :: proc(lpAddress: rawptr, dwSize: uint, flNewProtect: u32, lpflOldProtect: ^u32) -> b32 --- GetLastError :: proc() -> u32 --- + + CreateFileMappingW :: proc( + hFile: rawptr, + lpFileMappingAttributes: rawptr, + flProtect: u32, + dwMaximumSizeHigh: u32, + dwMaximumSizeLow: u32, + lpName: [^]u16, + ) -> rawptr --- + + MapViewOfFile :: proc( + hFileMappingObject: rawptr, + dwDesiredAccess: u32, + dwFileOffsetHigh: u32, + dwFileOffsetLow: u32, + dwNumberOfBytesToMap: uint, + ) -> rawptr --- } - - _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE) if result == nil { @@ -125,3 +147,33 @@ _platform_memory_init :: proc() { // is power of two assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } + + +_map_file :: proc "contextless" (fd: os.Handle, size: i64, flags: Mapped_File_Flags) -> (data: []byte, error: Mapped_File_Error) { + page_flags: u32 + if flags == {.Read} { + page_flags = PAGE_READONLY + } else if flags == {.Write} { + page_flags = PAGE_READWRITE + } else if flags == {.Read, .Write} { + page_flags = PAGE_READWRITE + } else { + page_flags = PAGE_NOACCESS + } + maximum_size := transmute([2]u32)size + handle := CreateFileMappingW(rawptr(fd), nil, page_flags, maximum_size[1], maximum_size[0], nil) + if handle == nil { + return nil, .Map_Failure + } + + desired_access: u32 + if .Read in flags { + desired_access |= FILE_MAP_READ + } + if .Write in flags { + desired_access |= FILE_MAP_WRITE + } + + file_data := MapViewOfFile(handle, desired_access, 0, 0, uint(size)) + return ([^]byte)(file_data)[:size], nil +}