static_arena.odin 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package mem_virtual
  2. import "core:mem"
  3. Static_Arena :: struct {
  4. block: ^Memory_Block,
  5. total_used: uint,
  6. total_reserved: uint,
  7. minimum_block_size: uint,
  8. temp_count: int,
  9. }
  10. STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
  11. // 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
  12. STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
  13. static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
  14. arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
  15. arena.total_used = 0
  16. arena.total_reserved = arena.block.reserved
  17. return
  18. }
  19. static_arena_destroy :: proc(arena: ^Static_Arena) {
  20. memory_block_dealloc(arena.block)
  21. arena^ = {}
  22. }
  23. static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
  24. align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
  25. mask := align-1
  26. return (ptr + mask) &~ mask
  27. }
  28. if arena.block == nil {
  29. reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
  30. static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
  31. }
  32. MINIMUM_ALIGN :: 2*align_of(uintptr)
  33. defer arena.total_used = arena.block.used
  34. return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
  35. }
  36. static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
  37. if arena.block != nil {
  38. prev_pos := arena.block.used
  39. arena.block.used = clamp(pos, 0, arena.block.reserved)
  40. if prev_pos < pos {
  41. mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
  42. }
  43. return true
  44. } else if pos == 0 {
  45. return true
  46. }
  47. return false
  48. }
  49. static_arena_free_all :: proc(arena: ^Static_Arena) {
  50. static_arena_reset_to(arena, 0)
  51. }
  52. static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
  53. bootstrap: Static_Arena
  54. bootstrap.minimum_block_size = reserved
  55. data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
  56. ptr = (^T)(raw_data(data))
  57. (^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
  58. return
  59. }
  60. static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
  61. return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
  62. }
  63. static_arena_bootstrap_new :: proc{
  64. static_arena_bootstrap_new_by_offset,
  65. static_arena_bootstrap_new_by_name,
  66. }
  67. static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
  68. return mem.Allocator{static_arena_allocator_proc, arena}
  69. }
  70. static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
  71. size, alignment: int,
  72. old_memory: rawptr, old_size: int,
  73. location := #caller_location) -> (data: []byte, err: Allocator_Error) {
  74. arena := (^Static_Arena)(allocator_data)
  75. switch mode {
  76. case .Alloc:
  77. return static_arena_alloc(arena, size, alignment)
  78. case .Free:
  79. err = .Mode_Not_Implemented
  80. return
  81. case .Free_All:
  82. static_arena_free_all(arena)
  83. return
  84. case .Resize:
  85. return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
  86. case .Query_Features, .Query_Info:
  87. err = .Mode_Not_Implemented
  88. return
  89. }
  90. err = .Mode_Not_Implemented
  91. return
  92. }
  93. Static_Arena_Temp :: struct {
  94. arena: ^Static_Arena,
  95. used: uint,
  96. }
  97. static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
  98. temp.arena = arena
  99. temp.used = arena.block.used if arena.block != nil else 0
  100. arena.temp_count += 1
  101. return
  102. }
  103. static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
  104. assert(temp.arena != nil, "nil arena", loc)
  105. arena := temp.arena
  106. used := arena.block.used if arena.block != nil else 0
  107. assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
  108. static_arena_reset_to(arena, temp.used)
  109. assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
  110. arena.temp_count -= 1
  111. }
  112. static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
  113. assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
  114. }