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
13
14#if !defined CAPACITY
15 #if !defined DC_PLACEHOLDERS
16TEMPLATE_ERROR("No CAPACITY")
17 #endif
18 #define CAPACITY 1024
19#endif
20
21DC_STATIC_ASSERT(CAPACITY > 0, "Capacity must be larger than zero");
22
23typedef char NS(SELF, buffer)[CAPACITY];
24
25typedef struct {
27 NS(ALLOC, ref) alloc_ref;
28 char* head_ptr; // Points to end of used buffer
29 char* last_alloc_ptr; // Points to last allocation, or NULL if none
31} SELF;
32
33DC_INTERNAL static bool PRIV(NS(SELF, contains_ptr))(SELF const* self, void* ptr) {
34 void* buffer_start = &(*self->buffer)[0];
35 void* buffer_end = &(*self->buffer)[CAPACITY];
36 return buffer_start <= ptr && ptr < buffer_end;
37}
38
39DC_PUBLIC static SELF NS(SELF, new)(NS(SELF, buffer) * buffer, NS(ALLOC, ref) alloc_ref) {
40 SELF self = {
41 .buffer = buffer,
42 .alloc_ref = alloc_ref,
43 .head_ptr = &(*buffer)[0],
44 .last_alloc_ptr = NULL,
45 .derive_c_hybridstaticalloc = {},
46 };
47
48 // JUSTIFY: no capabilities on init
49 // - Protect access by users outside of malloc/calloc
51 CAPACITY);
52
53 return self;
54}
55
56DC_PUBLIC static void* PRIV(NS(SELF, static_allocate_zeroed))(SELF* self, size_t size) {
57 DC_ASSUME(self);
58 DC_ASSERT(size > 0);
59
60 char* buffer_end = &(*self->buffer)[CAPACITY];
61 if (self->head_ptr + size > buffer_end) {
62 return NULL;
63 }
64
65 char* allocation_ptr = self->head_ptr;
66
67 // Set permissions before zeroing (buffer might be poisoned)
69 size);
70
71 // Zero the allocation
72 memset(allocation_ptr, 0, size);
73
74 self->last_alloc_ptr = allocation_ptr;
75 self->head_ptr += size;
76
77 // Now set to read-write
79 allocation_ptr, size);
80
81 return allocation_ptr;
82}
83
84DC_PUBLIC static void* NS(SELF, allocate_zeroed)(SELF* self, size_t size) {
85 void* allocation_ptr = PRIV(NS(SELF, static_allocate_zeroed))(self, size);
86 if (allocation_ptr == NULL) {
87 return NS(ALLOC, allocate_zeroed)(self->alloc_ref, size);
88 }
89 return allocation_ptr;
90}
91
92DC_PUBLIC static void* PRIV(NS(SELF, static_allocate_uninit))(SELF* self, size_t size) {
93 DC_ASSUME(self);
94 DC_ASSERT(size > 0);
95
96 char* buffer_end = &(*self->buffer)[CAPACITY];
97 if (self->head_ptr + size > buffer_end) {
98 return NULL;
99 }
100
101 char* allocation_ptr = self->head_ptr;
102
103 self->last_alloc_ptr = allocation_ptr;
104 self->head_ptr += size;
105
107 size);
108
109 return allocation_ptr;
110}
111
112DC_PUBLIC static void* NS(SELF, allocate_uninit)(SELF* self, size_t size) {
113 DC_ASSUME(self);
114 DC_ASSERT(size > 0, "Cannot allocate zero sized");
115
116 void* statically_allocated = PRIV(NS(SELF, static_allocate_uninit))(self, size);
117
118 if (statically_allocated == NULL) {
119 return NS(ALLOC, allocate_uninit)(self->alloc_ref, size);
120 }
121
122 return statically_allocated;
123}
124
125DC_PUBLIC static void PRIV(NS(SELF, static_deallocate))(SELF* self, void* ptr, size_t size) {
126 DC_ASSUME(self);
127 DC_ASSUME(ptr);
128
129 char* byte_ptr = (char*)ptr;
130
131 // If this is the last allocation, we can reclaim the space
132 if (byte_ptr == self->last_alloc_ptr) {
134 self->head_ptr = byte_ptr;
135 self->last_alloc_ptr = NULL;
136 } else {
137 // Not the last allocation, just mark as inaccessible
139 }
140}
141
142DC_PUBLIC static void NS(SELF, deallocate)(SELF* self, void* ptr, size_t size) {
143 DC_ASSUME(self);
144 DC_ASSUME(ptr);
145
146 if (PRIV(NS(SELF, contains_ptr))(self, ptr)) {
147 PRIV(NS(SELF, static_deallocate))(self, ptr, size);
148 } else {
149 NS(ALLOC, deallocate)(self->alloc_ref, ptr, size);
150 }
151}
152
153DC_PUBLIC static void* PRIV(NS(SELF, static_reallocate))(SELF* self, void* ptr, size_t old_size,
154 size_t new_size) {
155 char* byte_ptr = (char*)ptr;
156
157 if (new_size == old_size) {
158 return ptr;
159 }
160
161 // If this is the last allocation, we can grow/shrink in place
162 if (byte_ptr == self->last_alloc_ptr) {
163 char* buffer_end = &(*self->buffer)[CAPACITY];
164
165 if (new_size > old_size) {
166 // Growing
167 size_t increase = new_size - old_size;
168 if (self->head_ptr + increase <= buffer_end) {
170 self->head_ptr, increase);
171 self->head_ptr += increase;
172 return ptr;
173 }
174 } else {
175 // Shrinking
176 size_t decrease = old_size - new_size;
178 byte_ptr + new_size, decrease);
179 self->head_ptr -= decrease;
180 return ptr;
181 }
182 } else if (new_size < old_size) {
183 // Not the last allocation, but shrinking - just mark extra space as inaccessible
185 byte_ptr + new_size, old_size - new_size);
186 return ptr;
187 }
188
189 // Need to allocate new buffer and copy
190 void* new_buff = NS(SELF, allocate_uninit)(self, new_size);
191 memcpy(new_buff, ptr, old_size < new_size ? old_size : new_size);
192
193 PRIV(NS(SELF, static_deallocate))(self, ptr, old_size);
194 return new_buff;
195}
196
197DC_PUBLIC static void* NS(SELF, reallocate)(SELF* self, void* ptr, size_t old_size,
198 size_t new_size) {
199 DC_ASSUME(self);
200 DC_ASSERT(new_size > 0, "Cannot allocate zero sized");
201
202 if (!ptr) {
203 return NS(SELF, allocate_uninit)(self, new_size);
204 }
205
206 if (!PRIV(NS(SELF, contains_ptr))(self, ptr)) {
207 return NS(ALLOC, reallocate)(self->alloc_ref, ptr, old_size, new_size);
208 }
209
210 return PRIV(NS(SELF, static_reallocate))(self, ptr, old_size, new_size);
211}
212
213DC_PUBLIC static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
214 DC_ASSUME(self);
215 fprintf(stream, DC_EXPAND_STRING(SELF) "@%p {\n", (void*)self);
216 fmt = dc_debug_fmt_scope_begin(fmt);
217 dc_debug_fmt_print(fmt, stream, "capacity: %zu,\n", (size_t)CAPACITY);
218 dc_debug_fmt_print(fmt, stream, "used: %zu,\n", (size_t)(self->head_ptr - &(*self->buffer)[0]));
219 dc_debug_fmt_print(fmt, stream, "buffer: %p,\n", (void*)self->buffer);
220 dc_debug_fmt_print(fmt, stream, "last_alloc: %p,\n", (void*)self->last_alloc_ptr);
221 dc_debug_fmt_print(fmt, stream, "alloc: ");
222 NS(ALLOC, debug)(NS(NS(ALLOC, ref), deref)(self->alloc_ref), fmt, stream);
223 fprintf(stream, "\n");
224 fmt = dc_debug_fmt_scope_end(fmt);
225 dc_debug_fmt_print(fmt, stream, "}");
226}
227
228DC_PUBLIC static void NS(SELF, reset)(SELF* self) {
229 DC_ASSUME(self);
230
231 size_t used = (size_t)(self->head_ptr - &(*self->buffer)[0]);
232 if (used > 0) {
234 &(*self->buffer)[0], used);
235 }
236
237 self->head_ptr = &(*self->buffer)[0];
238 self->last_alloc_ptr = NULL;
239}
240
241#undef CAPACITY
242
243DC_PUBLIC static void NS(SELF, delete)(SELF* self) {
245 &self->buffer[0], sizeof(NS(SELF, buffer)));
246 DC_ASSUME(self);
247}
248
250
252
static DC_PUBLIC void deallocate(SELF *self, void *ptr, size_t size)
Definition template.h:127
static DC_PUBLIC void debug(SELF const *self, dc_debug_fmt fmt, FILE *stream)
Definition template.h:212
static DC_PUBLIC void * allocate_zeroed(SELF *self, size_t size)
Definition template.h:117
static DC_PUBLIC void reset(SELF *self)
Definition template.h:186
static DC_PUBLIC void * allocate_uninit(SELF *self, size_t size)
Definition template.h:92
static DC_PUBLIC void * reallocate(SELF *self, void *ptr, size_t old_size, size_t new_size)
Definition template.h:137
static DC_PUBLIC void PRIV static_deallocate(SELF *self, void *ptr, size_t size)
Definition template.h:125
#define CAPACITY
A hybrid of a bump allocator on a statically allocated buffer, and any other allocator.
Definition template.h:18
static DC_PUBLIC void *PRIV static_reallocate(SELF *self, void *ptr, size_t old_size, size_t new_size)
Definition template.h:153
static DC_PUBLIC void *PRIV static_allocate_zeroed(SELF *self, size_t size)
Definition template.h:56
char buffer[CAPACITY]
Definition template.h:23
static DC_INTERNAL bool PRIV contains_ptr(SELF const *self, void *ptr)
Definition template.h:33
static DC_PUBLIC void *PRIV static_allocate_uninit(SELF *self, size_t size)
Definition template.h:92
#define ALLOC
Definition template.h:31
#define DC_TRAIT_ALLOC(SELF)
Definition trait.h:18
static DC_PUBLIC size_t size(SELF const *self)
Definition template.h:252
#define TEMPLATE_ERROR(...)
With the user provided name, even in nested templates.
Definition def.h:56
#define SELF
Definition def.h:52
static DC_PUBLIC void dc_debug_fmt_print(dc_debug_fmt fmt, FILE *stream, const char *format,...)
Definition fmt.h:32
static DC_PUBLIC dc_debug_fmt dc_debug_fmt_scope_end(dc_debug_fmt fmt)
Definition fmt.h:57
static DC_PUBLIC dc_debug_fmt dc_debug_fmt_scope_begin(dc_debug_fmt fmt)
Definition fmt.h:50
static DC_PUBLIC 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 DC_PUBLIC
Definition namespace.h:25
#define NS(pre, post)
Definition namespace.h:14
#define DC_EXPAND_STRING(NAME)
Definition namespace.h:6
#define PRIV(name)
Definition namespace.h:20
#define DC_INTERNAL
Definition namespace.h:30
#define DC_ASSERT(expr,...)
Definition panic.h:37
#define DC_STATIC_ASSERT
Definition panic.h:22
#define DC_ASSUME(expr,...)
Definition panic.h:57
#define DC_TRAIT_REFERENCABLE_BY_PTR(SELF)
Definition ref.h:19
An allocator that prints to stdout when it allocates or frees memory.
Definition template.h:45
char * last_alloc_ptr
Definition template.h:29
buffer * buffer
Definition template.h:26
dc_gdb_marker derive_c_hybridstaticalloc
Definition template.h:30
ref alloc_ref
Definition template.h:49
char * head_ptr
Definition template.h:28
Debug format helpers for debug printin data structures.
Definition fmt.h:11
static DC_PUBLIC FILE * stream(SELF *self)
Definition template.h:108