Files
Odin/core/mem/virtual/virtual.odin
T
2021-10-04 17:42:15 +01:00

152 lines
3.7 KiB
Odin

package mem_virtual
import "core:mem"
import sync "core:sync/sync2"
DEFAULT_PAGE_SIZE := uint(4096)
Allocator_Error :: mem.Allocator_Error
reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
commit :: proc(data: rawptr, size: uint) {
_commit(data, size)
}
reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size)
return
}
decommit :: proc(data: rawptr, size: uint) {
_decommit(data, size)
}
release :: proc(data: rawptr, size: uint) {
_release(data, size)
}
Protect_Flag :: enum u32 {
Read,
Write,
Execute,
}
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
Protect_No_Access :: Protect_Flags{}
protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
return _protect(data, size, flags)
}
Memory_Block :: struct {
prev: ^Memory_Block,
base: [^]byte,
size: int,
used: int,
}
memory_alloc :: proc(size: int) -> (block: ^Memory_Block, err: Allocator_Error) {
align_formula :: proc "contextless" (size, align: uint) -> uint {
result := size + align-1
return result - result%align
}
page_size := DEFAULT_PAGE_SIZE
total_size := uint(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 := align_formula(uint(size), page_size)
total_size = uint(rounded_size + 2*page_size)
base_offset = uintptr(page_size + rounded_size - uint(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) {
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
}
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: uint,
prev, next: ^Platform_Memory_Block,
}
platform_memory_alloc :: proc(total_size: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
total_size := total_size
total_size = max(total_size, size_of(Platform_Memory_Block))
data := reserve_and_commit(total_size) or_return
block = (^Platform_Memory_Block)(raw_data(data))
block.total_size = total_size
return
}
platform_memory_free :: proc(block: ^Platform_Memory_Block) {
if block != nil {
release(block, block.total_size)
}
}
@(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, init)
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
}
}