Browse Source

Merge pull request #2124 from odin-lang/virtual-arena-unification

`core:mem/virtual` - Unify `Static_Arena` and `Growing_Arena` into `Arena`
gingerBill 2 years ago
parent
commit
aa799d6a0d

+ 8 - 7
core/mem/alloc.odin

@@ -243,13 +243,11 @@ default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, a
 		return alloc_bytes(new_size, alignment, allocator, loc)
 	}
 
-	if new_size == 0 {
-		err := free_bytes(old_data, allocator, loc)
-		return nil, err
-	}
-
 	if new_size == old_size {
-		return old_data, .None
+		return old_data, nil
+	}
+	if new_size == 0 {
+		return nil, free_bytes(old_data, allocator, loc)
 	}
 
 	new_memory, err := alloc_bytes(new_size, alignment, allocator, loc)
@@ -258,6 +256,9 @@ default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, a
 	}
 
 	runtime.copy(new_memory, old_data)
-	free_bytes(old_data, allocator, loc)
+	err1 := free_bytes(old_data, allocator, loc)
+	if err == nil {
+		err = err1
+	}
 	return new_memory, err
 }

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

@@ -0,0 +1,300 @@
+package mem_virtual
+
+import "core:mem"
+
+Arena_Kind :: enum uint {
+	Growing = 0, // chained memory blocks (singly linked list)
+	Static  = 1, // fixed reservation sized
+}
+
+Arena :: struct {
+	kind:               Arena_Kind,
+	curr_block:         ^Memory_Block,
+	total_used:         uint,
+	total_reserved:     uint,
+	minimum_block_size: uint,
+	temp_count:         uint,
+}
+
+
+// 1 MiB should be enough to start with
+DEFAULT_ARENA_STATIC_COMMIT_SIZE         :: 1<<20
+DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
+
+// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
+DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
+
+
+
+@(require_results)
+arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
+	arena.kind = .Growing
+	arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
+	arena.total_used = 0
+	arena.total_reserved = arena.curr_block.reserved
+	return
+}
+
+
+@(require_results)
+arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
+	arena.kind = .Static
+	arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
+	arena.total_used = 0
+	arena.total_reserved = arena.curr_block.reserved
+	return
+}
+
+@(require_results)
+arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+	assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
+
+	size := size
+	if size == 0 {
+		return nil, nil
+	}
+
+	switch arena.kind {
+	case .Growing:
+		if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
+			size = mem.align_forward_uint(size, alignment)
+			if arena.minimum_block_size == 0 {
+				arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
+			}
+
+			block_size := max(size, arena.minimum_block_size)
+
+			new_block := memory_block_alloc(size, block_size, {}) or_return
+			new_block.prev = arena.curr_block
+			arena.curr_block = new_block
+			arena.total_reserved += new_block.reserved
+		}
+
+		prev_used := arena.curr_block.used
+		data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
+		arena.total_used += arena.curr_block.used - prev_used
+	case .Static:
+		if arena.curr_block == nil {
+			if arena.minimum_block_size == 0 {
+				arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
+			}
+			arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
+		}
+		data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
+		arena.total_used = arena.curr_block.used
+	}
+	return
+}
+
+arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
+	if arena.curr_block != nil {
+		assert(arena.kind == .Static, "expected a .Static arena", loc)
+
+		prev_pos := arena.curr_block.used
+		arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
+
+		if prev_pos < pos {
+			mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
+		}
+		arena.total_used = arena.curr_block.used
+		return true
+	} else if pos == 0 {
+		arena.total_used = 0
+		return true
+	}
+	return false
+}
+
+arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
+	if free_block := arena.curr_block; free_block != nil {
+		assert(arena.kind == .Growing, "expected a .Growing arena", loc)
+		arena.curr_block = free_block.prev
+		memory_block_dealloc(free_block)
+	}
+}
+
+arena_free_all :: proc(arena: ^Arena) {
+	switch arena.kind {
+	case .Growing:
+		for arena.curr_block != nil {
+			arena_growing_free_last_memory_block(arena)
+		}
+		arena.total_reserved = 0
+	case .Static:
+		arena_static_reset_to(arena, 0)
+	}
+	arena.total_used = 0
+}
+
+arena_destroy :: proc(arena: ^Arena) {
+	arena_free_all(arena)
+	memory_block_dealloc(arena.curr_block)
+	arena.curr_block = nil
+	arena.total_used     = 0
+	arena.total_reserved = 0
+	arena.temp_count     = 0
+}
+
+arena_growing_bootstrap_new :: proc{
+	arena_growing_bootstrap_new_by_offset,
+	arena_growing_bootstrap_new_by_name,
+}
+
+arena_static_bootstrap_new :: proc{
+	arena_static_bootstrap_new_by_offset,
+	arena_static_bootstrap_new_by_name,
+}
+
+@(require_results)
+arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
+	bootstrap: Arena
+	bootstrap.kind = .Growing
+	bootstrap.minimum_block_size = minimum_block_size
+
+	data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
+
+	ptr = (^T)(raw_data(data))
+
+	(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
+
+	return
+}
+
+@(require_results)
+arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
+	return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
+}
+
+@(require_results)
+arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
+	bootstrap: Arena
+	bootstrap.kind = .Static
+	bootstrap.minimum_block_size = reserved
+
+	data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
+
+	ptr = (^T)(raw_data(data))
+
+	(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
+
+	return
+}
+
+@(require_results)
+arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
+	return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
+}
+
+
+@(require_results)
+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: Allocator_Error) {
+	arena := (^Arena)(allocator_data)
+
+	size, alignment := uint(size), uint(alignment)
+	old_size := uint(old_size)
+
+	switch mode {
+	case .Alloc:
+		return arena_alloc(arena, size, alignment)
+	case .Free:
+		err = .Mode_Not_Implemented
+	case .Free_All:
+		arena_free_all(arena)
+	case .Resize:
+		old_data := ([^]byte)(old_memory)
+
+		switch {
+		case old_data == nil:
+			return arena_alloc(arena, size, alignment)
+		case size == old_size:
+			// return old memory
+			data = old_data[:size]
+			return
+		case size == 0:
+			err = .Mode_Not_Implemented
+			return
+		case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
+			// shrink data in-place
+			data = old_data[:size]
+			return
+		}
+
+		new_memory := arena_alloc(arena, size, alignment) or_return
+		if new_memory == nil {
+			return
+		}
+		copy(new_memory, old_data[:old_size])
+		return new_memory, nil
+	case .Query_Features:
+		set := (^mem.Allocator_Mode_Set)(old_memory)
+		if set != nil {
+			set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
+		}
+	case .Query_Info:
+		err = .Mode_Not_Implemented
+	}
+
+	return
+}
+
+
+
+
+Arena_Temp :: struct {
+	arena: ^Arena,
+	block: ^Memory_Block,
+	used:  uint,
+}
+
+@(require_results)
+arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
+	assert(arena != nil, "nil arena", loc)
+	temp.arena = arena
+	temp.block = arena.curr_block
+	if arena.curr_block != nil {
+		temp.used = arena.curr_block.used
+	}
+	arena.temp_count += 1
+	return
+}
+
+arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
+	assert(temp.arena != nil, "nil arena", loc)
+	arena := temp.arena
+
+	memory_block_found := false
+	for block := arena.curr_block; block != nil; block = block.prev {
+		if block == temp.block {
+			memory_block_found = true
+			break
+		}
+	}
+	if !memory_block_found {
+		assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
+	}
+
+	for arena.curr_block != temp.block {
+		arena_growing_free_last_memory_block(arena)
+	}
+
+	if block := arena.curr_block; block != nil {
+		assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
+		amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
+		mem.zero_slice(block.base[temp.used:][:amount_to_zero])
+		block.used = temp.used
+	}
+
+	assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
+	arena.temp_count -= 1
+}
+
+arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
+	assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
+}

