universe@571: /* universe@571: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@571: * universe@571: * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. universe@571: * universe@571: * Redistribution and use in source and binary forms, with or without universe@571: * modification, are permitted provided that the following conditions are met: universe@571: * universe@571: * 1. Redistributions of source code must retain the above copyright universe@571: * notice, this list of conditions and the following disclaimer. universe@571: * universe@571: * 2. Redistributions in binary form must reproduce the above copyright universe@571: * notice, this list of conditions and the following disclaimer in the universe@571: * documentation and/or other materials provided with the distribution. universe@571: * universe@571: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@571: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@571: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@571: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@571: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@571: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@571: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@571: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@571: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@571: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@571: * POSSIBILITY OF SUCH DAMAGE. universe@571: */ universe@571: universe@727: #include "cx/mempool.h" universe@571: #include "cx/utils.h" universe@571: #include universe@571: universe@727: struct cx_mempool_memory_s { universe@571: /** The destructor. */ universe@571: cx_destructor_func destructor; universe@727: /** The actual memory. */ universe@727: char c[]; universe@727: }; universe@571: universe@727: static void *cx_mempool_malloc( universe@727: void *p, universe@727: size_t n universe@571: ) { universe@727: struct cx_mempool_s *pool = p; universe@727: universe@727: if (pool->size >= pool->capacity) { universe@727: size_t newcap = pool->capacity - (pool->capacity % 16) + 16; universe@727: struct cx_mempool_memory_s **newdata = realloc(pool->data, newcap*sizeof(struct cx_mempool_memory_s*)); universe@727: if (newdata == NULL) { universe@727: return NULL; universe@727: } universe@727: pool->data = newdata; universe@727: pool->capacity = newcap; universe@571: } universe@571: universe@727: struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); universe@571: if (mem == NULL) { universe@571: return NULL; universe@571: } universe@571: universe@727: mem->destructor = pool->auto_destr; universe@727: pool->data[pool->size] = mem; universe@727: pool->size++; universe@571: universe@727: return mem->c; universe@571: } universe@571: universe@727: static void *cx_mempool_calloc( universe@727: void *p, universe@571: size_t nelem, universe@571: size_t elsize universe@571: ) { universe@571: size_t msz; universe@571: if (cx_szmul(nelem, elsize, &msz)) { universe@571: return NULL; universe@571: } universe@727: void *ptr = cx_mempool_malloc(p, msz); universe@571: if (ptr == NULL) { universe@571: return NULL; universe@571: } universe@571: memset(ptr, 0, nelem * elsize); universe@571: return ptr; universe@571: } universe@571: universe@727: static void *cx_mempool_realloc( universe@727: void *p, universe@571: void *ptr, universe@571: size_t n universe@571: ) { universe@727: struct cx_mempool_s *pool = p; universe@571: universe@727: struct cx_mempool_memory_s *mem, *newm; universe@727: mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); universe@727: newm = realloc(mem, n + sizeof(cx_destructor_func)); universe@727: universe@571: if (newm == NULL) { universe@571: return NULL; universe@571: } universe@571: if (mem != newm) { universe@727: cx_for_n(i, pool->size) { universe@571: if (pool->data[i] == mem) { universe@571: pool->data[i] = newm; universe@727: return ((char*)newm) + sizeof(cx_destructor_func); universe@571: } universe@571: } universe@571: abort(); universe@571: } else { universe@727: return ptr; universe@571: } universe@571: } universe@571: universe@727: static void cx_mempool_free( universe@727: void *p, universe@571: void *ptr universe@571: ) { universe@727: struct cx_mempool_s *pool = p; universe@571: universe@727: struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) universe@571: ((char *) ptr - sizeof(cx_destructor_func)); universe@727: universe@727: cx_for_n(i, pool->size) { universe@571: if (mem == pool->data[i]) { universe@727: if (mem->destructor) { universe@727: mem->destructor(mem->c); universe@571: } universe@572: free(mem); universe@727: size_t last_index = pool->size - 1; universe@571: if (i != last_index) { universe@571: pool->data[i] = pool->data[last_index]; universe@571: pool->data[last_index] = NULL; universe@571: } universe@727: pool->size--; universe@571: return; universe@571: } universe@571: } universe@571: abort(); universe@571: } universe@571: universe@727: void cxMempoolDestroy(CxMempool *pool) { universe@727: struct cx_mempool_memory_s *mem; universe@727: cx_for_n(i, pool->size) { universe@727: mem = pool->data[i]; universe@727: if (mem->destructor) { universe@727: mem->destructor(mem->c); universe@571: } universe@727: free(mem); universe@571: } universe@571: free(pool->data); universe@727: free((void*) pool->allocator); universe@571: free(pool); universe@571: } universe@571: universe@727: void cxMempoolSetDestructor( universe@571: void *ptr, universe@571: cx_destructor_func func universe@571: ) { universe@571: *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; universe@571: } universe@571: universe@727: struct cx_mempool_foreign_mem_s { universe@727: cx_destructor_func destr; universe@727: void* mem; universe@571: }; universe@571: universe@727: static void cx_mempool_destr_foreign_mem(void* ptr) { universe@727: struct cx_mempool_foreign_mem_s *fm = ptr; universe@727: fm->destr(fm->mem); universe@727: } universe@727: universe@727: int cxMempoolRegister( universe@727: CxMempool *pool, universe@727: void *memory, universe@727: cx_destructor_func destr universe@727: ) { universe@727: struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( universe@727: pool, universe@727: sizeof(struct cx_mempool_foreign_mem_s) universe@727: ); universe@727: if (fm == NULL) return 1; universe@727: universe@727: fm->mem = memory; universe@727: fm->destr = destr; universe@727: *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; universe@727: universe@727: return 0; universe@727: } universe@727: universe@727: static cx_allocator_class cx_mempool_allocator_class = { universe@727: cx_mempool_malloc, universe@727: cx_mempool_realloc, universe@727: cx_mempool_calloc, universe@727: cx_mempool_free universe@571: }; universe@571: universe@727: CxMempool *cxMempoolCreate( universe@727: size_t capacity, universe@727: cx_destructor_func destr universe@727: ) { universe@571: size_t poolsize; universe@727: if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { universe@571: return NULL; universe@571: } universe@571: universe@727: struct cx_mempool_s *pool = universe@727: malloc(sizeof(struct cx_mempool_s)); universe@571: if (pool == NULL) { universe@571: return NULL; universe@571: } universe@571: universe@571: CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); universe@727: if (provided_allocator == NULL) { universe@571: free(pool); universe@571: return NULL; universe@571: } universe@727: provided_allocator->cl = &cx_mempool_allocator_class; universe@571: provided_allocator->data = pool; universe@571: universe@727: pool->allocator = provided_allocator; universe@571: universe@571: pool->data = malloc(poolsize); universe@571: if (pool->data == NULL) { universe@571: free(provided_allocator); universe@571: free(pool); universe@571: return NULL; universe@571: } universe@571: universe@727: pool->size = 0; universe@727: pool->capacity = capacity; universe@727: pool->auto_destr = destr; universe@571: universe@571: return (CxMempool *) pool; universe@571: }