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