+ 0 - 41
core/mem/virtual/arena_util.odin

@@ -1,41 +0,0 @@
-package mem_virtual
-
-arena_init :: proc{
-	static_arena_init,
-	growing_arena_init,
-}
-
-arena_temp_begin :: proc{
-	static_arena_temp_begin,
-	growing_arena_temp_begin,
-}
-
-arena_temp_end :: proc{
-	static_arena_temp_end,
-	growing_arena_temp_end,
-}
-
-arena_check_temp :: proc{
-	static_arena_check_temp,
-	growing_arena_check_temp,
-}
-
-arena_allocator :: proc{
-	static_arena_allocator,
-	growing_arena_allocator,
-}
-
-arena_alloc :: proc{
-	static_arena_alloc,
-	growing_arena_alloc,
-}
-
-arena_free_all :: proc{
-	static_arena_free_all,
-	growing_arena_free_all,
-}
-
-arena_destroy :: proc{
-	static_arena_destroy,
-	growing_arena_destroy,
-}

+ 0 - 170
core/mem/virtual/growing_arena.odin

@@ -1,170 +0,0 @@
-package mem_virtual
-
-import "core:mem"
-
-Growing_Arena :: struct {
-	curr_block:     ^Memory_Block,
-	total_used:     uint,
-	total_reserved: uint,
-	
-	minimum_block_size: uint,
-	temp_count: int,
-}
-
-DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
-
-growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
-	arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
-	arena.total_used = 0
-	arena.total_reserved = arena.curr_block.reserved
-	return
-}
-
-growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
-	align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
-		alignment_offset := uint(0)
-		ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
-		mask := uintptr(alignment-1)
-		if ptr & mask != 0 {
-			alignment_offset = uint(alignment) - uint(ptr & mask)
-		}
-		return alignment_offset
-	}
-	
-	assert(mem.is_power_of_two(uintptr(alignment)))
-
-	size := uint(0)
-	if arena.curr_block != nil {
-		size = uint(min_size) + align_forward_offset(arena, alignment)
-	}
-	
-	if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
-		size = uint(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_block_alloc(size, block_size, {}) or_return
-		new_block.prev = arena.curr_block
-		arena.curr_block = new_block
-		arena.total_reserved += new_block.reserved	
-	}	
-	
-	
-	data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
-	if err == nil {
-		arena.total_used += size
-	}
-	return
-}
-
-growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
-	free_block := arena.curr_block
-	arena.curr_block = free_block.prev
-	memory_block_dealloc(free_block)
-}
-
-growing_arena_free_all :: proc(arena: ^Growing_Arena) {
-	for arena.curr_block != nil {
-		growing_arena_free_last_memory_block(arena)
-	}
-	arena.total_used = 0
-	arena.total_reserved = 0
-}
-
-growing_arena_destroy :: proc(arena: ^Growing_Arena) {
-	growing_arena_free_all(arena)
-}
-
-growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {	bootstrap: Growing_Arena
-	bootstrap.minimum_block_size = minimum_block_size
-	
-	data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
-	
-	ptr = (^T)(raw_data(data))
-	
-	(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
-	
-	return
-}
-
-growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
-	return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
-}
-growing_arena_bootstrap_new :: proc{
-	growing_arena_bootstrap_new_by_offset, 
-	growing_arena_bootstrap_new_by_name,
-}
-
-growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
-	return mem.Allocator{growing_arena_allocator_proc, arena}
-}
-
-growing_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: Allocator_Error) {
-	arena := (^Growing_Arena)(allocator_data)
-		
-	switch mode {
-	case .Alloc:
-		return growing_arena_alloc(arena, size, alignment)
-	case .Free:
-		err = .Mode_Not_Implemented
-		return
-	case .Free_All:
-		growing_arena_free_all(arena)
-		return
-	case .Resize:
-		return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
-		
-	case .Query_Features, .Query_Info:
-		err = .Mode_Not_Implemented
-		return	
-	}	
-	
-	err = .Mode_Not_Implemented
-	return 
-}
-
-Growing_Arena_Temp :: struct {
-	arena: ^Growing_Arena,
-	block: ^Memory_Block,
-	used:  uint,
-}
-
-growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
-	temp.arena = arena
-	temp.block = arena.curr_block
-	if arena.curr_block != nil {
-		temp.used = arena.curr_block.used
-	}
-	arena.temp_count += 1
-	return
-}
-
-growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
-	assert(temp.arena != nil, "nil arena", loc)
-	arena := temp.arena
-	
-	for arena.curr_block != temp.block {
-		growing_arena_free_last_memory_block(arena)
-	}
-	
-	if block := arena.curr_block; block != nil {
-		assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
-		amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
-		mem.zero_slice(block.base[temp.used:][:amount_to_zero])
-		block.used = temp.used
-	}
-	
-	assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
-	arena.temp_count -= 1
-}
-
-growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
-	assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
-}
-
-
-

