Browse Source

Add `virtual.map_file`

gingerBill 1 year ago
parent
commit
248a0bfa5f

+ 47 - 0
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)
+}

+ 4 - 0
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
+}

+ 17 - 0
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
+}

+ 18 - 0
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
+}

+ 53 - 1
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
+}