virtual.odin 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package mem_virtual
  2. import "core:mem"
  3. DEFAULT_PAGE_SIZE := uint(4096)
  4. Allocator_Error :: mem.Allocator_Error
  5. reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
  6. return _reserve(size)
  7. }
  8. commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
  9. return _commit(data, size)
  10. }
  11. reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
  12. data = reserve(size) or_return
  13. commit(raw_data(data), size) or_return
  14. return
  15. }
  16. decommit :: proc "contextless" (data: rawptr, size: uint) {
  17. _decommit(data, size)
  18. }
  19. release :: proc "contextless" (data: rawptr, size: uint) {
  20. _release(data, size)
  21. }
  22. Protect_Flag :: enum u32 {
  23. Read,
  24. Write,
  25. Execute,
  26. }
  27. Protect_Flags :: distinct bit_set[Protect_Flag; u32]
  28. Protect_No_Access :: Protect_Flags{}
  29. protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
  30. return _protect(data, size, flags)
  31. }
  32. Memory_Block :: struct {
  33. prev: ^Memory_Block,
  34. base: [^]byte,
  35. used: uint,
  36. committed: uint,
  37. reserved: uint,
  38. }
  39. Memory_Block_Flag :: enum u32 {
  40. Overflow_Protection,
  41. }
  42. Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
  43. memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
  44. align_formula :: proc "contextless" (size, align: uint) -> uint {
  45. result := size + align-1
  46. return result - result%align
  47. }
  48. page_size := DEFAULT_PAGE_SIZE
  49. committed := committed
  50. committed = clamp(committed, 0, reserved)
  51. total_size := uint(reserved + size_of(Platform_Memory_Block))
  52. base_offset := uintptr(size_of(Platform_Memory_Block))
  53. protect_offset := uintptr(0)
  54. do_protection := false
  55. if .Overflow_Protection in flags { // overflow protection
  56. rounded_size := align_formula(uint(reserved), page_size)
  57. total_size = uint(rounded_size + 2*page_size)
  58. base_offset = uintptr(page_size + rounded_size - uint(reserved))
  59. protect_offset = uintptr(page_size + rounded_size)
  60. do_protection = true
  61. }
  62. pmblock := platform_memory_alloc(0, total_size) or_return
  63. pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
  64. commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
  65. assert(commit_err == nil)
  66. // Should be zeroed
  67. assert(pmblock.block.used == 0)
  68. assert(pmblock.block.prev == nil)
  69. if do_protection {
  70. protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
  71. }
  72. pmblock.block.committed = committed
  73. pmblock.block.reserved = reserved
  74. sentinel := &global_platform_memory_block_sentinel
  75. platform_mutex_lock()
  76. pmblock.next = sentinel
  77. pmblock.prev = sentinel.prev
  78. pmblock.prev.next = pmblock
  79. pmblock.next.prev = pmblock
  80. platform_mutex_unlock()
  81. return &pmblock.block, nil
  82. }
  83. alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
  84. calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
  85. alignment_offset := uint(0)
  86. ptr := uintptr(block.base[block.used:])
  87. mask := alignment-1
  88. if ptr & mask != 0 {
  89. alignment_offset = uint(alignment - (ptr & mask))
  90. }
  91. return alignment_offset
  92. }
  93. do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
  94. if block.committed - block.used < size {
  95. pmblock := (^Platform_Memory_Block)(block)
  96. base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
  97. platform_total_commit := base_offset + block.used + size
  98. assert(pmblock.committed <= pmblock.reserved)
  99. assert(pmblock.committed < platform_total_commit)
  100. platform_memory_commit(pmblock, platform_total_commit) or_return
  101. pmblock.committed = platform_total_commit
  102. block.committed = pmblock.committed - base_offset
  103. }
  104. return nil
  105. }
  106. alignment_offset := calc_alignment_offset(block, uintptr(alignment))
  107. size := uint(min_size) + alignment_offset
  108. if block.used + size > block.reserved {
  109. err = .Out_Of_Memory
  110. return
  111. }
  112. assert(block.committed <= block.reserved)
  113. do_commit_if_necessary(block, size) or_return
  114. data = block.base[block.used+alignment_offset:][:min_size]
  115. block.used += size
  116. return
  117. }
  118. memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
  119. if block := (^Platform_Memory_Block)(block_to_free); block != nil {
  120. platform_mutex_lock()
  121. block.prev.next = block.next
  122. block.next.prev = block.prev
  123. platform_mutex_unlock()
  124. platform_memory_free(block)
  125. }
  126. }