+ 0 - 153
core/mem/virtual/static_arena.odin

@@ -1,153 +0,0 @@
-package mem_virtual
-
-import "core:mem"
-
-Static_Arena :: struct {
-	block: ^Memory_Block,
-	total_used:     uint,
-	total_reserved: uint,
-	
-	minimum_block_size: uint,
-	temp_count: int,
-}
-
-STATIC_ARENA_DEFAULT_COMMIT_SIZE  :: 1<<20 // 1 MiB should be enough to start with
-
-// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
-STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
-
-static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {	
-	arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
-	arena.total_used = 0
-	arena.total_reserved = arena.block.reserved
-	return
-} 
-
-static_arena_destroy :: proc(arena: ^Static_Arena) {
-	memory_block_dealloc(arena.block)
-	arena^ = {}
-} 
-
-
-static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
-	align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
-		mask := align-1
-		return (ptr + mask) &~ mask
-	}
-	
-	if arena.block == nil {
-		reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
-		static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
-	}
-	
-	MINIMUM_ALIGN :: 2*align_of(uintptr)
-	
-	defer arena.total_used = arena.block.used
-	return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
-}
-
-static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
-	if arena.block != nil {
-		prev_pos := arena.block.used
-		arena.block.used = clamp(pos, 0, arena.block.reserved)
-		
-		if prev_pos < pos {
-			mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
-		}
-		return true
-	} else if pos == 0 {
-		return true
-	}
-	return false
-}
-
-static_arena_free_all :: proc(arena: ^Static_Arena) {
-	static_arena_reset_to(arena, 0)
-}
-
-
-static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
-	bootstrap: Static_Arena
-	bootstrap.minimum_block_size = reserved
-	
-	data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
-	
-	ptr = (^T)(raw_data(data))
-	
-	(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
-	
-	return
-}
-
-static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { 
-	return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
-}
-static_arena_bootstrap_new :: proc{
-	static_arena_bootstrap_new_by_offset, 
-	static_arena_bootstrap_new_by_name,
-}
-
-
-static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
-	return mem.Allocator{static_arena_allocator_proc, arena}
-}
-
-static_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: Allocator_Error) {
-	arena := (^Static_Arena)(allocator_data)
-	
-	switch mode {
-	case .Alloc:
-		return static_arena_alloc(arena, size, alignment)
-	case .Free:
-		err = .Mode_Not_Implemented
-		return
-	case .Free_All:
-		static_arena_free_all(arena)
-		return
-	case .Resize:
-		return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
-		
-	case .Query_Features, .Query_Info:
-		err = .Mode_Not_Implemented
-		return	
-	}	
-	
-	err = .Mode_Not_Implemented
-	return 
-}
-
-
-Static_Arena_Temp :: struct {
-	arena: ^Static_Arena,
-	used:  uint,
-}
-
-
-static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
-	temp.arena = arena
-	temp.used = arena.block.used if arena.block != nil else 0
-	arena.temp_count += 1
-	return
-}
-
-static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
-	assert(temp.arena != nil, "nil arena", loc)
-	arena := temp.arena
-	
-	used := arena.block.used if arena.block != nil else 0
-	
-	assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
-	
-	static_arena_reset_to(arena, temp.used)
-	
-	assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
-	arena.temp_count -= 1
-}
-
-
-static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
-	assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
-}

