You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
11 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  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. #include <assert.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "libcork/core/allocator.h"
  14. #include "libcork/core/attributes.h"
  15. #include "libcork/core/error.h"
  16. #include "libcork/core/types.h"
  17. #include "libcork/os/process.h"
  18. /*-----------------------------------------------------------------------
  19. * Allocator interface
  20. */
  21. struct cork_alloc_priv {
  22. struct cork_alloc public;
  23. struct cork_alloc_priv *next;
  24. };
  25. static void *
  26. cork_alloc__default_calloc(const struct cork_alloc *alloc,
  27. size_t count, size_t size)
  28. {
  29. void *result = cork_alloc_xcalloc(alloc, count, size);
  30. if (CORK_UNLIKELY(result == NULL)) {
  31. abort();
  32. }
  33. return result;
  34. }
  35. static void *
  36. cork_alloc__default_malloc(const struct cork_alloc *alloc, size_t size)
  37. {
  38. void *result = cork_alloc_xmalloc(alloc, size);
  39. if (CORK_UNLIKELY(result == NULL)) {
  40. abort();
  41. }
  42. return result;
  43. }
  44. static void *
  45. cork_alloc__default_realloc(const struct cork_alloc *alloc, void *ptr,
  46. size_t old_size, size_t new_size)
  47. {
  48. void *result = cork_alloc_xrealloc(alloc, ptr, old_size, new_size);
  49. if (CORK_UNLIKELY(result == NULL)) {
  50. abort();
  51. }
  52. return result;
  53. }
  54. static void *
  55. cork_alloc__default_xcalloc(const struct cork_alloc *alloc,
  56. size_t count, size_t size)
  57. {
  58. void *result;
  59. assert(count < (SIZE_MAX / size));
  60. result = cork_alloc_xmalloc(alloc, count * size);
  61. if (result != NULL) {
  62. memset(result, 0, count * size);
  63. }
  64. return result;
  65. }
  66. static void *
  67. cork_alloc__default_xmalloc(const struct cork_alloc *alloc, size_t size)
  68. {
  69. cork_abort("%s isn't defined", "cork_alloc:xmalloc");
  70. }
  71. static void *
  72. cork_alloc__default_xrealloc(const struct cork_alloc *alloc, void *ptr,
  73. size_t old_size, size_t new_size)
  74. {
  75. void *result = cork_alloc_xmalloc(alloc, new_size);
  76. if (CORK_LIKELY(result != NULL) && ptr != NULL) {
  77. size_t min_size = (new_size < old_size)? new_size: old_size;
  78. memcpy(result, ptr, min_size);
  79. cork_alloc_free(alloc, ptr, old_size);
  80. }
  81. return result;
  82. }
  83. static void
  84. cork_alloc__default_free(const struct cork_alloc *alloc, void *ptr, size_t size)
  85. {
  86. cork_abort("%s isn't defined", "cork_alloc:free");
  87. }
  88. static bool cleanup_registered = false;
  89. static struct cork_alloc_priv *all_allocs = NULL;
  90. static void
  91. cork_alloc_free_alloc(struct cork_alloc_priv *alloc)
  92. {
  93. cork_free_user_data(&alloc->public);
  94. cork_alloc_delete(alloc->public.parent, struct cork_alloc_priv, alloc);
  95. }
  96. static void
  97. cork_alloc_free_all(void)
  98. {
  99. struct cork_alloc_priv *curr;
  100. struct cork_alloc_priv *next;
  101. for (curr = all_allocs; curr != NULL; curr = next) {
  102. next = curr->next;
  103. cork_alloc_free_alloc(curr);
  104. }
  105. }
  106. static void
  107. cork_alloc_register_cleanup(void)
  108. {
  109. if (CORK_UNLIKELY(!cleanup_registered)) {
  110. /* We don't use cork_cleanup because that requires the allocators to
  111. * have already been set up! (atexit calls its functions in reverse
  112. * order, and this one will be registered before cork_cleanup's, which
  113. * makes it safe for cork_cleanup functions to still use the allocator,
  114. * since the allocator atexit function will be called last.) */
  115. atexit(cork_alloc_free_all);
  116. cleanup_registered = true;
  117. }
  118. }
  119. struct cork_alloc *
  120. cork_alloc_new_alloc(const struct cork_alloc *parent)
  121. {
  122. struct cork_alloc_priv *alloc =
  123. cork_alloc_new(parent, struct cork_alloc_priv);
  124. alloc->public.parent = parent;
  125. alloc->public.user_data = NULL;
  126. alloc->public.free_user_data = NULL;
  127. alloc->public.calloc = cork_alloc__default_calloc;
  128. alloc->public.malloc = cork_alloc__default_malloc;
  129. alloc->public.realloc = cork_alloc__default_realloc;
  130. alloc->public.xcalloc = cork_alloc__default_xcalloc;
  131. alloc->public.xmalloc = cork_alloc__default_xmalloc;
  132. alloc->public.xrealloc = cork_alloc__default_xrealloc;
  133. alloc->public.free = cork_alloc__default_free;
  134. cork_alloc_register_cleanup();
  135. alloc->next = all_allocs;
  136. all_allocs = alloc;
  137. return &alloc->public;
  138. }
  139. void
  140. cork_alloc_set_user_data(struct cork_alloc *alloc,
  141. void *user_data, cork_free_f free_user_data)
  142. {
  143. cork_free_user_data(alloc);
  144. alloc->user_data = user_data;
  145. alloc->free_user_data = free_user_data;
  146. }
  147. void
  148. cork_alloc_set_calloc(struct cork_alloc *alloc, cork_alloc_calloc_f calloc)
  149. {
  150. alloc->calloc = calloc;
  151. }
  152. void
  153. cork_alloc_set_malloc(struct cork_alloc *alloc, cork_alloc_malloc_f malloc)
  154. {
  155. alloc->malloc = malloc;
  156. }
  157. void
  158. cork_alloc_set_realloc(struct cork_alloc *alloc, cork_alloc_realloc_f realloc)
  159. {
  160. alloc->realloc = realloc;
  161. }
  162. void
  163. cork_alloc_set_xcalloc(struct cork_alloc *alloc, cork_alloc_calloc_f xcalloc)
  164. {
  165. alloc->xcalloc = xcalloc;
  166. }
  167. void
  168. cork_alloc_set_xmalloc(struct cork_alloc *alloc, cork_alloc_malloc_f xmalloc)
  169. {
  170. alloc->xmalloc = xmalloc;
  171. }
  172. void
  173. cork_alloc_set_xrealloc(struct cork_alloc *alloc,
  174. cork_alloc_realloc_f xrealloc)
  175. {
  176. alloc->xrealloc = xrealloc;
  177. }
  178. void
  179. cork_alloc_set_free(struct cork_alloc *alloc, cork_alloc_free_f free)
  180. {
  181. alloc->free = free;
  182. }
  183. /*-----------------------------------------------------------------------
  184. * Allocating strings
  185. */
  186. static inline const char *
  187. strndup_internal(const struct cork_alloc *alloc,
  188. const char *str, size_t len)
  189. {
  190. char *dest;
  191. size_t allocated_size = len + sizeof(size_t) + 1;
  192. size_t *new_str = cork_alloc_malloc(alloc, allocated_size);
  193. *new_str = allocated_size;
  194. dest = (char *) (void *) (new_str + 1);
  195. memcpy(dest, str, len);
  196. dest[len] = '\0';
  197. return dest;
  198. }
  199. const char *
  200. cork_alloc_strdup(const struct cork_alloc *alloc, const char *str)
  201. {
  202. return strndup_internal(alloc, str, strlen(str));
  203. }
  204. const char *
  205. cork_alloc_strndup(const struct cork_alloc *alloc,
  206. const char *str, size_t size)
  207. {
  208. return strndup_internal(alloc, str, size);
  209. }
  210. static inline const char *
  211. xstrndup_internal(const struct cork_alloc *alloc,
  212. const char *str, size_t len)
  213. {
  214. size_t allocated_size = len + sizeof(size_t) + 1;
  215. size_t *new_str = cork_alloc_xmalloc(alloc, allocated_size);
  216. if (CORK_UNLIKELY(new_str == NULL)) {
  217. return NULL;
  218. } else {
  219. char *dest;
  220. *new_str = allocated_size;
  221. dest = (char *) (void *) (new_str + 1);
  222. memcpy(dest, str, len);
  223. dest[len] = '\0';
  224. return dest;
  225. }
  226. }
  227. const char *
  228. cork_alloc_xstrdup(const struct cork_alloc *alloc, const char *str)
  229. {
  230. return xstrndup_internal(alloc, str, strlen(str));
  231. }
  232. const char *
  233. cork_alloc_xstrndup(const struct cork_alloc *alloc,
  234. const char *str, size_t size)
  235. {
  236. return xstrndup_internal(alloc, str, size);
  237. }
  238. void
  239. cork_alloc_strfree(const struct cork_alloc *alloc, const char *str)
  240. {
  241. size_t *base = ((size_t *) str) - 1;
  242. cork_alloc_free(alloc, base, *base);
  243. }
  244. /*-----------------------------------------------------------------------
  245. * stdlib allocator
  246. */
  247. static void *
  248. cork_stdlib_alloc__calloc(const struct cork_alloc *alloc,
  249. size_t count, size_t size)
  250. {
  251. void *result = calloc(count, size);
  252. if (CORK_UNLIKELY(result == NULL)) {
  253. abort();
  254. }
  255. return result;
  256. }
  257. static void *
  258. cork_stdlib_alloc__malloc(const struct cork_alloc *alloc, size_t size)
  259. {
  260. void *result = malloc(size);
  261. if (CORK_UNLIKELY(result == NULL)) {
  262. abort();
  263. }
  264. return result;
  265. }
  266. static void *
  267. cork_stdlib_alloc__realloc(const struct cork_alloc *alloc, void *ptr,
  268. size_t old_size, size_t new_size)
  269. {
  270. /* Technically we don't really need to free `ptr` if the reallocation fails,
  271. * since we'll abort the process immediately after. But my sense of
  272. * cleanliness makes me do it anyway. */
  273. #if CORK_HAVE_REALLOCF
  274. void *result = reallocf(ptr, new_size);
  275. if (result == NULL) {
  276. abort();
  277. }
  278. return result;
  279. #else
  280. void *result = realloc(ptr, new_size);
  281. if (result == NULL) {
  282. free(ptr);
  283. abort();
  284. }
  285. return result;
  286. #endif
  287. }
  288. static void *
  289. cork_stdlib_alloc__xcalloc(const struct cork_alloc *alloc,
  290. size_t count, size_t size)
  291. {
  292. return calloc(count, size);
  293. }
  294. static void *
  295. cork_stdlib_alloc__xmalloc(const struct cork_alloc *alloc, size_t size)
  296. {
  297. return malloc(size);
  298. }
  299. static void *
  300. cork_stdlib_alloc__xrealloc(const struct cork_alloc *alloc, void *ptr,
  301. size_t old_size, size_t new_size)
  302. {
  303. return realloc(ptr, new_size);
  304. }
  305. static void
  306. cork_stdlib_alloc__free(const struct cork_alloc *alloc, void *ptr, size_t size)
  307. {
  308. free(ptr);
  309. }
  310. static const struct cork_alloc default_allocator = {
  311. NULL,
  312. NULL,
  313. NULL,
  314. cork_stdlib_alloc__calloc,
  315. cork_stdlib_alloc__malloc,
  316. cork_stdlib_alloc__realloc,
  317. cork_stdlib_alloc__xcalloc,
  318. cork_stdlib_alloc__xmalloc,
  319. cork_stdlib_alloc__xrealloc,
  320. cork_stdlib_alloc__free
  321. };
  322. /*-----------------------------------------------------------------------
  323. * Customizing libcork's allocator
  324. */
  325. const struct cork_alloc *cork_allocator = &default_allocator;
  326. void
  327. cork_set_allocator(const struct cork_alloc *alloc)
  328. {
  329. cork_allocator = alloc;
  330. }
  331. /*-----------------------------------------------------------------------
  332. * Debugging allocator
  333. */
  334. static void *
  335. cork_debug_alloc__xmalloc(const struct cork_alloc *alloc, size_t size)
  336. {
  337. size_t real_size = size + sizeof(size_t);
  338. size_t *base = cork_alloc_xmalloc(alloc->parent, real_size);
  339. *base = size;
  340. return base + 1;
  341. }
  342. static void
  343. cork_debug_alloc__free(const struct cork_alloc *alloc, void *ptr,
  344. size_t expected_size)
  345. {
  346. size_t *base = ((size_t *) ptr) - 1;
  347. size_t actual_size = *base;
  348. size_t real_size = actual_size + sizeof(size_t);
  349. if (CORK_UNLIKELY(actual_size != expected_size)) {
  350. cork_abort
  351. ("Incorrect size when freeing pointer (got %zu, expected %zu)",
  352. expected_size, actual_size);
  353. }
  354. cork_alloc_free(alloc->parent, base, real_size);
  355. }
  356. struct cork_alloc *
  357. cork_debug_alloc_new(const struct cork_alloc *parent)
  358. {
  359. struct cork_alloc *debug = cork_alloc_new_alloc(parent);
  360. cork_alloc_set_xmalloc(debug, cork_debug_alloc__xmalloc);
  361. cork_alloc_set_free(debug, cork_debug_alloc__free);
  362. return debug;
  363. }