/* * Copyright (C) 2003, 2004, 2005, 2014, 2015 Filip Pizlo. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Region memory management API specifically for complex objects that consist of lots of * small chunks of memory. This code will compile as both C and C++. */ #ifndef FP_TSF_REGION_H #define FP_TSF_REGION_H #include "tsf_config.h" #include "tsf_types.h" #include #include /* private stuff */ #define TSF_ALIGN(size) \ ((size + TSF_NATIVE_ALIGNMENT - 1) & (~(TSF_NATIVE_ALIGNMENT - 1))) /* the following invariant must hold: TSF_REGION_SIZE_BITS < 16 */ #define TSF_REGION_SIZE_BITS 10 #define TSF_REGION_SIZE_UNIT (1 << TSF_REGION_SIZE_BITS) /* only pass an aligned size */ #define TSF_SIZE_FOR_REGION(size_needed) \ ((size_needed + TSF_REGION_SIZE_UNIT - 1) & ~(TSF_REGION_SIZE_UNIT - 1)) #define TSF_INITIAL_SIZE_FOR_REGION(size_needed) \ TSF_SIZE_FOR_REGION(size_needed) struct tsf_region; struct tsf_region_cback_node; typedef struct tsf_region tsf_region_t; typedef struct tsf_region_cback_node tsf_region_cback_node_t; typedef void (*tsf_region_cback_t)(void *arg); struct tsf_region_cback_node { tsf_region_cback_t cback; void *arg; tsf_region_cback_node_t *next; }; struct tsf_region { tsf_region_t *next; uint8_t *alloc; size_t size; tsf_region_cback_node_t *cback_head; /* wanna make this structure native aligned. */ #if (((NATIVE_ALIGNMENT - \ (SIZEOF_VOID_P * 3 + SIZEOF_SIZE_T)) & \ (NATIVE_ALIGNMENT - 1)) != 0) char padding[(TSF_NATIVE_ALIGNMENT - (TSF_SIZEOF_VOID_P * 3 + TSF_SIZEOF_SIZE_T)) & (TSF_NATIVE_ALIGNMENT - 1)]; #endif }; static TSF_inline void *tsf_region_create(size_t size) { size_t region_size; tsf_region_t *region; size = TSF_ALIGN(size); region_size = TSF_INITIAL_SIZE_FOR_REGION(size); /* start a new region and allocate the root object */ region = (tsf_region_t*) malloc(sizeof(tsf_region_t) + region_size); if (region == NULL) { return NULL; } region->next = region; region->alloc = ((uint8_t*)(region + 1)) + size; region->size = region_size; region->cback_head = NULL; return region + 1; } static TSF_inline void *tsf_region_alloc(void *root, size_t size) { tsf_region_t *region; tsf_region_t *next; void *result; size = TSF_ALIGN(size); region = ((tsf_region_t*)root) - 1; next = region->next; /* allocate new object */ /* we do the comparison this way to avoid overflow! */ if (next->alloc - ((uint8_t*)next) - sizeof(tsf_region_t) + size > next->size) { /* alloc new chunk */ tsf_region_t *new_chunk = (tsf_region_t*) malloc(sizeof(tsf_region_t) + TSF_SIZE_FOR_REGION(size)); if (new_chunk == NULL) { return NULL; } new_chunk->next = next; new_chunk->alloc = (uint8_t*)(new_chunk + 1); new_chunk->size = TSF_SIZE_FOR_REGION(size); next=region->next = new_chunk; } result = next->alloc; next->alloc += size; return result; } static TSF_inline char *tsf_region_strdup(void *root, const char *str) { size_t size; char *result; size = strlen(str) + 1; result = (char*)tsf_region_alloc(root, size); if (result == NULL) { return NULL; } memcpy(result, str, size); return result; } static TSF_inline void *tsf_region_realloc(void *root, void *obj, size_t size) { tsf_region_t *region; tsf_region_t *next; size = TSF_ALIGN(size); region = ((tsf_region_t*)root) - 1; next = region->next; /* resize an existing object */ /* we do the comparison this way to avoid overflow! */ if (((uint8_t*)obj) - ((uint8_t*)next) - sizeof(tsf_region_t) + size > next->size) { /* check if this is the only object in the chunk. */ if (next + 1 == obj) { /* if so, simply realloc the chunk. we do this to prevent * O(n^2) memory usage in the case of repeated reallocs. */ next = (tsf_region_t*) realloc(next, sizeof(tsf_region_t) + TSF_SIZE_FOR_REGION(size)); if (next == NULL) { return NULL; } } else { /* alloc new chunk */ tsf_region_t *new_chunk = (tsf_region_t*) malloc(sizeof(tsf_region_t) + TSF_SIZE_FOR_REGION(size)); if (new_chunk == NULL) { return NULL; } new_chunk->next = next; new_chunk->size = TSF_SIZE_FOR_REGION(size); memcpy(new_chunk + 1, obj, next->alloc - ((uint8_t*)obj)); next = new_chunk; } region->next = next; obj = next + 1; } next->alloc = ((uint8_t*)obj) + size; return obj; } /* allocate either in a region or using straight malloc. will use * malloc if region is NULL, otherwise will call tsf_region_alloc.() * this cannot create a new region. this is useful as a convenience * function for implementing data structures that need to be used both * in a region and with malloc & free. */ static TSF_inline void *tsf_cond_alloc(void *root, size_t size) { if (root == NULL) { return malloc(size); } return tsf_region_alloc(root, size); } /* reallocate either in a region or using straight malloc. */ static TSF_inline void *tsf_cond_realloc(void *root, void *ptr, size_t size) { if (root == NULL) { return realloc(ptr, size); } return tsf_region_realloc(root, ptr, size); } static TSF_inline tsf_bool_t tsf_region_add_cback(void *root, tsf_region_cback_t cback, void *arg) { tsf_region_t *region = ((tsf_region_t*)root) - 1; tsf_region_cback_node_t *node = (tsf_region_cback_node_t*) tsf_region_alloc(root, sizeof(tsf_region_cback_node_t)); if (node == NULL) { return tsf_false; } node->next = region->cback_head; node->cback = cback; node->arg = arg; region->cback_head = node; return tsf_true; } /* delete a region rooted at the given object. */ static TSF_inline void tsf_region_free(void *root) { tsf_region_t *cur = ((tsf_region_t*)root) - 1; tsf_region_cback_node_t *cback_node = cur->cback_head; while (cback_node != NULL) { cback_node->cback(cback_node->arg); cback_node = cback_node->next; } do { tsf_region_t *to_free = cur; cur = cur->next; free(to_free); } while (cur+1 != root); } /* get the total allocatable space occupied by this region */ static TSF_inline size_t tsf_region_size(void *root) { tsf_region_t *cur = ((tsf_region_t*)root) - 1; size_t result = 0; do { result += cur->size; cur = cur->next; } while (cur + 1 != root); return result; } #endif