virtual.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package mem_virtual
  2. import "core:mem"
  3. import "base:intrinsics"
  4. import "base:runtime"
  5. _ :: runtime
  6. DEFAULT_PAGE_SIZE := uint(4096)
  7. Allocator_Error :: mem.Allocator_Error
  8. @(require_results)
  9. reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
  10. return _reserve(size)
  11. }
  12. commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
  13. return _commit(data, size)
  14. }
  15. @(require_results)
  16. reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
  17. data = reserve(size) or_return
  18. commit(raw_data(data), size) or_return
  19. return
  20. }
  21. decommit :: proc "contextless" (data: rawptr, size: uint) {
  22. _decommit(data, size)
  23. }
  24. release :: proc "contextless" (data: rawptr, size: uint) {
  25. _release(data, size)
  26. }
  27. Protect_Flag :: enum u32 {
  28. Read,
  29. Write,
  30. Execute,
  31. }
  32. Protect_Flags :: distinct bit_set[Protect_Flag; u32]
  33. Protect_No_Access :: Protect_Flags{}
  34. protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
  35. return _protect(data, size, flags)
  36. }
  37. Memory_Block :: struct {
  38. prev: ^Memory_Block,
  39. base: [^]byte,
  40. used: uint,
  41. committed: uint,
  42. reserved: uint,
  43. }
  44. Memory_Block_Flag :: enum u32 {
  45. Overflow_Protection,
  46. }
  47. Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
  48. @(private="file", require_results)
  49. align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint {
  50. result := size + align-1
  51. return result - result%align
  52. }
  53. @(require_results)
  54. memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags: Memory_Block_Flags = {}) -> (block: ^Memory_Block, err: Allocator_Error) {
  55. page_size := DEFAULT_PAGE_SIZE
  56. assert(mem.is_power_of_two(uintptr(page_size)))
  57. committed := committed
  58. reserved := reserved
  59. committed = align_formula(committed, page_size)
  60. reserved = align_formula(reserved, page_size)
  61. committed = clamp(committed, 0, reserved)
  62. total_size := uint(reserved + max(alignment, size_of(Platform_Memory_Block)))
  63. base_offset := uintptr(max(alignment, size_of(Platform_Memory_Block)))
  64. protect_offset := uintptr(0)
  65. do_protection := false
  66. if .Overflow_Protection in flags { // overflow protection
  67. rounded_size := reserved
  68. total_size = uint(rounded_size + 2*page_size)
  69. base_offset = uintptr(page_size + rounded_size - uint(reserved))
  70. protect_offset = uintptr(page_size + rounded_size)
  71. do_protection = true
  72. }
  73. pmblock := platform_memory_alloc(0, total_size) or_return
  74. pmblock.block.base = ([^]byte)(pmblock)[base_offset:]
  75. platform_memory_commit(pmblock, uint(base_offset) + committed) or_return
  76. // Should be zeroed
  77. assert(pmblock.block.used == 0)
  78. assert(pmblock.block.prev == nil)
  79. if do_protection {
  80. protect(([^]byte)(pmblock)[protect_offset:], page_size, Protect_No_Access)
  81. }
  82. pmblock.block.committed = committed
  83. pmblock.block.reserved = reserved
  84. return &pmblock.block, nil
  85. }
  86. @(require_results)
  87. alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
  88. calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
  89. alignment_offset := uint(0)
  90. ptr := uintptr(block.base[block.used:])
  91. mask := alignment-1
  92. if ptr & mask != 0 {
  93. alignment_offset = uint(alignment - (ptr & mask))
  94. }
  95. return alignment_offset
  96. }
  97. do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
  98. if block.committed - block.used < size {
  99. pmblock := (^Platform_Memory_Block)(block)
  100. base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
  101. // NOTE(bill): [Heuristic] grow the commit size larger than needed
  102. // TODO(bill): determine a better heuristic for this behaviour
  103. extra_size := max(size, block.committed>>1)
  104. platform_total_commit := base_offset + block.used + extra_size
  105. platform_total_commit = align_formula(platform_total_commit, DEFAULT_PAGE_SIZE)
  106. platform_total_commit = min(platform_total_commit, pmblock.reserved)
  107. assert(pmblock.committed <= pmblock.reserved)
  108. assert(pmblock.committed < platform_total_commit)
  109. platform_memory_commit(pmblock, platform_total_commit) or_return
  110. pmblock.committed = platform_total_commit
  111. block.committed = pmblock.committed - base_offset
  112. }
  113. return
  114. }
  115. if block == nil {
  116. return nil, .Out_Of_Memory
  117. }
  118. alignment_offset := calc_alignment_offset(block, uintptr(alignment))
  119. size, size_ok := safe_add(min_size, alignment_offset)
  120. if !size_ok {
  121. err = .Out_Of_Memory
  122. return
  123. }
  124. if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
  125. err = .Out_Of_Memory
  126. return
  127. }
  128. assert(block.committed <= block.reserved)
  129. do_commit_if_necessary(block, size) or_return
  130. data = block.base[block.used+alignment_offset:][:min_size]
  131. block.used += size
  132. return
  133. }
  134. memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
  135. if block := (^Platform_Memory_Block)(block_to_free); block != nil {
  136. platform_memory_free(block)
  137. }
  138. }
  139. @(private, require_results)
  140. safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
  141. z, did_overflow := intrinsics.overflow_add(x, y)
  142. return z, !did_overflow
  143. }