advent-of-code

Solutions for Advent of Code.
git clone git://git.amin.space/advent-of-code.git
Log | Files | Refs | LICENSE

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