Browse Source

Add `core:mem/virtual`

gingerBill 3 years ago
parent
commit
805e48ae1e
3 changed files with 289 additions and 0 deletions
  1. 97 0
      core/mem/virtual/arena.odin
  2. 105 0
      core/mem/virtual/virtual.odin
  3. 87 0
      core/mem/virtual/virtual_windows.odin

+ 97 - 0
core/mem/virtual/arena.odin

@@ -0,0 +1,97 @@
+package mem_virtual
+
+import "core:mem"
+
+Arena :: struct {
+	curr_block:      ^Memory_Block,
+	total_used:      int,
+	total_allocated: int,
+	
+	minimum_block_size: int,
+}
+
+DEFAULT_MINIMUM_BLOCK_SIZE :: 8*1024*1024
+DEFAULT_PAGE_SIZE := 4096
+
+arena_alloc :: proc(arena: ^Arena, min_size: int, alignment: int) -> (data: []byte, err: mem.Allocator_Error) {
+	align_forward_offset :: proc(arena: ^Arena, alignment: int) -> int #no_bounds_check {
+		alignment_offset := 0
+		ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
+		mask := uintptr(alignment-1)
+		if ptr & mask != 0 {
+			alignment_offset = alignment - int(ptr & mask)
+		}
+		return alignment_offset
+	}
+	
+	assert(mem.is_power_of_two(uintptr(alignment)))
+	
+	size := 0
+	if arena.curr_block != nil {
+		size = min_size + align_forward_offset(arena, alignment)
+	}
+	
+	if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.size {
+		size = mem.align_forward_int(min_size, alignment)
+		arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
+		
+		block_size := max(size, arena.minimum_block_size)
+		
+		new_block := memory_alloc(block_size) or_return
+		new_block.prev = arena.curr_block
+		arena.curr_block = new_block
+		arena.total_allocated += new_block.size
+	}
+	
+	curr_block := arena.curr_block
+	assert(curr_block.used + size <= curr_block.size)
+	
+	ptr := curr_block.base[curr_block.used:]
+	ptr = ptr[uintptr(align_forward_offset(arena, alignment)):]
+	
+	curr_block.used += size
+	assert(curr_block.used <= curr_block.size)
+	arena.total_used += size
+	
+	return ptr[:min_size], nil
+}
+
+arena_free_all :: proc(arena: ^Arena) {
+	for arena.curr_block != nil {
+		free_block := arena.curr_block
+		arena.curr_block = free_block.prev
+		memory_dealloc(free_block)
+	}
+	arena.total_used = 0
+}
+
+arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
+	return mem.Allocator{arena_allocator_proc, arena}
+}
+
+arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
+                             size, alignment: int,
+                             old_memory: rawptr, old_size: int,
+                             location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
+	arena := (^Arena)(allocator_data)
+	
+	switch mode {
+	case .Alloc:
+		return arena_alloc(arena, size, alignment)
+	case .Free:
+		err = .Mode_Not_Implemented
+		return
+	case .Free_All:
+		arena_free_all(arena)
+		return
+	case .Resize:
+		return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena))
+		
+	case .Query_Features, .Query_Info:
+		err = .Mode_Not_Implemented
+		return	
+	}	
+	
+	err = .Mode_Not_Implemented
+	return 
+}

+ 105 - 0
core/mem/virtual/virtual.odin

@@ -0,0 +1,105 @@
+package mem_virtual
+
+import "core:mem"
+import sync "core:sync/sync2"
+
+Memory_Block :: struct {
+	prev: ^Memory_Block,
+	base: [^]byte,
+	size: int,
+	used: int,
+}
+
+
+memory_alloc :: proc(size: int) -> (block: ^Memory_Block, err: mem.Allocator_Error) {
+	page_size := DEFAULT_PAGE_SIZE
+	
+	total_size     := size + size_of(Platform_Memory_Block)
+	base_offset    := uintptr(size_of(Platform_Memory_Block))
+	protect_offset := uintptr(0)
+	
+	do_protection := false
+	{ // overflow protection
+		rounded_size := mem.align_formula(size, page_size)
+		total_size     = rounded_size + 2*page_size
+		base_offset    = uintptr(page_size + rounded_size - size)
+		protect_offset = uintptr(page_size + rounded_size)
+		do_protection  = true
+	}
+	
+	pmblock := platform_memory_alloc(total_size) or_return
+	
+	pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
+	// Should be zeroed
+	assert(pmblock.block.used == 0)
+	assert(pmblock.block.prev == nil)
+	
+	if (do_protection) {
+		platform_memory_protect(rawptr(uintptr(pmblock) + protect_offset), page_size)
+	}
+	
+	pmblock.block.size = size
+	pmblock.total_size = total_size
+
+	sentinel := &global_platform_memory_block_sentinel
+	sync.mutex_lock(&global_memory_block_mutex)
+	pmblock.next = sentinel
+	pmblock.prev = sentinel.prev
+	pmblock.prev.next = pmblock
+	pmblock.next.prev = pmblock
+	sync.mutex_unlock(&global_memory_block_mutex)
+	
+	return &pmblock.block, nil
+}
+
+
+memory_dealloc :: proc(block_to_free: ^Memory_Block) {
+	block := (^Platform_Memory_Block)(block_to_free)
+	if block != nil {
+		sync.mutex_lock(&global_memory_block_mutex)
+		block.prev.next = block.next
+		block.next.prev = block.prev
+		sync.mutex_unlock(&global_memory_block_mutex)
+		
+		platform_memory_free(block)
+	}
+}
+
+Platform_Memory_Block :: struct {
+	block:      Memory_Block,
+	total_size: int,
+	prev, next: ^Platform_Memory_Block,
+} 
+
+@(private)
+global_memory_block_mutex: sync.Mutex
+@(private)
+global_platform_memory_block_sentinel: Platform_Memory_Block
+@(private)
+global_platform_memory_block_sentinel_set: bool
+
+@(private)
+platform_memory_init :: proc() {
+	if !global_platform_memory_block_sentinel_set {
+		_platform_memory_init()
+		global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
+		global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
+		global_platform_memory_block_sentinel_set = true
+	}
+}
+
+platform_memory_alloc :: proc(block_size: int) -> (^Platform_Memory_Block, mem.Allocator_Error) {
+	platform_memory_init()
+	return _platform_memory_alloc(block_size)
+}
+
+
+platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+	platform_memory_init()
+	_platform_memory_free(block)
+}
+
+platform_memory_protect :: proc(memory: rawptr, size: int) {
+	platform_memory_init()
+	_platform_memory_protect(memory, size)
+}

