diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin new file mode 100644 index 000000000..a9e77c65e --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin new file mode 100644 index 000000000..322b99db8 --- /dev/null +++ b/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) +} diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin new file mode 100644 index 000000000..58dff6d3c --- /dev/null +++ b/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) +} \ No newline at end of file