+ 15 - 4
core/mem/virtual/virtual.odin

@@ -1,6 +1,7 @@
 package mem_virtual
 
 import "core:mem"
+import "core:intrinsics"
 
 DEFAULT_PAGE_SIZE := uint(4096)
 
@@ -106,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 	return &pmblock.block, nil
 }
 
-alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
+alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
 	calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
 		alignment_offset := uint(0)
 		ptr := uintptr(block.base[block.used:])
@@ -134,11 +135,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
 		return nil
 	}
 
-
 	alignment_offset := calc_alignment_offset(block, uintptr(alignment))
-	size := uint(min_size) + alignment_offset
+	size, size_ok := safe_add(min_size, alignment_offset)
+	if !size_ok {
+		err = .Out_Of_Memory
+		return
+	}
 
-	if block.used + size > block.reserved {
+	if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
 		err = .Out_Of_Memory
 		return
 	}
@@ -162,3 +166,10 @@ memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
 	}
 }
 
+
+
+@(private)
+safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
+	z, did_overflow := intrinsics.overflow_add(x, y)
+	return z, !did_overflow
+}

+ 0 - 1
core/mem/virtual/virtual_platform.odin

@@ -63,7 +63,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
 		return .Out_Of_Memory
 	}
 
-
 	commit(block, to_commit) or_return
 	block.committed = to_commit
 	return nil