/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2021 Mike Becker, Olaf Wintermann 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. */ #include "cx/mempool.h" #include "cx/utils.h" #include struct cx_mempool_memory_s { /** The destructor. */ cx_destructor_func destructor; /** The actual memory. */ char c[]; }; static void *cx_mempool_malloc( void *p, size_t n ) { struct cx_mempool_s *pool = p; if (pool->size >= pool->capacity) { size_t newcap = pool->capacity - (pool->capacity % 16) + 16; struct cx_mempool_memory_s **newdata = realloc(pool->data, newcap*sizeof(struct cx_mempool_memory_s*)); if (newdata == NULL) { return NULL; } pool->data = newdata; pool->capacity = newcap; } struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); if (mem == NULL) { return NULL; } mem->destructor = pool->auto_destr; pool->data[pool->size] = mem; pool->size++; return mem->c; } static void *cx_mempool_calloc( void *p, size_t nelem, size_t elsize ) { size_t msz; if (cx_szmul(nelem, elsize, &msz)) { return NULL; } void *ptr = cx_mempool_malloc(p, msz); if (ptr == NULL) { return NULL; } memset(ptr, 0, nelem * elsize); return ptr; } static void *cx_mempool_realloc( void *p, void *ptr, size_t n ) { struct cx_mempool_s *pool = p; struct cx_mempool_memory_s *mem, *newm; mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); newm = realloc(mem, n + sizeof(cx_destructor_func)); if (newm == NULL) { return NULL; } if (mem != newm) { cx_for_n(i, pool->size) { if (pool->data[i] == mem) { pool->data[i] = newm; return ((char*)newm) + sizeof(cx_destructor_func); } } abort(); } else { return ptr; } } static void cx_mempool_free( void *p, void *ptr ) { struct cx_mempool_s *pool = p; struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) ((char *) ptr - sizeof(cx_destructor_func)); cx_for_n(i, pool->size) { if (mem == pool->data[i]) { if (mem->destructor) { mem->destructor(mem->c); } free(mem); size_t last_index = pool->size - 1; if (i != last_index) { pool->data[i] = pool->data[last_index]; pool->data[last_index] = NULL; } pool->size--; return; } } abort(); } void cxMempoolDestroy(CxMempool *pool) { struct cx_mempool_memory_s *mem; cx_for_n(i, pool->size) { mem = pool->data[i]; if (mem->destructor) { mem->destructor(mem->c); } free(mem); } free(pool->data); free((void*) pool->allocator); free(pool); } void cxMempoolSetDestructor( void *ptr, cx_destructor_func func ) { *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; } struct cx_mempool_foreign_mem_s { cx_destructor_func destr; void* mem; }; static void cx_mempool_destr_foreign_mem(void* ptr) { struct cx_mempool_foreign_mem_s *fm = ptr; fm->destr(fm->mem); } int cxMempoolRegister( CxMempool *pool, void *memory, cx_destructor_func destr ) { struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( pool, sizeof(struct cx_mempool_foreign_mem_s) ); if (fm == NULL) return 1; fm->mem = memory; fm->destr = destr; *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; return 0; } static cx_allocator_class cx_mempool_allocator_class = { cx_mempool_malloc, cx_mempool_realloc, cx_mempool_calloc, cx_mempool_free }; CxMempool *cxMempoolCreate( size_t capacity, cx_destructor_func destr ) { size_t poolsize; if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { return NULL; } struct cx_mempool_s *pool = malloc(sizeof(struct cx_mempool_s)); if (pool == NULL) { return NULL; } CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); if (provided_allocator == NULL) { free(pool); return NULL; } provided_allocator->cl = &cx_mempool_allocator_class; provided_allocator->data = pool; pool->allocator = provided_allocator; pool->data = malloc(poolsize); if (pool->data == NULL) { free(provided_allocator); free(pool); return NULL; } pool->size = 0; pool->capacity = capacity; pool->auto_destr = destr; return (CxMempool *) pool; }