/* * 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/basic_mempool.h" #include "cx/utils.h" #include #define of_chk_(n) if (SIZE_MAX - sizeof(cx_destructor_func) < (n)) return NULL /** Internal structure for denoting pooled memory. */ typedef struct { /** The destructor. */ cx_destructor_func destructor; /** * Access to the first byte of the polled memory. */ char c; } cx_basic_mempool_memory; static int cx_basic_mempool_chcap( struct cx_basic_mempool_s *pool, size_t newcap ) { if (newcap < pool->ndata) { return 1; } size_t newcapsz; if (cx_szmul(newcap, sizeof(void *), &newcapsz)) { return 1; } void **data = realloc(pool->data, newcapsz); if (data) { pool->data = data; pool->size = newcap; return 0; } else { return 1; } } void *cx_malloc_basic_mempool( void *data, size_t n ) { of_chk_(n); struct cx_basic_mempool_s *pool = data; if (pool->ndata >= pool->size) { size_t newcap = pool->size * 2; if (newcap < pool->size || cx_basic_mempool_chcap(pool, newcap)) { return NULL; } } cx_basic_mempool_memory *mem = malloc(sizeof(cx_destructor_func) + n); if (mem == NULL) { return NULL; } mem->destructor = NULL; pool->data[pool->ndata] = mem; pool->ndata++; return &(mem->c); } void *cx_calloc_basic_mempool( void *data, size_t nelem, size_t elsize ) { size_t msz; if (cx_szmul(nelem, elsize, &msz)) { return NULL; } void *ptr = cx_malloc_basic_mempool(data, msz); if (ptr == NULL) { return NULL; } memset(ptr, 0, nelem * elsize); return ptr; } void *cx_realloc_basic_mempool( void *data, void *ptr, size_t n ) { of_chk_(n); struct cx_basic_mempool_s *pool = data; char *mem = ((char *) ptr) - sizeof(cx_destructor_func); char *newm = (char *) realloc(mem, n + sizeof(cx_destructor_func)); if (newm == NULL) { return NULL; } if (mem != newm) { cx_for_n(i, pool->ndata) { if (pool->data[i] == mem) { pool->data[i] = newm; return newm + sizeof(cx_destructor_func); } } abort(); } else { return newm + sizeof(cx_destructor_func); } } void cx_free_basic_mempool( void *data, void *ptr ) { struct cx_basic_mempool_s *pool = data; cx_basic_mempool_memory *mem = (cx_basic_mempool_memory *) ((char *) ptr - sizeof(cx_destructor_func)); cx_for_n(i, pool->ndata) { if (mem == pool->data[i]) { if (mem->destructor != NULL) { mem->destructor(&(mem->c)); } free(mem); size_t last_index = pool->ndata - 1; if (i != last_index) { pool->data[i] = pool->data[last_index]; pool->data[last_index] = NULL; } pool->ndata--; return; } } abort(); } void cx_basic_mempool_destroy(CxMempool *p) { struct cx_basic_mempool_s *pool = (struct cx_basic_mempool_s *) p; cx_basic_mempool_memory *mem; cx_for_n(i, pool->ndata) { mem = (cx_basic_mempool_memory *) pool->data[i]; if (mem) { if (mem->destructor) { mem->destructor(&(mem->c)); } free(mem); } } free(pool->data); free((void *) p->allocator); free(pool); } void cx_basic_mempool_set_destr( __attribute__((__unused__)) CxMempool *pool, void *ptr, cx_destructor_func func ) { *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; } static cx_allocator_class cx_basic_mempool_allocator_class = { cx_malloc_basic_mempool, cx_realloc_basic_mempool, cx_calloc_basic_mempool, cx_free_basic_mempool }; static cx_mempool_class cx_basic_mempool_class = { cx_basic_mempool_destroy, cx_basic_mempool_set_destr, }; CxMempool *cxBasicMempoolCreate(size_t capacity) { size_t poolsize; if (cx_szmul(capacity, sizeof(void *), &poolsize)) { return NULL; } struct cx_basic_mempool_s *pool = malloc(sizeof(struct cx_basic_mempool_s)); if (pool == NULL) { return NULL; } CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); if (!provided_allocator) { free(pool); return NULL; } provided_allocator->cl = &cx_basic_mempool_allocator_class; provided_allocator->data = pool; pool->base.cl = &cx_basic_mempool_class; pool->base.allocator = provided_allocator; pool->data = malloc(poolsize); if (pool->data == NULL) { free(provided_allocator); free(pool); return NULL; } pool->ndata = 0; pool->size = capacity; return (CxMempool *) pool; }