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 (4958B)


      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     assert(dst);
    117     for (u64 i = 0; i < size; i++) {
    118         ((u8 *)dst)[i] = ((u8 *)src)[i];
    119     }
    120 }
    121 
    122 internal void am_mem_set(u64 size, void *dst, u8 byte) {
    123     assert(dst);
    124     for (u64 i = 0; i < size; i++) {
    125         ((u8 *)dst)[i] = byte;
    126     }
    127 }
    128 
    129 internal bool am_mem_equal(void *dst, void *src, u64 size) {
    130     for (u64 i = 0; i < size; i++) {
    131         if (((u8 *)dst)[i] != ((u8 *)src)[i]) {
    132             return false;
    133         }
    134     }
    135     return true;
    136 }
    137 
    138 internal void am_memory_test(void) {
    139     assert(MEM_KIB(10) == 10240);
    140     assert(MEM_MIB(10) == 10485760);
    141     assert(MEM_GIB(10) == 10737418240);
    142 
    143     {
    144         BaseAllocator *a = am_mem_base_allocator_malloc();
    145         u64 num_test_ints = 10;
    146         s32 *int_buf = a->reserve(NULL, num_test_ints * sizeof(s32));
    147         assert(int_buf);
    148         a->release(NULL, int_buf, 0);
    149     }
    150 
    151     {
    152         MemArena a = am_mem_arena_create(am_mem_base_allocator_malloc());
    153         am_mem_arena_push(&a, 1020);
    154         assert(a.pos == 1020);
    155         u64 p = a.pos;
    156         u64 cp = a.commit_pos;
    157 
    158         AM_MEM_ARENA_PUSH_ARRAY(&a, u8, a.cap - a.pos);
    159         assert(a.pos == a.cap);
    160         assert(a.commit_pos == a.cap);
    161 
    162         am_mem_arena_pop_to(&a, p);
    163         assert(a.pos == p);
    164         assert(a.commit_pos == cp);
    165 
    166         am_mem_arena_release(&a);
    167         assert(a.pos == 0);
    168         assert(a.commit_pos == 0);
    169         assert(a.mem == NULL);
    170     }
    171 
    172     {
    173         u8 src[10] = {0};
    174         u8 dst[10] = {0};
    175         for (u64 i = 0; i < ARRAY_COUNT(src); i++) {
    176             assert(i < UINT8_MAX);
    177             src[i] = (u8)i;
    178         }
    179 
    180         am_mem_copy(dst, src, ARRAY_COUNT(dst));
    181         assert(am_mem_equal(dst, src, ARRAY_COUNT(dst)));
    182     }
    183 }
    184 
    185 #endif // AM_MEMORY_H