growing_arena.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package mem_virtual
  2. import "core:mem"
  3. Growing_Arena :: struct {
  4. curr_block: ^Memory_Block,
  5. total_used: uint,
  6. total_reserved: uint,
  7. minimum_block_size: uint,
  8. temp_count: int,
  9. }
  10. DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
  11. growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
  12. arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
  13. arena.total_used = 0
  14. arena.total_reserved = arena.curr_block.reserved
  15. return
  16. }
  17. growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
  18. align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
  19. alignment_offset := uint(0)
  20. ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
  21. mask := uintptr(alignment-1)
  22. if ptr & mask != 0 {
  23. alignment_offset = uint(alignment) - uint(ptr & mask)
  24. }
  25. return alignment_offset
  26. }
  27. assert(mem.is_power_of_two(uintptr(alignment)))
  28. size := uint(0)
  29. if arena.curr_block != nil {
  30. size = uint(min_size) + align_forward_offset(arena, alignment)
  31. }
  32. if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
  33. size = uint(mem.align_forward_int(min_size, alignment))
  34. arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
  35. block_size := max(size, arena.minimum_block_size)
  36. new_block := memory_block_alloc(size, block_size, {}) or_return
  37. new_block.prev = arena.curr_block
  38. arena.curr_block = new_block
  39. arena.total_reserved += new_block.reserved
  40. }
  41. data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
  42. if err == nil {
  43. arena.total_used += size
  44. }
  45. return
  46. }
  47. growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
  48. free_block := arena.curr_block
  49. arena.curr_block = free_block.prev
  50. memory_block_dealloc(free_block)
  51. }
  52. growing_arena_free_all :: proc(arena: ^Growing_Arena) {
  53. for arena.curr_block != nil {
  54. growing_arena_free_last_memory_block(arena)
  55. }
  56. arena.total_used = 0
  57. arena.total_reserved = 0
  58. }
  59. growing_arena_destroy :: proc(arena: ^Growing_Arena) {
  60. growing_arena_free_all(arena)
  61. }
  62. growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
  63. bootstrap: Growing_Arena
  64. bootstrap.minimum_block_size = minimum_block_size
  65. data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
  66. ptr = (^T)(raw_data(data))
  67. (^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
  68. return
  69. }
  70. growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
  71. return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
  72. }
  73. growing_arena_bootstrap_new :: proc{
  74. growing_arena_bootstrap_new_by_offset,
  75. growing_arena_bootstrap_new_by_name,
  76. }
  77. growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
  78. return mem.Allocator{growing_arena_allocator_proc, arena}
  79. }
  80. growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
  81. size, alignment: int,
  82. old_memory: rawptr, old_size: int,
  83. location := #caller_location) -> (data: []byte, err: Allocator_Error) {
  84. arena := (^Growing_Arena)(allocator_data)
  85. switch mode {
  86. case .Alloc:
  87. return growing_arena_alloc(arena, size, alignment)
  88. case .Free:
  89. err = .Mode_Not_Implemented
  90. return
  91. case .Free_All:
  92. growing_arena_free_all(arena)
  93. return
  94. case .Resize:
  95. return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
  96. case .Query_Features, .Query_Info:
  97. err = .Mode_Not_Implemented
  98. return
  99. }
  100. err = .Mode_Not_Implemented
  101. return
  102. }
  103. Growing_Arena_Temp :: struct {
  104. arena: ^Growing_Arena,
  105. block: ^Memory_Block,
  106. used: uint,
  107. }
  108. growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
  109. temp.arena = arena
  110. temp.block = arena.curr_block
  111. if arena.curr_block != nil {
  112. temp.used = arena.curr_block.used
  113. }
  114. arena.temp_count += 1
  115. return
  116. }
  117. growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
  118. assert(temp.arena != nil, "nil arena", loc)
  119. arena := temp.arena
  120. for arena.curr_block != temp.block {
  121. growing_arena_free_last_memory_block(arena)
  122. }
  123. if block := arena.curr_block; block != nil {
  124. assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
  125. amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
  126. mem.zero_slice(block.base[temp.used:][:amount_to_zero])
  127. block.used = temp.used
  128. }
  129. assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
  130. arena.temp_count -= 1
  131. }
  132. growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
  133. assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
  134. }