duk_alloc_hybrid.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Example memory allocator with pool allocation for small sizes and
  3. * fallback into malloc/realloc/free for larger sizes or when the pools
  4. * are exhausted.
  5. *
  6. * Useful to reduce memory churn or work around a platform allocator
  7. * that doesn't handle a lot of small allocations efficiently.
  8. */
  9. #include "duktape.h"
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <stdint.h>
  14. /* Define to enable some debug printfs. */
  15. /* #define DUK_ALLOC_HYBRID_DEBUG */
  16. typedef struct {
  17. size_t size;
  18. int count;
  19. } pool_size_spec;
  20. static pool_size_spec pool_sizes[] = {
  21. { 32, 1024 },
  22. { 48, 2048 },
  23. { 64, 2048 },
  24. { 128, 2048 },
  25. { 256, 512 },
  26. { 1024, 64 },
  27. { 2048, 32 }
  28. };
  29. #define NUM_POOLS (sizeof(pool_sizes) / sizeof(pool_size_spec))
  30. /* This must fit into the smallest pool entry. */
  31. struct pool_free_entry;
  32. typedef struct pool_free_entry pool_free_entry;
  33. struct pool_free_entry {
  34. pool_free_entry *next;
  35. };
  36. typedef struct {
  37. pool_free_entry *free;
  38. char *alloc_start;
  39. char *alloc_end;
  40. size_t size;
  41. int count;
  42. } pool_header;
  43. typedef struct {
  44. pool_header headers[NUM_POOLS];
  45. size_t pool_max_size;
  46. char *alloc_start;
  47. char *alloc_end;
  48. } pool_state;
  49. #define ADDR_IN_STATE_ALLOC(st,p) \
  50. ((char *) (p) >= (st)->alloc_start && (char *) (p) < (st)->alloc_end)
  51. #define ADDR_IN_HEADER_ALLOC(hdr,p) \
  52. ((char *) (p) >= (hdr)->alloc_start && (char *) (p) < (hdr)->alloc_end)
  53. #ifdef DUK_ALLOC_HYBRID_DEBUG
  54. static void dump_pool_state(pool_state *st) {
  55. pool_free_entry *free;
  56. int free_len;
  57. int i;
  58. printf("=== Pool state: st=%p\n", (void *) st);
  59. for (i = 0; i < (int) NUM_POOLS; i++) {
  60. pool_header *hdr = st->headers + i;
  61. for (free = hdr->free, free_len = 0; free != NULL; free = free->next) {
  62. free_len++;
  63. }
  64. printf("[%d]: size %ld, count %ld, used %ld, free list len %ld\n",
  65. i, (long) hdr->size, (long) hdr->count,
  66. (long) (hdr->count - free_len),
  67. (long) free_len);
  68. }
  69. }
  70. #else
  71. static void dump_pool_state(pool_state *st) {
  72. (void) st;
  73. }
  74. #endif
  75. void *duk_alloc_hybrid_init(void) {
  76. pool_state *st;
  77. size_t total_size, max_size;
  78. int i, j;
  79. char *p;
  80. st = (pool_state *) malloc(sizeof(pool_state));
  81. if (!st) {
  82. return NULL;
  83. }
  84. memset((void *) st, 0, sizeof(pool_state));
  85. st->alloc_start = NULL;
  86. st->alloc_end = NULL;
  87. for (i = 0, total_size = 0, max_size = 0; i < (int) NUM_POOLS; i++) {
  88. #ifdef DUK_ALLOC_HYBRID_DEBUG
  89. printf("Pool %d: size %ld, count %ld\n", i, (long) pool_sizes[i].size, (long) pool_sizes[i].count);
  90. #endif
  91. total_size += pool_sizes[i].size * pool_sizes[i].count;
  92. if (pool_sizes[i].size > max_size) {
  93. max_size = pool_sizes[i].size;
  94. }
  95. }
  96. #ifdef DUK_ALLOC_HYBRID_DEBUG
  97. printf("Total size %ld, max pool size %ld\n", (long) total_size, (long) max_size);
  98. #endif
  99. st->alloc_start = (char *) malloc(total_size);
  100. if (!st->alloc_start) {
  101. free(st);
  102. return NULL;
  103. }
  104. st->alloc_end = st->alloc_start + total_size;
  105. st->pool_max_size = max_size;
  106. memset((void *) st->alloc_start, 0, total_size);
  107. for (i = 0, p = st->alloc_start; i < (int) NUM_POOLS; i++) {
  108. pool_header *hdr = st->headers + i;
  109. hdr->alloc_start = p;
  110. hdr->alloc_end = p + pool_sizes[i].size * pool_sizes[i].count;
  111. hdr->free = (pool_free_entry *) (void *) p;
  112. hdr->size = pool_sizes[i].size;
  113. hdr->count = pool_sizes[i].count;
  114. for (j = 0; j < pool_sizes[i].count; j++) {
  115. pool_free_entry *ent = (pool_free_entry *) (void *) p;
  116. if (j == pool_sizes[i].count - 1) {
  117. ent->next = (pool_free_entry *) NULL;
  118. } else {
  119. ent->next = (pool_free_entry *) (void *) (p + pool_sizes[i].size);
  120. }
  121. p += pool_sizes[i].size;
  122. }
  123. }
  124. dump_pool_state(st);
  125. /* Use 'st' as udata. */
  126. return (void *) st;
  127. }
  128. void *duk_alloc_hybrid(void *udata, duk_size_t size) {
  129. pool_state *st = (pool_state *) udata;
  130. int i;
  131. void *new_ptr;
  132. #if 0
  133. dump_pool_state(st);
  134. #endif
  135. if (size == 0) {
  136. return NULL;
  137. }
  138. if (size > st->pool_max_size) {
  139. #ifdef DUK_ALLOC_HYBRID_DEBUG
  140. printf("alloc fallback: %ld\n", (long) size);
  141. #endif
  142. return malloc(size);
  143. }
  144. for (i = 0; i < (int) NUM_POOLS; i++) {
  145. pool_header *hdr = st->headers + i;
  146. if (hdr->size < size) {
  147. continue;
  148. }
  149. if (hdr->free) {
  150. #if 0
  151. printf("alloc from pool: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
  152. #endif
  153. new_ptr = (void *) hdr->free;
  154. hdr->free = hdr->free->next;
  155. return new_ptr;
  156. } else {
  157. #ifdef DUK_ALLOC_HYBRID_DEBUG
  158. printf("alloc out of pool entries: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
  159. #endif
  160. break;
  161. }
  162. }
  163. #ifdef DUK_ALLOC_HYBRID_DEBUG
  164. printf("alloc fallback (out of pool): %ld\n", (long) size);
  165. #endif
  166. return malloc(size);
  167. }
  168. void *duk_realloc_hybrid(void *udata, void *ptr, duk_size_t size) {
  169. pool_state *st = (pool_state *) udata;
  170. void *new_ptr;
  171. int i;
  172. #if 0
  173. dump_pool_state(st);
  174. #endif
  175. if (ADDR_IN_STATE_ALLOC(st, ptr)) {
  176. /* 'ptr' cannot be NULL. */
  177. for (i = 0; i < (int) NUM_POOLS; i++) {
  178. pool_header *hdr = st->headers + i;
  179. if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
  180. if (size <= hdr->size) {
  181. /* Still fits, no shrink support. */
  182. #if 0
  183. printf("realloc original from pool: still fits, size %ld, pool size %ld\n",
  184. (long) size, (long) hdr->size);
  185. #endif
  186. return ptr;
  187. }
  188. new_ptr = duk_alloc_hybrid(udata, size);
  189. if (!new_ptr) {
  190. #ifdef DUK_ALLOC_HYBRID_DEBUG
  191. printf("realloc original from pool: needed larger size, failed to alloc\n");
  192. #endif
  193. return NULL;
  194. }
  195. memcpy(new_ptr, ptr, hdr->size);
  196. ((pool_free_entry *) ptr)->next = hdr->free;
  197. hdr->free = (pool_free_entry *) ptr;
  198. #if 0
  199. printf("realloc original from pool: size %ld, pool size %ld\n", (long) size, (long) hdr->size);
  200. #endif
  201. return new_ptr;
  202. }
  203. }
  204. #ifdef DUK_ALLOC_HYBRID_DEBUG
  205. printf("NEVER HERE\n");
  206. #endif
  207. return NULL;
  208. } else if (ptr != NULL) {
  209. if (size == 0) {
  210. free(ptr);
  211. return NULL;
  212. } else {
  213. #ifdef DUK_ALLOC_HYBRID_DEBUG
  214. printf("realloc fallback: size %ld\n", (long) size);
  215. #endif
  216. return realloc(ptr, size);
  217. }
  218. } else {
  219. #if 0
  220. printf("realloc NULL ptr, call alloc: %ld\n", (long) size);
  221. #endif
  222. return duk_alloc_hybrid(udata, size);
  223. }
  224. }
  225. void duk_free_hybrid(void *udata, void *ptr) {
  226. pool_state *st = (pool_state *) udata;
  227. int i;
  228. #if 0
  229. dump_pool_state(st);
  230. #endif
  231. if (!ADDR_IN_STATE_ALLOC(st, ptr)) {
  232. if (ptr == NULL) {
  233. return;
  234. }
  235. #if 0
  236. printf("free out of pool: %p\n", (void *) ptr);
  237. #endif
  238. free(ptr);
  239. return;
  240. }
  241. for (i = 0; i < (int) NUM_POOLS; i++) {
  242. pool_header *hdr = st->headers + i;
  243. if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
  244. ((pool_free_entry *) ptr)->next = hdr->free;
  245. hdr->free = (pool_free_entry *) ptr;
  246. #if 0
  247. printf("free from pool: %p\n", ptr);
  248. #endif
  249. return;
  250. }
  251. }
  252. #ifdef DUK_ALLOC_HYBRID_DEBUG
  253. printf("NEVER HERE\n");
  254. #endif
  255. }