aboutsummaryrefslogtreecommitdiff
path: root/src/un_memory.c
blob: dd1bc8793f2fb4e0f37b6e4eb1d00cb5b395d67a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdlib.h>

/// -------- Basic functions

void *un_memory_alloc(u64 size, Allocator alloc) {
    assert(alloc.proc != NULL);
    return alloc.proc(NULL, size, UN_DEFAULT_ALIGN, UN_ALLOC_MSG_ALLOCATE, alloc.data);
}

void *un_memory_alloc_align(u64 size, u32 align, Allocator alloc) {
    assert(alloc.proc != NULL);
    return alloc.proc(NULL, size, align, UN_ALLOC_MSG_ALLOCATE, alloc.data);
}

void un_memory_free(void *ptr, Allocator alloc) {
    assert(alloc.proc != NULL);
    alloc.proc(ptr, 0, 0, UN_ALLOC_MSG_FREE, alloc.data);
}

/// -------- Std allocator

#if defined(OS_WINDOWS)
#   include "un_mem_win_x64.c"
#elif defined(OS_LINUX)
#   include "un_mem_linux_x64.c"
#else
#   include "un_mem_std.c"
#endif

Allocator un_alloc_std_get(void) {
    return CLITERAL(Allocator) { .proc = un_std_alloc_proc };
}

/// Arena allocator

typedef struct Arena {
    u8 *buffer;
    u64 size, curr_offset, prev_offset;
} Arena;

static Arena __temp_arena;

static Arena arena_create(u64 size) {
    Allocator alloc;
    Arena     arena;

    UN_CLEAR(arena);

    alloc = un_alloc_std_get();
    arena.buffer = un_memory_alloc(size, alloc);

    if (arena.buffer == NULL) {
        return arena;
    }

    arena.size = size;
    return arena;
}

// special thanks to ginger bill: https://www.gingerbill.org/article/2019/02/08/memory-allocation-strategies-002/
static uintptr_t align_forward(uintptr_t ptr, u32 align) {
    uintptr_t p, a, modulo;

    assert(UN_POW_OF2(align));

    p = ptr;
    a = (uintptr_t)align;
	modulo = p & (a-1);

	if (modulo != 0) {
		p += a - modulo;
	}

	return p;
}

static void* arena_allocate_align(Arena *arena, u32 align, u64 size) {
    uintptr_t curr_ptr, offset;
    u8 *ptr;

    assert(arena != NULL);

    curr_ptr = (uintptr_t)arena->buffer + arena->curr_offset;
    offset   = align_forward(curr_ptr, align);
    offset  -= (uintptr_t)arena->buffer;
    
    if ((offset + size) <= arena->size) {
        ptr = (u8*)(arena->buffer + offset);
        arena->prev_offset = offset;
        arena->curr_offset = offset + size;
        memset(ptr, 0, size);
    } else {
        ptr = NULL;
    }

    return ptr;
}

static void arena_free_memory(Arena *arena) {
    if (arena == &__temp_arena) return;

    assert(arena != NULL);

    if (arena->buffer != NULL) {
        un_memory_free(arena->buffer, un_alloc_std_get());
    }
}

static ALLOCATOR_PROC_SIGNATURE(un_arena_alloc_proc) {
    UNUSED(p);

    switch (message) {
        case UN_ALLOC_MSG_ALLOCATE:
            return arena_allocate_align((Arena*)data, align, size);
        case UN_ALLOC_MSG_FREE:
            break;
        case UN_ALLOC_MSG_SELF_DELETE:
            arena_free_memory((Arena*)data);
            break;
    }

    return NULL;
}

Allocator un_alloc_arena_create(u64 byte_size) {
    Allocator alloc, std;
    Arena arena;

    UN_CLEAR(alloc);
    std        = un_alloc_std_get();
    alloc.proc = un_arena_alloc_proc;
    arena      = arena_create(byte_size);

    if(arena.buffer == NULL) {
        UN_CLEAR(alloc);
    }

    alloc.data = un_memory_alloc(sizeof(arena), std); // yeah, it allocs 4kb for 32 bytes...

    if(alloc.data == NULL) {
        arena_free_memory(&arena);
        UN_CLEAR(alloc);
        return alloc;
    }

    *((Arena *)alloc.data) = arena;
    return alloc;
}

void un_alloc_arena_destroy(Allocator *alloc) {
    arena_free_memory((Arena*)alloc->data);

    un_memory_free(alloc->data, un_alloc_std_get());

    UN_CLEAR(*alloc);
}


void un_alloc_temp_init(u64 byte_size) {
    __temp_arena = arena_create(byte_size);
}

void un_alloc_temp_reset(void) {
    __temp_arena.curr_offset = 0;
    __temp_arena.prev_offset = 0;
}

Allocator un_alloc_temp_get(void) {
    assert(__temp_arena.buffer != NULL);

    return CLITERAL(Allocator) { un_arena_alloc_proc, &__temp_arena };
}