1 /* -*- coding: utf-8 -*-
2 * ----------------------------------------------------------------------
3 * Copyright © 2011-2014, RedJack, LLC.
4 * All rights reserved.
5 *
6 * Please see the COPYING file in this distribution for license details.
7 * ----------------------------------------------------------------------
8 */
9
10 #ifndef LIBCORK_CORE_ALLOCATOR_H
11 #define LIBCORK_CORE_ALLOCATOR_H
12
13 #include <assert.h>
14 #include <stdlib.h>
15
16 #include <libcork/core/api.h>
17 #include <libcork/core/attributes.h>
18 #include <libcork/core/callbacks.h>
19 #include <libcork/core/error.h>
20 #include <libcork/core/types.h>
21
22
23 /*-----------------------------------------------------------------------
24 * Allocator interface
25 */
26
27 struct cork_alloc;
28
29 typedef void *
30 (*cork_alloc_calloc_f)(const struct cork_alloc *alloc,
31 size_t count, size_t size);
32
33 typedef void *
34 (*cork_alloc_malloc_f)(const struct cork_alloc *alloc, size_t size);
35
36 /* Must not free `ptr` if allocation fails. */
37 typedef void *
38 (*cork_alloc_realloc_f)(const struct cork_alloc *alloc, void *ptr,
39 size_t old_size, size_t new_size);
40
41 typedef void
42 (*cork_alloc_free_f)(const struct cork_alloc *alloc, void *ptr, size_t size);
43
44 struct cork_alloc {
45 const struct cork_alloc *parent;
46 void *user_data;
47 cork_free_f free_user_data;
48 cork_alloc_calloc_f calloc;
49 cork_alloc_malloc_f malloc;
50 cork_alloc_realloc_f realloc;
51 cork_alloc_calloc_f xcalloc;
52 cork_alloc_malloc_f xmalloc;
53 cork_alloc_realloc_f xrealloc;
54 cork_alloc_free_f free;
55 };
56
57 /* NOT thread-safe; must be called before most other libcork functions.
58 * Allocator will automatically be freed at process exit. */
59 CORK_API struct cork_alloc *
60 cork_alloc_new_alloc(const struct cork_alloc *parent);
61
62
63 CORK_API void
64 cork_alloc_set_user_data(struct cork_alloc *alloc,
65 void *user_data, cork_free_f free_user_data);
66
67 /* These variants must always return a valid pointer. If allocation fails, they
68 * should abort the process or transfer control in some other way to an error
69 * handler or cleanup routine.
70 *
71 * If you only provide implementations of the `x` variants, we'll provide
72 * default implementations of these that abort the process if a memory
73 * allocation fails. */
74
75 CORK_API void
76 cork_alloc_set_calloc(struct cork_alloc *alloc, cork_alloc_calloc_f calloc);
77
78 CORK_API void
79 cork_alloc_set_malloc(struct cork_alloc *alloc, cork_alloc_malloc_f malloc);
80
81 CORK_API void
82 cork_alloc_set_realloc(struct cork_alloc *alloc, cork_alloc_realloc_f realloc);
83
84 /* These variants can return a NULL pointer if allocation fails. */
85
86 CORK_API void
87 cork_alloc_set_xcalloc(struct cork_alloc *alloc, cork_alloc_calloc_f xcalloc);
88
89 CORK_API void
90 cork_alloc_set_xmalloc(struct cork_alloc *alloc, cork_alloc_malloc_f xmalloc);
91
92 CORK_API void
93 cork_alloc_set_xrealloc(struct cork_alloc *alloc,
94 cork_alloc_realloc_f xrealloc);
95
96
97 CORK_API void
98 cork_alloc_set_free(struct cork_alloc *alloc, cork_alloc_free_f free);
99
100
101 /* Low-level use of an allocator. */
102
103 CORK_ATTR_MALLOC
104 CORK_ATTR_UNUSED
105 static void *
cork_alloc_calloc(const struct cork_alloc * alloc,size_t count,size_t size)106 cork_alloc_calloc(const struct cork_alloc *alloc, size_t count, size_t size)
107 {
108 return alloc->calloc(alloc, count, size);
109 }
110
111 CORK_ATTR_MALLOC
112 CORK_ATTR_UNUSED
113 static void *
cork_alloc_malloc(const struct cork_alloc * alloc,size_t size)114 cork_alloc_malloc(const struct cork_alloc *alloc, size_t size)
115 {
116 return alloc->malloc(alloc, size);
117 }
118
119 CORK_ATTR_MALLOC
120 CORK_ATTR_UNUSED
121 static void *
cork_alloc_realloc(const struct cork_alloc * alloc,void * ptr,size_t old_size,size_t new_size)122 cork_alloc_realloc(const struct cork_alloc *alloc, void *ptr,
123 size_t old_size, size_t new_size)
124 {
125 return alloc->realloc(alloc, ptr, old_size, new_size);
126 }
127
128 CORK_ATTR_MALLOC
129 CORK_ATTR_UNUSED
130 static void *
cork_alloc_xcalloc(const struct cork_alloc * alloc,size_t count,size_t size)131 cork_alloc_xcalloc(const struct cork_alloc *alloc, size_t count, size_t size)
132 {
133 return alloc->xcalloc(alloc, count, size);
134 }
135
136 CORK_ATTR_MALLOC
137 CORK_ATTR_UNUSED
138 static void *
cork_alloc_xmalloc(const struct cork_alloc * alloc,size_t size)139 cork_alloc_xmalloc(const struct cork_alloc *alloc, size_t size)
140 {
141 return alloc->xmalloc(alloc, size);
142 }
143
144 CORK_ATTR_MALLOC
145 CORK_ATTR_UNUSED
146 static void *
cork_alloc_xrealloc(const struct cork_alloc * alloc,void * ptr,size_t old_size,size_t new_size)147 cork_alloc_xrealloc(const struct cork_alloc *alloc, void *ptr,
148 size_t old_size, size_t new_size)
149 {
150 return alloc->xrealloc(alloc, ptr, old_size, new_size);
151 }
152
153 CORK_ATTR_MALLOC
154 CORK_ATTR_UNUSED
155 static void *
cork_alloc_xreallocf(const struct cork_alloc * alloc,void * ptr,size_t old_size,size_t new_size)156 cork_alloc_xreallocf(const struct cork_alloc *alloc, void *ptr,
157 size_t old_size, size_t new_size)
158 {
159 void *result = alloc->xrealloc(alloc, ptr, old_size, new_size);
160 if (result == NULL) {
161 alloc->free(alloc, ptr, old_size);
162 return NULL;
163 } else {
164 return result;
165 }
166 }
167
168 CORK_ATTR_UNUSED
169 static void
cork_alloc_free(const struct cork_alloc * alloc,void * ptr,size_t size)170 cork_alloc_free(const struct cork_alloc *alloc, void *ptr, size_t size)
171 {
172 return alloc->free(alloc, ptr, size);
173 }
174
175 CORK_ATTR_UNUSED
176 static void
cork_alloc_cfree(const struct cork_alloc * alloc,void * ptr,size_t count,size_t size)177 cork_alloc_cfree(const struct cork_alloc *alloc, void *ptr,
178 size_t count, size_t size)
179 {
180 assert(count < (SIZE_MAX / size));
181 return alloc->free(alloc, ptr, count * size);
182 }
183
184 #define cork_alloc_new(alloc, type) \
185 cork_alloc_malloc((alloc), sizeof(type))
186 #define cork_alloc_xnew(alloc, type) \
187 cork_alloc_xmalloc((alloc), sizeof(type))
188 #define cork_alloc_delete(alloc, type, ptr) \
189 cork_alloc_free((alloc), (ptr), sizeof(type))
190
191 /* string-related helper functions */
192
193 CORK_ATTR_MALLOC
194 CORK_API const char *
195 cork_alloc_strdup(const struct cork_alloc *alloc, const char *str);
196
197 CORK_ATTR_MALLOC
198 CORK_API const char *
199 cork_alloc_strndup(const struct cork_alloc *alloc,
200 const char *str, size_t size);
201
202 CORK_ATTR_MALLOC
203 CORK_API const char *
204 cork_alloc_xstrdup(const struct cork_alloc *alloc, const char *str);
205
206 CORK_ATTR_MALLOC
207 CORK_API const char *
208 cork_alloc_xstrndup(const struct cork_alloc *alloc,
209 const char *str, size_t size);
210
211 CORK_API void
212 cork_alloc_strfree(const struct cork_alloc *alloc, const char *str);
213
214
215 /*-----------------------------------------------------------------------
216 * Using the allocator interface
217 */
218
219 /* All of the functions that you use to actually allocate memory assume that
220 * cork_current_allocator() returns the allocator instance that should be used.
221 * Your easiest approach is to do nothing special; in that case, all of the
222 * libcork memory allocation functions will transparently use the standard
223 * malloc/free family of functions.
224 *
225 * If you're writing a library, and want to allow your library clients to
226 * provide a separate custom memory allocator then the one they can already
227 * override for libcork itself, you should declare a pair of functions for
228 * getting and setting your library's current allocator (like libcork itself
229 * does), and (only when compiling the source of your library) define
230 * `cork_current_allocator` as a macro that aliases the getter function. That
231 * will cause the libcork memory allocation functions to use whichever allocator
232 * your library user has provided.
233 *
234 * If you're writing an application, and want to provide a single allocator that
235 * all libcork-using libraries will pick up, just call cork_set_allocator before
236 * calling any other library functions. Other libraries will use this as a
237 * default and everything that uses libcork's memory allocation functions will
238 * use your custom allocator. */
239
240
241 /* libcork's current allocator */
242
243 extern const struct cork_alloc *cork_allocator;
244
245 /* We take control and will free when the process exits. This is *NOT*
246 * thread-safe; it's only safe to call before you've called *ANY* other libcork
247 * function (or any function from any other library that uses libcork). You can
248 * only call this at most once. */
249 CORK_API void
250 cork_set_allocator(const struct cork_alloc *alloc);
251
252
253 /* The current allocator of whichever library is being compiled. */
254
255 #if !defined(cork_current_allocator)
256 #define cork_current_allocator() (cork_allocator)
257 #endif
258
259
260 /* using an allocator */
261
262 CORK_ATTR_MALLOC
263 CORK_ATTR_UNUSED
264 static void *
cork_calloc(size_t count,size_t size)265 cork_calloc(size_t count, size_t size)
266 {
267 const struct cork_alloc *alloc = cork_current_allocator();
268 return cork_alloc_calloc(alloc, count, size);
269 }
270
271 CORK_ATTR_MALLOC
272 CORK_ATTR_UNUSED
273 static void *
cork_malloc(size_t size)274 cork_malloc(size_t size)
275 {
276 const struct cork_alloc *alloc = cork_current_allocator();
277 return cork_alloc_malloc(alloc, size);
278 }
279
280 CORK_ATTR_MALLOC
281 CORK_ATTR_UNUSED
282 static void *
cork_realloc(void * ptr,size_t old_size,size_t new_size)283 cork_realloc(void *ptr, size_t old_size, size_t new_size)
284 {
285 const struct cork_alloc *alloc = cork_current_allocator();
286 return cork_alloc_realloc(alloc, ptr, old_size, new_size);
287 }
288
289 CORK_ATTR_MALLOC
290 CORK_ATTR_UNUSED
291 static void *
cork_xcalloc(size_t count,size_t size)292 cork_xcalloc(size_t count, size_t size)
293 {
294 const struct cork_alloc *alloc = cork_current_allocator();
295 return cork_alloc_xcalloc(alloc, count, size);
296 }
297
298 CORK_ATTR_MALLOC
299 CORK_ATTR_UNUSED
300 static void *
cork_xmalloc(size_t size)301 cork_xmalloc(size_t size)
302 {
303 const struct cork_alloc *alloc = cork_current_allocator();
304 return cork_alloc_xmalloc(alloc, size);
305 }
306
307 CORK_ATTR_MALLOC
308 CORK_ATTR_UNUSED
309 static void *
cork_xrealloc(void * ptr,size_t old_size,size_t new_size)310 cork_xrealloc(void *ptr, size_t old_size, size_t new_size)
311 {
312 const struct cork_alloc *alloc = cork_current_allocator();
313 return cork_alloc_xrealloc(alloc, ptr, old_size, new_size);
314 }
315
316 CORK_ATTR_MALLOC
317 CORK_ATTR_UNUSED
318 static void *
cork_xreallocf(void * ptr,size_t old_size,size_t new_size)319 cork_xreallocf(void *ptr, size_t old_size, size_t new_size)
320 {
321 const struct cork_alloc *alloc = cork_current_allocator();
322 return cork_alloc_xreallocf(alloc, ptr, old_size, new_size);
323 }
324
325 CORK_ATTR_UNUSED
326 static void
cork_free(void * ptr,size_t size)327 cork_free(void *ptr, size_t size)
328 {
329 const struct cork_alloc *alloc = cork_current_allocator();
330 cork_alloc_free(alloc, ptr, size);
331 }
332
333 CORK_ATTR_UNUSED
334 static void
cork_cfree(void * ptr,size_t count,size_t size)335 cork_cfree(void *ptr, size_t count, size_t size)
336 {
337 const struct cork_alloc *alloc = cork_current_allocator();
338 cork_alloc_cfree(alloc, ptr, count, size);
339 }
340
341 #define cork_new(type) cork_malloc(sizeof(type))
342 #define cork_xnew(type) cork_xmalloc(sizeof(type))
343 #define cork_delete(type, ptr) cork_free((ptr), sizeof(type))
344
345
346 /* string-related helper functions */
347
348 CORK_ATTR_MALLOC
349 CORK_ATTR_UNUSED
350 static const char *
cork_strdup(const char * str)351 cork_strdup(const char *str)
352 {
353 const struct cork_alloc *alloc = cork_current_allocator();
354 return cork_alloc_strdup(alloc, str);
355 }
356
357 CORK_ATTR_MALLOC
358 CORK_ATTR_UNUSED
359 static const char *
cork_strndup(const char * str,size_t size)360 cork_strndup(const char *str, size_t size)
361 {
362 const struct cork_alloc *alloc = cork_current_allocator();
363 return cork_alloc_strndup(alloc, str, size);
364 }
365
366 CORK_ATTR_MALLOC
367 CORK_ATTR_UNUSED
368 static const char *
cork_xstrdup(const char * str)369 cork_xstrdup(const char *str)
370 {
371 const struct cork_alloc *alloc = cork_current_allocator();
372 return cork_alloc_xstrdup(alloc, str);
373 }
374
375 CORK_ATTR_MALLOC
376 CORK_ATTR_UNUSED
377 static const char *
cork_xstrndup(const char * str,size_t size)378 cork_xstrndup(const char *str, size_t size)
379 {
380 const struct cork_alloc *alloc = cork_current_allocator();
381 return cork_alloc_xstrndup(alloc, str, size);
382 }
383
384 CORK_ATTR_UNUSED
385 static void
cork_strfree(const char * str)386 cork_strfree(const char *str)
387 {
388 const struct cork_alloc *alloc = cork_current_allocator();
389 return cork_alloc_strfree(alloc, str);
390 }
391
392
393 /*-----------------------------------------------------------------------
394 * Debugging allocator
395 */
396
397 /* An allocator that adds some additional debugging checks:
398 *
399 * - We verify that every "free" call (cork_free, cork_cfree, cork_delete,
400 * cork_realloc) is passed the "correct" size — i.e., the same size that was
401 * passed in to the correspond "new" call (cork_malloc, cork_calloc,
402 * cork_realloc, cork_new).
403 */
404
405 struct cork_alloc *
406 cork_debug_alloc_new(const struct cork_alloc *parent);
407
408
409 #endif /* LIBCORK_CORE_ALLOCATOR_H */
410