default_temporary_allocator.odin 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package runtime
  2. @(private)
  3. byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
  4. return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}
  5. }
  6. DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
  7. Default_Temp_Allocator :: struct {
  8. data: []byte,
  9. curr_offset: int,
  10. prev_allocation: rawptr,
  11. backup_allocator: Allocator,
  12. leaked_allocations: [dynamic][]byte,
  13. }
  14. default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
  15. s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
  16. s.curr_offset = 0
  17. s.prev_allocation = nil
  18. s.backup_allocator = backup_allocator
  19. s.leaked_allocations.allocator = backup_allocator
  20. }
  21. default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
  22. if s == nil {
  23. return
  24. }
  25. for ptr in s.leaked_allocations {
  26. free(raw_data(ptr), s.backup_allocator)
  27. }
  28. delete(s.leaked_allocations)
  29. delete(s.data, s.backup_allocator)
  30. s^ = {}
  31. }
  32. @(private)
  33. default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
  34. size := size
  35. size = align_forward_int(size, alignment)
  36. switch {
  37. case s.curr_offset+size <= len(s.data):
  38. start := uintptr(raw_data(s.data))
  39. ptr := start + uintptr(s.curr_offset)
  40. ptr = align_forward_uintptr(ptr, uintptr(alignment))
  41. mem_zero(rawptr(ptr), size)
  42. s.prev_allocation = rawptr(ptr)
  43. offset := int(ptr - start)
  44. s.curr_offset = offset + size
  45. return byte_slice(rawptr(ptr), size), .None
  46. case size <= len(s.data):
  47. start := uintptr(raw_data(s.data))
  48. ptr := align_forward_uintptr(start, uintptr(alignment))
  49. mem_zero(rawptr(ptr), size)
  50. s.prev_allocation = rawptr(ptr)
  51. offset := int(ptr - start)
  52. s.curr_offset = offset + size
  53. return byte_slice(rawptr(ptr), size), .None
  54. }
  55. a := s.backup_allocator
  56. if a.procedure == nil {
  57. a = context.allocator
  58. s.backup_allocator = a
  59. }
  60. data, err := mem_alloc_bytes(size, alignment, a, loc)
  61. if err != nil {
  62. return data, err
  63. }
  64. if s.leaked_allocations == nil {
  65. s.leaked_allocations = make([dynamic][]byte, a)
  66. }
  67. append(&s.leaked_allocations, data)
  68. // TODO(bill): Should leaks be notified about?
  69. if logger := context.logger; logger.lowest_level <= .Warning {
  70. if logger.procedure != nil {
  71. logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
  72. }
  73. }
  74. return data, .None
  75. }
  76. @(private)
  77. default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
  78. if old_memory == nil {
  79. return .None
  80. }
  81. start := uintptr(raw_data(s.data))
  82. end := start + uintptr(len(s.data))
  83. old_ptr := uintptr(old_memory)
  84. if s.prev_allocation == old_memory {
  85. s.curr_offset = int(uintptr(s.prev_allocation) - start)
  86. s.prev_allocation = nil
  87. return .None
  88. }
  89. if start <= old_ptr && old_ptr < end {
  90. // NOTE(bill): Cannot free this pointer but it is valid
  91. return .None
  92. }
  93. if len(s.leaked_allocations) != 0 {
  94. for data, i in s.leaked_allocations {
  95. ptr := raw_data(data)
  96. if ptr == old_memory {
  97. free(ptr, s.backup_allocator)
  98. ordered_remove(&s.leaked_allocations, i)
  99. return .None
  100. }
  101. }
  102. }
  103. return .Invalid_Pointer
  104. // panic("invalid pointer passed to default_temp_allocator");
  105. }
  106. @(private)
  107. default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
  108. s.curr_offset = 0
  109. s.prev_allocation = nil
  110. for data in s.leaked_allocations {
  111. free(raw_data(data), s.backup_allocator)
  112. }
  113. clear(&s.leaked_allocations)
  114. }
  115. @(private)
  116. default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
  117. begin := uintptr(raw_data(s.data))
  118. end := begin + uintptr(len(s.data))
  119. old_ptr := uintptr(old_memory)
  120. if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
  121. if old_ptr+uintptr(size) < end {
  122. s.curr_offset = int(old_ptr-begin)+size
  123. return byte_slice(old_memory, size), .None
  124. }
  125. }
  126. data, err := default_temp_allocator_alloc(s, size, alignment, loc)
  127. if err == .None {
  128. copy(data, byte_slice(old_memory, old_size))
  129. err = default_temp_allocator_free(s, old_memory, loc)
  130. }
  131. return data, err
  132. }
  133. default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
  134. size, alignment: int,
  135. old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
  136. s := (^Default_Temp_Allocator)(allocator_data)
  137. if s.data == nil {
  138. default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
  139. }
  140. switch mode {
  141. case .Alloc:
  142. data, err = default_temp_allocator_alloc(s, size, alignment, loc)
  143. case .Free:
  144. err = default_temp_allocator_free(s, old_memory, loc)
  145. case .Free_All:
  146. default_temp_allocator_free_all(s, loc)
  147. case .Resize:
  148. data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
  149. case .Query_Features:
  150. set := (^Allocator_Mode_Set)(old_memory)
  151. if set != nil {
  152. set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
  153. }
  154. case .Query_Info:
  155. // Nothing to give
  156. }
  157. return
  158. }
  159. default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
  160. return Allocator{
  161. procedure = default_temp_allocator_proc,
  162. data = allocator,
  163. }
  164. }