/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012-2015, RedJack, LLC. * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/callbacks.h" #include "libcork/core/mempool.h" #include "libcork/core/types.h" #include "libcork/helpers/errors.h" #if !defined(CORK_DEBUG_MEMPOOL) #define CORK_DEBUG_MEMPOOL 0 #endif #if CORK_DEBUG_MEMPOOL #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif struct cork_mempool { size_t element_size; size_t block_size; struct cork_mempool_object *free_list; /* The number of objects that have been given out by * cork_mempool_new but not returned via cork_mempool_free. */ size_t allocated_count; struct cork_mempool_block *blocks; void *user_data; cork_free_f free_user_data; cork_init_f init_object; cork_done_f done_object; }; struct cork_mempool_object { /* When this object is unclaimed, it will be in the cork_mempool * object's free_list using this pointer. */ struct cork_mempool_object *next_free; }; struct cork_mempool_block { struct cork_mempool_block *next_block; }; #define cork_mempool_object_size(mp) \ (sizeof(struct cork_mempool_object) + (mp)->element_size) #define cork_mempool_get_header(obj) \ (((struct cork_mempool_object *) (obj)) - 1) #define cork_mempool_get_object(hdr) \ ((void *) (((struct cork_mempool_object *) (hdr)) + 1)) struct cork_mempool * cork_mempool_new_size_ex(size_t element_size, size_t block_size) { struct cork_mempool *mp = cork_new(struct cork_mempool); mp->element_size = element_size; mp->block_size = block_size; mp->free_list = NULL; mp->allocated_count = 0; mp->blocks = NULL; mp->user_data = NULL; mp->free_user_data = NULL; mp->init_object = NULL; mp->done_object = NULL; return mp; } void cork_mempool_free(struct cork_mempool *mp) { struct cork_mempool_block *curr; assert(mp->allocated_count == 0); if (mp->done_object != NULL) { struct cork_mempool_object *obj; for (obj = mp->free_list; obj != NULL; obj = obj->next_free) { mp->done_object (mp->user_data, cork_mempool_get_object(obj)); } } for (curr = mp->blocks; curr != NULL; ) { struct cork_mempool_block *next = curr->next_block; cork_free(curr, mp->block_size); /* Do this here instead of in the for statement to avoid * accessing the just-freed block. */ curr = next; } cork_free_user_data(mp); cork_delete(struct cork_mempool, mp); } void cork_mempool_set_user_data(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data) { cork_free_user_data(mp); mp->user_data = user_data; mp->free_user_data = free_user_data; } void cork_mempool_set_init_object(struct cork_mempool *mp, cork_init_f init_object) { mp->init_object = init_object; } void cork_mempool_set_done_object(struct cork_mempool *mp, cork_done_f done_object) { mp->done_object = done_object; } void cork_mempool_set_callbacks(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data, cork_init_f init_object, cork_done_f done_object) { cork_mempool_set_user_data(mp, user_data, free_user_data); cork_mempool_set_init_object(mp, init_object); cork_mempool_set_done_object(mp, done_object); } /* If this function succeeds, then we guarantee that there will be at * least one object in mp->free_list. */ static void cork_mempool_new_block(struct cork_mempool *mp) { /* Allocate the new block and add it to mp's block list. */ struct cork_mempool_block *block; void *vblock; DEBUG("Allocating new %zu-byte block\n", mp->block_size); block = cork_malloc(mp->block_size); block->next_block = mp->blocks; mp->blocks = block; vblock = block; /* Divide the block's memory region into a bunch of objects. */ size_t index = sizeof(struct cork_mempool_block); for (index = sizeof(struct cork_mempool_block); (index + cork_mempool_object_size(mp)) <= mp->block_size; index += cork_mempool_object_size(mp)) { struct cork_mempool_object *obj = vblock + index; DEBUG(" New object at %p[%p]\n", cork_mempool_get_object(obj), obj); if (mp->init_object != NULL) { mp->init_object (mp->user_data, cork_mempool_get_object(obj)); } obj->next_free = mp->free_list; mp->free_list = obj; } } void * cork_mempool_new_object(struct cork_mempool *mp) { struct cork_mempool_object *obj; void *ptr; if (CORK_UNLIKELY(mp->free_list == NULL)) { cork_mempool_new_block(mp); } obj = mp->free_list; mp->free_list = obj->next_free; mp->allocated_count++; ptr = cork_mempool_get_object(obj); return ptr; } void cork_mempool_free_object(struct cork_mempool *mp, void *ptr) { struct cork_mempool_object *obj = cork_mempool_get_header(ptr); DEBUG("Returning %p[%p] to memory pool\n", ptr, obj); obj->next_free = mp->free_list; mp->free_list = obj; mp->allocated_count--; }