-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzdx_memory.h
182 lines (141 loc) · 4.97 KB
/
zdx_memory.h
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
175
176
177
178
179
180
181
182
#ifndef ZDX_MEMORY_H_
#define ZDX_MEMORY_H_
#ifndef MEM_API
#define MEM_API
#endif // MEM_API
#ifdef ZDX_MEMORY_IMPLEMENTATION_AUTO
# define ZDX_STRING_VIEW_IMPLEMENTATION
# define ZDX_MEMORY_IMPLEMENTATION
#endif // ZDX_MEMORY_IMPLEMENTATION_AUTO
#include <stddef.h>
#include "zdx_string_view.h"
#include "zdx_util.h"
typedef struct mem_allocator_t mem_allocator_t;
/**
* To understand function pointers -
*
* extern void (*signal(int, void(*)(int)))(int); <-- the outermost `void` and outermost `(int)`
* come together to make the return type of
* signal be a function void (*)(int) aka
* SignalHandler below.
* ^^^^^^ is the same as below:
* typedef void (*SignalHandler)(int signum);
* extern SignalHandler signal(int signum, SignalHandler handler);
*/
typedef void* (*mem_malloc_fn_t)(const mem_allocator_t *const al, const size_t sz);
typedef void* (*mem_calloc_fn_t)(const mem_allocator_t *const al, const size_t count, const size_t sz);
typedef void* (*mem_realloc_fn_t)(const mem_allocator_t *const al, void *ptr, const size_t old_sz, const size_t new_sz);
typedef void (*mem_free_fn_t)(const mem_allocator_t *const al, void *ptr);
typedef void (*mem_empty_fn_t)(const mem_allocator_t *const al);
// TODO(mudit): should it deinit the allocator and thus null out ctx
// after deinit(ctx)? or should it only deinit the ctx and leave *ctx
// as a valid pointer?
typedef void (*mem_deinit_fn_t)(mem_allocator_t *const al);
typedef struct mem_allocator_t {
void *ctx;
sv_t name;
mem_malloc_fn_t alloc;
mem_calloc_fn_t calloc;
mem_realloc_fn_t realloc;
mem_free_fn_t free;
mem_empty_fn_t empty;
mem_deinit_fn_t deinit;
} mem_allocator_t;
MEM_API mem_allocator_t mem_gpa_init(const char name_cstr[const static 1]);
// ----------------------------------------------------------------------------------------------------------------
#ifdef ZDX_MEMORY_IMPLEMENTATION
#include <stdlib.h>
#ifndef MEM_ASSERT
#define MEM_ASSERT assertm
#endif // MEM_ASSERT
#define MEM_ASSERT_NONNULL(val) MEM_ASSERT((val) != NULL, "Expected: context to be non-NULL, Received: %p", (void *)(val))
static void *gpa_malloc(const mem_allocator_t *const al, const size_t sz)
{
(void) al;
MEM_ASSERT_NONNULL(al);
dbg(">> [allocator "SV_FMT"]: size = %zu", sv_fmt_args(al->name), sz);
void *ptr = malloc(sz);
dbg("<< [allocator "SV_FMT"]: %p", sv_fmt_args(al->name), ptr);
return ptr;
}
static void *gpa_calloc(const mem_allocator_t *const al, const size_t count, const size_t sz)
{
(void) al;
MEM_ASSERT_NONNULL(al);
dbg(">> [allocator "SV_FMT"]: count = %zu, size = %zu",
sv_fmt_args(al->name), count, sz);
void *ptr = calloc(count, sz);
dbg("<< [allocator "SV_FMT"]: %p", sv_fmt_args(al->name), ptr);
return ptr;
}
static void *gpa_realloc(const mem_allocator_t *const al, void *ptr, const size_t old_sz, const size_t new_sz)
{
(void) al;
MEM_ASSERT_NONNULL(al);
(void) old_sz;
dbg(">> [allocator "SV_FMT"]: ptr = %p, old size = %zu, new size = %zu",
sv_fmt_args(al->name), ptr, old_sz, new_sz);
void *new_ptr = realloc(ptr, new_sz);
dbg("<< [allocator "SV_FMT"]: realloced ptr = %p", sv_fmt_args(al->name), ptr);
return new_ptr;
}
static void gpa_free(const mem_allocator_t *const al, void *ptr)
{
(void) al;
MEM_ASSERT_NONNULL(al);
dbg(">> [allocator "SV_FMT"]: ptr = %p", sv_fmt_args(al->name), ptr);
dbg("<< [allocator "SV_FMT"]", sv_fmt_args(al->name));
free(ptr);
}
static void gpa_empty(const mem_allocator_t *const al)
{
(void) al;
return;
}
static void gpa_deinit(mem_allocator_t *const al)
{
MEM_ASSERT_NONNULL(al);
dbg(">> [allocator "SV_FMT"]", sv_fmt_args(al->name));
sv_t name = al->name;
(void) name;
al->name = (sv_t){0};
free(al->ctx);
// reset the name string view. If it holds a dynamically allocated pointer,
// it's whoever allocated it that needs to free it as string view never
// allocates and is only a readonly view on the buffer pointer it holds.
dbg("<< [allocator "SV_FMT"] Destroyed!", sv_fmt_args(name));
return;
}
/**
* GPA = General Purpose Allocator
* This function initializes a malloc and friends based general purpose allocator
*
* Example:
* mem_allocator_t gpa = mem_gpa_init("test");
* int *a = gpa.alloc(&gpa, sizeof(*a) * 10);
* ...
* ...
* gpa.free(&gpa, a);
* ...
* ...
* gpa.deinit(&gpa);
*/
MEM_API mem_allocator_t mem_gpa_init(const char name_cstr[const static 1])
{
MEM_ASSERT_NONNULL((void *)name_cstr);
dbg(">> name = %s", name_cstr);
mem_allocator_t allocator = {
.ctx = NULL,
.name = sv_from_cstr(name_cstr),
.alloc = gpa_malloc,
.calloc = gpa_calloc,
.realloc = gpa_realloc,
.free = gpa_free,
.empty = gpa_empty,
.deinit = gpa_deinit,
};
dbg("<< [allocator "SV_FMT"]", sv_fmt_args(allocator.name));
return allocator;
}
#endif // ZDX_MEMORY_IMPLEMENTATION
#endif // ZDX_MEMORY_H_