Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
5
7#if !defined(SKIP_INCLUDES)
8 #include "includes.h"
9#endif
10
12
13#if !defined CAPACITY
14 #if !defined PLACEHOLDERS
15 #error "The capacity of the static allocator must be defined"
16 #endif
17 #define CAPACITY 1024
18#endif
19
20#if CAPACITY > (1ULL << 30)
21 #error "CAPACITY must not exceed 1 GiB"
22#endif
23
24#if CAPACITY <= UINT8_MAX
25 #define USED uint8_t
26#elif CAPACITY <= UINT16_MAX
27 #define USED uint16_t
28#elif CAPACITY <= UINT32_MAX
29 #define USED uint32_t
30#else
31 #define USED uint64_t
32#endif
33
34typedef char NS(SELF, buffer)[CAPACITY];
35
36typedef struct {
37 size_t used;
40} SELF;
41
42static size_t NS(SELF, metadata_size) = sizeof(USED);
43
44static SELF NS(SELF, new)(NS(SELF, buffer) * buffer) {
45 SELF self = {
46 .used = 0,
47 .buffer = buffer,
48 .derive_c_staticbumpalloc = {},
49 };
50 // JUSTIFY: Zeroed buffer
51 // - For easier debugging & view in gdb.
52 // - Additionally allows for calloc to be malloc.
53 memset(*self.buffer, 0, CAPACITY);
54
55 // JUSTIFY: no capabilities on init
56 // - Protect access by users outside of malloc/calloc
58 CAPACITY);
59
60 return self;
61}
62
64static void NS(SELF, clear)(SELF* self) {
65 DC_ASSUME(self);
66
67#if !defined NDEBUG
68 // JUSTIFY: Allocations & sizes zeroed on free in debug, we check all data has been freed.
70 (*self->buffer), self->used);
71 for (size_t i = 0; i < self->used; i++) {
72 if ((*self->buffer)[i] != 0) {
73 DC_PANIC("Data not freed before clearing the static bump allocator");
74 }
75 }
76#endif
78 self->used);
79 memset((*self->buffer), 0, self->used);
81 CAPACITY);
82 self->used = 0;
83}
84
85static USED NS(SELF, get_used)(SELF const* self) {
86 DC_ASSUME(self);
87 return self->used;
88}
89
90static void* NS(SELF, malloc)(SELF* self, size_t size) {
91 DC_ASSUME(self);
92 if (self->used + (size + sizeof(USED)) > CAPACITY) {
93 return NULL;
94 }
95 char* ptr = &(*self->buffer)[self->used];
96 USED* used_ptr = (USED*)ptr;
97
99 sizeof(USED));
100 *used_ptr = size;
101 char* allocation_ptr = ptr + sizeof(USED);
103 sizeof(USED));
104
105 self->used += size + sizeof(USED);
107 size);
108
109 return allocation_ptr;
110}
111
112static void NS(SELF, free)(SELF* self, void* ptr) {
113 DC_ASSUME(self);
114 DC_ASSUME(ptr);
115
116#if !defined NDEBUG
117 // JUSTIFY: Zero memory in debug.
118 // - Expensive for release, but helpful when debugging
119 // NOTE: This means that users should free, before they clear and reuse the buffer.
120 USED* used_ptr = (USED*)((char*)ptr - sizeof(USED));
122 sizeof(USED));
123 memset(ptr, 0, *used_ptr);
125 *used_ptr = 0;
127 sizeof(USED));
128#endif
129}
130
131static void* NS(SELF, realloc)(SELF* self, void* ptr, size_t new_size) {
132 DC_ASSUME(self);
133
134 if (!ptr) {
135 return NS(SELF, malloc)(self, new_size);
136 }
137
138 char* byte_ptr = (char*)ptr;
139 USED* old_size = (USED*)(byte_ptr - sizeof(USED));
141 sizeof(USED));
142 const bool was_last_alloc = (byte_ptr + *old_size == &(*self->buffer)[self->used]);
143
144 if (was_last_alloc) {
145 if (new_size > *old_size) {
146 size_t increase = new_size - *old_size;
147 if (self->used + increase > CAPACITY) {
148 return NULL;
149 }
150 self->used += increase;
152 (char*)ptr + *old_size, increase);
153 } else if (new_size < *old_size) {
154 size_t decrease = *old_size - new_size;
155 self->used -= decrease;
156
157 // JUSTIFY: Zeroing the end of the old allocation
158 // - Future calls to calloc may reuse this memory
159 memset((char*)ptr + new_size, 0, decrease);
160
162 (char*)ptr + new_size, decrease);
163 }
164
165 *old_size = new_size;
167 sizeof(USED));
168 return ptr;
169 }
170
171 if (new_size < *old_size) {
172 // JUSTIFY: Shrinking an allocation
173 // - Can become a noop - as we cannot re-extend the allocation
174 // afterwards - no metadata for unused capacity not at end of buffer.
175 *old_size = new_size;
177 sizeof(USED));
178 return ptr;
179 }
180
181 void* new_buff = NS(SELF, malloc)(self, new_size);
182 if (!new_buff) {
183 return NULL;
184 }
185
186 memcpy(new_buff, ptr, *old_size);
187
188 NS(SELF, free)(self, ptr);
189 return new_buff;
190}
191
192static void* NS(SELF, calloc)(SELF* self, size_t count, size_t size) {
193 // JUSTIFY:
194 // - We already zeroed the buffer in `new()`
195 return NS(SELF, malloc)(self, count * size);
196}
197
198static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
199 fprintf(stream, STRINGIFY(SELF) "@%p {\n", self);
200 fmt = dc_debug_fmt_scope_begin(fmt);
201 dc_debug_fmt_print(fmt, stream, "capacity: %lu,\n", CAPACITY);
202 dc_debug_fmt_print(fmt, stream, "used: %lu,\n", self->used);
203 dc_debug_fmt_print(fmt, stream, "buffer: %p,\n", self->buffer);
204 fmt = dc_debug_fmt_scope_end(fmt);
205 dc_debug_fmt_print(fmt, stream, "}");
206}
207
208#undef USED
209#undef CAPACITY
210
212
static void debug(SELF const *self, dc_debug_fmt fmt, FILE *stream)
Definition template.h:62
static void free(SELF *self, void *ptr)
Definition template.h:56
static void * realloc(SELF *self, void *ptr, size_t size)
Definition template.h:45
static void * malloc(SELF *self, size_t size)
Definition template.h:23
static void * calloc(SELF *self, size_t count, size_t size)
Definition template.h:34
static void clear(SELF *self)
Clear the allocator, note that all data should be freed before this occurs.
Definition template.h:64
static size_t metadata_size
Definition template.h:42
static USED get_used(SELF const *self)
Definition template.h:85
#define CAPACITY
A very simple bump allocator making use of a provided fixed size buffer (e.g. statically allocated).
Definition template.h:17
char buffer[CAPACITY]
Definition template.h:34
#define USED
Definition template.h:25
#define DC_TRAIT_ALLOC(SELF)
Definition trait.h:5
static INDEX_TYPE size(SELF const *self)
Definition template.h:252
dc_debug_fmt dc_debug_fmt_scope_end(dc_debug_fmt fmt)
Definition fmt.h:39
dc_debug_fmt dc_debug_fmt_scope_begin(dc_debug_fmt fmt)
Definition fmt.h:33
static void dc_debug_fmt_print(dc_debug_fmt fmt, FILE *stream, const char *format,...)
Definition fmt.h:22
static void dc_memory_tracker_set(dc_memory_tracker_level level, dc_memory_tracker_capability cap, const volatile void *addr, size_t size)
@ DC_MEMORY_TRACKER_LVL_ALLOC
@ DC_MEMORY_TRACKER_CAP_WRITE
@ DC_MEMORY_TRACKER_CAP_NONE
@ DC_MEMORY_TRACKER_CAP_READ_WRITE
#define STRINGIFY(MACRO)
Definition namespace.h:7
#define NS(pre, post)
Definition namespace.h:4
#define DC_PANIC(...)
Definition panic.h:28
#define DC_ASSUME(expr,...)
Definition panic.h:56
#define SELF
Definition def.h:52
An allocator that prints to stdout when it allocates or frees memory.
Definition template.h:14
size_t used
Definition template.h:37
buffer * buffer
Definition template.h:38
dc_gdb_marker derive_c_staticbumpalloc
Definition template.h:39
Debug format helpers for debug printin data structures.
Definition fmt.h:10
static FILE * stream(SELF *self)
Opens a file for.
Definition template.h:107