+++ /dev/null
-/*
- * Example memory allocator with pool allocation for small sizes and
- * fallback into malloc/realloc/free for larger sizes or when the pools
- * are exhausted.
- *
- * Useful to reduce memory churn or work around a platform allocator
- * that doesn't handle a lot of small allocations efficiently.
- */
-
-#include "duktape.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-
-/* Define to enable some debug printfs. */
-/* #define DUK_ALLOC_HYBRID_DEBUG */
-
-typedef struct {
- size_t size;
- int count;
-} pool_size_spec;
-
-static pool_size_spec pool_sizes[] = {
- { 32, 1024 },
- { 48, 2048 },
- { 64, 2048 },
- { 128, 2048 },
- { 256, 512 },
- { 1024, 64 },
- { 2048, 32 }
-};
-
-#define NUM_POOLS (sizeof(pool_sizes) / sizeof(pool_size_spec))
-
-/* This must fit into the smallest pool entry. */
-struct pool_free_entry;
-typedef struct pool_free_entry pool_free_entry;
-struct pool_free_entry {
- pool_free_entry *next;
-};
-
-typedef struct {
- pool_free_entry *free;
- char *alloc_start;
- char *alloc_end;
- size_t size;
- int count;
-} pool_header;
-
-typedef struct {
- pool_header headers[NUM_POOLS];
- size_t pool_max_size;
- char *alloc_start;
- char *alloc_end;
-} pool_state;
-
-#define ADDR_IN_STATE_ALLOC(st,p) \
- ((char *) (p) >= (st)->alloc_start && (char *) (p) < (st)->alloc_end)
-#define ADDR_IN_HEADER_ALLOC(hdr,p) \
- ((char *) (p) >= (hdr)->alloc_start && (char *) (p) < (hdr)->alloc_end)
-
-#ifdef DUK_ALLOC_HYBRID_DEBUG
-static void dump_pool_state(pool_state *st) {
- pool_free_entry *free;
- int free_len;
- int i;
-
- printf("=== Pool state: st=%p\n", (void *) st);
- for (i = 0; i < (int) NUM_POOLS; i++) {
- pool_header *hdr = st->headers + i;
-
- for (free = hdr->free, free_len = 0; free != NULL; free = free->next) {
- free_len++;
- }
-
- printf("[%d]: size %ld, count %ld, used %ld, free list len %ld\n",
- i, (long) hdr->size, (long) hdr->count,
- (long) (hdr->count - free_len),
- (long) free_len);
- }
-}
-#else
-static void dump_pool_state(pool_state *st) {
- (void) st;
-}
-#endif
-
-void *duk_alloc_hybrid_init(void) {
- pool_state *st;
- size_t total_size, max_size;
- int i, j;
- char *p;
-
- st = (pool_state *) malloc(sizeof(pool_state));
- if (!st) {
- return NULL;
- }
- memset((void *) st, 0, sizeof(pool_state));
- st->alloc_start = NULL;
- st->alloc_end = NULL;
-
- for (i = 0, total_size = 0, max_size = 0; i < (int) NUM_POOLS; i++) {
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("Pool %d: size %ld, count %ld\n", i, (long) pool_sizes[i].size, (long) pool_sizes[i].count);
-#endif
- total_size += pool_sizes[i].size * pool_sizes[i].count;
- if (pool_sizes[i].size > max_size) {
- max_size = pool_sizes[i].size;
- }
- }
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("Total size %ld, max pool size %ld\n", (long) total_size, (long) max_size);
-#endif
-
- st->alloc_start = (char *) malloc(total_size);
- if (!st->alloc_start) {
- free(st);
- return NULL;
- }
- st->alloc_end = st->alloc_start + total_size;
- st->pool_max_size = max_size;
- memset((void *) st->alloc_start, 0, total_size);
-
- for (i = 0, p = st->alloc_start; i < (int) NUM_POOLS; i++) {
- pool_header *hdr = st->headers + i;
-
- hdr->alloc_start = p;
- hdr->alloc_end = p + pool_sizes[i].size * pool_sizes[i].count;
- hdr->free = (pool_free_entry *) (void *) p;
- hdr->size = pool_sizes[i].size;
- hdr->count = pool_sizes[i].count;
-
- for (j = 0; j < pool_sizes[i].count; j++) {
- pool_free_entry *ent = (pool_free_entry *) (void *) p;
- if (j == pool_sizes[i].count - 1) {
- ent->next = (pool_free_entry *) NULL;
- } else {
- ent->next = (pool_free_entry *) (void *) (p + pool_sizes[i].size);
- }
- p += pool_sizes[i].size;
- }
- }
-
- dump_pool_state(st);
-
- /* Use 'st' as udata. */
- return (void *) st;
-}
-
-void *duk_alloc_hybrid(void *udata, duk_size_t size) {
- pool_state *st = (pool_state *) udata;
- int i;
- void *new_ptr;
-
-#if 0
- dump_pool_state(st);
-#endif
-
- if (size == 0) {
- return NULL;
- }
- if (size > st->pool_max_size) {
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("alloc fallback: %ld\n", (long) size);
-#endif
- return malloc(size);
- }
-
- for (i = 0; i < (int) NUM_POOLS; i++) {
- pool_header *hdr = st->headers + i;
- if (hdr->size < size) {
- continue;
- }
-
- if (hdr->free) {
-#if 0
- printf("alloc from pool: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
-#endif
- new_ptr = (void *) hdr->free;
- hdr->free = hdr->free->next;
- return new_ptr;
- } else {
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("alloc out of pool entries: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
-#endif
- break;
- }
- }
-
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("alloc fallback (out of pool): %ld\n", (long) size);
-#endif
- return malloc(size);
-}
-
-void *duk_realloc_hybrid(void *udata, void *ptr, duk_size_t size) {
- pool_state *st = (pool_state *) udata;
- void *new_ptr;
- int i;
-
-#if 0
- dump_pool_state(st);
-#endif
-
- if (ADDR_IN_STATE_ALLOC(st, ptr)) {
- /* 'ptr' cannot be NULL. */
- for (i = 0; i < (int) NUM_POOLS; i++) {
- pool_header *hdr = st->headers + i;
- if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
- if (size <= hdr->size) {
- /* Still fits, no shrink support. */
-#if 0
- printf("realloc original from pool: still fits, size %ld, pool size %ld\n",
- (long) size, (long) hdr->size);
-#endif
- return ptr;
- }
-
- new_ptr = duk_alloc_hybrid(udata, size);
- if (!new_ptr) {
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("realloc original from pool: needed larger size, failed to alloc\n");
-#endif
- return NULL;
- }
- memcpy(new_ptr, ptr, hdr->size);
-
- ((pool_free_entry *) ptr)->next = hdr->free;
- hdr->free = (pool_free_entry *) ptr;
-#if 0
- printf("realloc original from pool: size %ld, pool size %ld\n", (long) size, (long) hdr->size);
-#endif
- return new_ptr;
- }
- }
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("NEVER HERE\n");
-#endif
- return NULL;
- } else if (ptr != NULL) {
- if (size == 0) {
- free(ptr);
- return NULL;
- } else {
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("realloc fallback: size %ld\n", (long) size);
-#endif
- return realloc(ptr, size);
- }
- } else {
-#if 0
- printf("realloc NULL ptr, call alloc: %ld\n", (long) size);
-#endif
- return duk_alloc_hybrid(udata, size);
- }
-}
-
-void duk_free_hybrid(void *udata, void *ptr) {
- pool_state *st = (pool_state *) udata;
- int i;
-
-#if 0
- dump_pool_state(st);
-#endif
-
- if (!ADDR_IN_STATE_ALLOC(st, ptr)) {
- if (ptr == NULL) {
- return;
- }
-#if 0
- printf("free out of pool: %p\n", (void *) ptr);
-#endif
- free(ptr);
- return;
- }
-
- for (i = 0; i < (int) NUM_POOLS; i++) {
- pool_header *hdr = st->headers + i;
- if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
- ((pool_free_entry *) ptr)->next = hdr->free;
- hdr->free = (pool_free_entry *) ptr;
-#if 0
- printf("free from pool: %p\n", ptr);
-#endif
- return;
- }
- }
-
-#ifdef DUK_ALLOC_HYBRID_DEBUG
- printf("NEVER HERE\n");
-#endif
-}