am_memory.h (4817B)
1 #ifndef AM_MEMORY_H 2 #define AM_MEMORY_H 3 4 #define MEM_KIB(b) ((u64)(b) << 10) 5 #define MEM_MIB(b) ((u64)(b) << 20) 6 #define MEM_GIB(b) ((u64)(b) << 30) 7 8 #define AM_MEM_RESERVE_FN(name) void *(name)(void *ctx, u64 size) 9 typedef AM_MEM_RESERVE_FN(am_mem_reserve_fn); 10 11 #define AM_MEM_CHANGE_FN(name) void (name)(void *ctx, void *ptr, u64 size) 12 typedef AM_MEM_CHANGE_FN(am_mem_change_fn); 13 14 typedef struct { 15 am_mem_reserve_fn* reserve; 16 am_mem_change_fn* commit; 17 am_mem_change_fn* decommit; 18 am_mem_change_fn* release; 19 void *ctx; 20 } BaseAllocator; 21 22 #define AM_MEM_ARENA_COMMIT_BLOCK_SIZE MEM_MIB(64) 23 #define AM_MEM_ARENA_DEFAULT_RESERVE_SIZE MEM_GIB(1) 24 25 typedef struct { 26 BaseAllocator *base; 27 u8 *mem; 28 u64 cap; 29 u64 pos; 30 u64 commit_pos; 31 } MemArena; 32 33 internal AM_MEM_CHANGE_FN(am_mem_change_noop) { 34 UNUSED(ctx); 35 UNUSED(ptr); 36 UNUSED(size); 37 } 38 39 internal AM_MEM_RESERVE_FN(am_mem_reserve_malloc) { 40 UNUSED(ctx); 41 return malloc(size); 42 } 43 44 internal AM_MEM_CHANGE_FN(am_mem_release_malloc) { 45 UNUSED(ctx); 46 UNUSED(size); 47 free(ptr); 48 } 49 50 internal BaseAllocator *am_mem_base_allocator_malloc(void) { 51 local_persist BaseAllocator b; 52 if (!b.reserve) { 53 b = (BaseAllocator) { 54 .reserve = am_mem_reserve_malloc, 55 .commit = am_mem_change_noop, 56 .decommit = am_mem_change_noop, 57 .release = am_mem_release_malloc, 58 }; 59 } 60 return &b; 61 } 62 63 internal MemArena am_mem_arena_create_reserve(BaseAllocator *base, u64 reserve_size) { 64 MemArena a = { 65 .base = base, 66 }; 67 a.mem = a.base->reserve(a.base->ctx, reserve_size); 68 a.cap = reserve_size; 69 return a; 70 } 71 72 internal MemArena am_mem_arena_create(BaseAllocator *base) { 73 return am_mem_arena_create_reserve(base, AM_MEM_ARENA_DEFAULT_RESERVE_SIZE); 74 } 75 76 internal void *am_mem_arena_push(MemArena *a, u64 size) { 77 void *result = NULL; 78 if (a->mem + size <= a->mem + a->cap) { 79 result = a->mem + a->pos; 80 a->pos += size; 81 82 if (a->pos > a->commit_pos) { 83 u64 aligned = ALIGN_UP_POW_2(a->pos, AM_MEM_ARENA_COMMIT_BLOCK_SIZE); 84 u64 next_commit_pos = CLAMP_TOP(aligned, a->cap); 85 u64 commit_size = next_commit_pos - a->commit_pos; 86 a->base->commit(a->base->ctx, a->mem + a->commit_pos, commit_size); 87 a->commit_pos = next_commit_pos; 88 } 89 } 90 return result; 91 } 92 93 #define AM_MEM_ARENA_PUSH_ARRAY(arena, T, count) (am_mem_arena_push((arena), sizeof(T) * (count))) 94 95 internal void am_mem_arena_pop_to(MemArena *a, u64 pos) { 96 if (pos < a->pos) { 97 a->pos = pos; 98 99 u64 aligned = ALIGN_UP_POW_2(a->pos, AM_MEM_ARENA_COMMIT_BLOCK_SIZE); 100 u64 next_commit_pos = CLAMP_TOP(aligned, a->cap); 101 if (next_commit_pos < a->commit_pos) { 102 u64 decommit_size = a->commit_pos - next_commit_pos; 103 a->base->decommit(a->base->ctx, a->mem + a->commit_pos, decommit_size); 104 a->commit_pos = next_commit_pos; 105 } 106 } 107 } 108 109 internal void am_mem_arena_release(MemArena *a) { 110 a->base->release(a->base->ctx, a->mem, a->cap); 111 *a = (MemArena){0}; 112 } 113 114 // TODO: SIMD 115 internal void am_mem_copy(void *dst, void *src, u64 size) { 116 for (u64 i = 0; i < size; i++) { 117 ((u8 *)dst)[i] = ((u8 *)src)[i]; 118 } 119 } 120 121 internal bool am_mem_equal(void *dst, void *src, u64 size) { 122 for (u64 i = 0; i < size; i++) { 123 if (((u8 *)dst)[i] != ((u8 *)src)[i]) { 124 return false; 125 } 126 } 127 return true; 128 } 129 130 #if defined(AM_INCLUDE_TESTS) 131 internal void am_memory_test(void) { 132 assert(MEM_KIB(10) == 10240); 133 assert(MEM_MIB(10) == 10485760); 134 assert(MEM_GIB(10) == 10737418240); 135 136 { 137 BaseAllocator *a = am_mem_base_allocator_malloc(); 138 u64 num_test_ints = 10; 139 s32 *int_buf = a->reserve(NULL, num_test_ints * sizeof(s32)); 140 assert(int_buf); 141 a->release(NULL, int_buf, 0); 142 } 143 144 { 145 MemArena a = am_mem_arena_create(am_mem_base_allocator_malloc()); 146 am_mem_arena_push(&a, 1020); 147 assert(a.pos == 1020); 148 u64 p = a.pos; 149 u64 cp = a.commit_pos; 150 151 AM_MEM_ARENA_PUSH_ARRAY(&a, u8, a.cap - a.pos); 152 assert(a.pos == a.cap); 153 assert(a.commit_pos == a.cap); 154 155 am_mem_arena_pop_to(&a, p); 156 assert(a.pos == p); 157 assert(a.commit_pos == cp); 158 159 am_mem_arena_release(&a); 160 assert(a.pos == 0); 161 assert(a.commit_pos == 0); 162 assert(a.mem == NULL); 163 } 164 165 { 166 u8 src[10] = {0}; 167 u8 dst[10] = {0}; 168 for (u64 i = 0; i < ARRAY_COUNT(src); i++) { 169 src[i] = i; 170 } 171 172 am_mem_copy(dst, src, ARRAY_COUNT(dst)); 173 assert(am_mem_equal(dst, src, ARRAY_COUNT(dst))); 174 } 175 } 176 #endif // defined(AM_INCLUDE_TESTS) 177 178 #endif // AM_MEMORY_H