mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 07:08:48 +00:00
Add core:mem/virtual
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user