+ 87 - 0
core/mem/virtual/virtual_windows.odin

@@ -0,0 +1,87 @@
+//+build windows
+//+private
+package mem_virtual
+
+import "core:mem"
+
+foreign import Kernel32 "system:Kernel32.lib"
+
+LPSYSTEM_INFO :: ^SYSTEM_INFO
+SYSTEM_INFO :: struct {
+	using DUMMYUNIONNAME: struct #raw_union {
+		dwOemId: u32,
+		using DUMMYSTRUCTNAME:struct {
+			wProcessorArchitecture: u16,
+			wReserved: u16,
+		},
+	},
+	dwPageSize:                  u32,
+	lpMinimumApplicationAddress: rawptr,
+	lpMaximumApplicationAddress: rawptr,
+	dwActiveProcessorMask:       uint,
+	dwNumberOfProcessors:        u32,
+	dwProcessorType:             u32,
+	dwAllocationGranularity:     u32,
+	wProcessorLevel:             u16,
+	wProcessorRevision:          u16,
+}
+
+MEM_COMMIT      :: 0x00001000
+MEM_RESERVE     :: 0x00002000
+MEM_RESET       :: 0x00080000
+MEM_RESET_UNDO  :: 0x01000000
+MEM_LARGE_PAGES :: 0x20000000
+MEM_PHYSICAL    :: 0x00400000
+MEM_TOP_DOWN    :: 0x00100000
+MEM_WRITE_WATCH :: 0x00200000
+
+MEM_DECOMMIT :: 0x00004000
+MEM_RELEASE  :: 0x00008000
+
+MEM_COALESCE_PLACEHOLDERS :: 0x00000001
+MEM_PRESERVE_PLACEHOLDER  :: 0x00000002
+
+PAGE_EXECUTE           :: 0x10
+PAGE_EXECUTE_READ      :: 0x20
+PAGE_EXECUTE_READWRITE :: 0x40
+PAGE_EXECUTE_WRITECOPY :: 0x80
+PAGE_NOACCESS          :: 0x01
+PAGE_READONLY          :: 0x02
+PAGE_READWRITE         :: 0x04
+PAGE_WRITECOPY         :: 0x08
+PAGE_TARGETS_INVALID   :: 0x40000000
+PAGE_TARGETS_NO_UPDATE :: 0x40000000
+
+foreign Kernel32 {
+	GetSystemInfo  :: proc(lpSystemInfo: LPSYSTEM_INFO) ---
+	VirtualAlloc   :: proc(lpAddress: rawptr, dwSize: uint, flAllocationType: u32, flProtect: u32) -> rawptr ---
+	VirtualFree    :: proc(lpAddress: rawptr, dwSize: uint, dwFreeType: u32) -> b32 ---
+	VirtualProtect :: proc(lpAddress: rawptr, dwSize: uint, flNewProtect: u32, lpflOldProtect: ^u32) -> b32 ---
+}
+
+
+_platform_memory_init :: proc() {
+	sys_info: SYSTEM_INFO
+	GetSystemInfo(&sys_info)
+	DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, int(sys_info.dwPageSize))
+	assert(mem.is_power_of_two(uintptr(DEFAULT_PAGE_SIZE)))
+}
+
+_platform_memory_alloc :: proc(total_size: int) -> (pmblock: ^Platform_Memory_Block, err: mem.Allocator_Error) {
+	pmblock = (^Platform_Memory_Block)(VirtualAlloc(nil, uint(total_size), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE))
+	if pmblock == nil {
+		err = .Out_Of_Memory
+	}
+	return 
+}
+
+
+_platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+	VirtualFree(block, 0, MEM_RELEASE)
+}
+
+_platform_memory_protect :: proc(memory: rawptr, size: int) -> bool {
+	old_protect: u32
+	ok := VirtualProtect(memory, uint(size), PAGE_NOACCESS, &old_protect)
+	return bool(ok)
+}