--- a/src/mempool.c Thu May 22 16:23:55 2025 +0200 +++ b/src/mempool.c Thu May 22 16:25:32 2025 +0200 @@ -31,13 +31,6 @@ #include <string.h> #include <errno.h> -struct cx_mempool_memory_s { - /** The destructor. */ - cx_destructor_func destructor; - /** The actual memory. */ - char c[]; -}; - static int cx_mempool_ensure_capacity( struct cx_mempool_s *pool, size_t needed_capacity @@ -46,20 +39,39 @@ size_t newcap = pool->capacity >= 1000 ? pool->capacity + 1000 : pool->capacity * 2; size_t newmsize; - if (pool->capacity > newcap || cx_szmul(newcap, - sizeof(struct cx_mempool_memory_s*), &newmsize)) { + if (pool->capacity > newcap + || cx_szmul(newcap, sizeof(void*), &newmsize)) { errno = EOVERFLOW; return 1; } - struct cx_mempool_memory_s **newdata = cxRealloc( - cxDefaultAllocator, pool->data, newmsize); + void **newdata = cxReallocDefault(pool->data, newmsize); if (newdata == NULL) return 1; pool->data = newdata; pool->capacity = newcap; return 0; } -static void *cx_mempool_malloc( +static int cx_mempool_ensure_registered_capacity( + struct cx_mempool_s *pool, + size_t needed_capacity +) { + if (needed_capacity <= pool->registered_capacity) return 0; + // we do not expect so many registrations + size_t newcap = pool->registered_capacity + 8; + size_t newmsize; + if (pool->registered_capacity > newcap || cx_szmul(newcap, + sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) { + errno = EOVERFLOW; + return 1; + } + void *newdata = cxReallocDefault(pool->registered, newmsize); + if (newdata == NULL) return 1; + pool->registered = newdata; + pool->registered_capacity = newcap; + return 0; +} + +static void *cx_mempool_malloc_simple( void *p, size_t n ) { @@ -69,18 +81,17 @@ return NULL; } - struct cx_mempool_memory_s *mem = cxMalloc( - cxDefaultAllocator, sizeof(cx_destructor_func) + n); + struct cx_mempool_memory_s *mem = + cxMallocDefault(sizeof(struct cx_mempool_memory_s) + n); if (mem == NULL) return NULL; - - mem->destructor = pool->auto_destr; + mem->destructor = NULL; pool->data[pool->size] = mem; pool->size++; return mem->c; } -static void *cx_mempool_calloc( +static void *cx_mempool_calloc_simple( void *p, size_t nelem, size_t elsize @@ -90,29 +101,31 @@ errno = EOVERFLOW; return NULL; } - void *ptr = cx_mempool_malloc(p, msz); + void *ptr = cx_mempool_malloc_simple(p, msz); if (ptr == NULL) return NULL; memset(ptr, 0, nelem * elsize); return ptr; } -static void *cx_mempool_realloc( +static void *cx_mempool_realloc_simple( 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 = cxReallocDefault(mem, n + sizeof(cx_destructor_func)); + const unsigned overhead = sizeof(struct cx_mempool_memory_s); + struct cx_mempool_memory_s *mem = + (void *) (((char *) ptr) - overhead); + struct cx_mempool_memory_s *newm = + cxReallocDefault(mem, n + overhead); if (newm == NULL) return NULL; if (mem != newm) { for (size_t i = 0; i < pool->size; i++) { if (pool->data[i] == mem) { pool->data[i] = newm; - return ((char*)newm) + sizeof(cx_destructor_func); + return ((char*)newm) + overhead; } } abort(); // LCOV_EXCL_LINE @@ -121,21 +134,27 @@ } } -static void cx_mempool_free( +static void cx_mempool_free_simple( void *p, void *ptr ) { if (!ptr) return; struct cx_mempool_s *pool = p; - struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) - ((char *) ptr - sizeof(cx_destructor_func)); + struct cx_mempool_memory_s *mem = + (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s)); for (size_t i = 0; i < pool->size; i++) { if (mem == pool->data[i]) { if (mem->destructor) { mem->destructor(mem->c); } + if (pool->destr) { + pool->destr(mem->c); + } + if (pool->destr2) { + pool->destr2(pool->destr2_data, mem->c); + } cxFreeDefault(mem); size_t last_index = pool->size - 1; if (i != last_index) { @@ -149,17 +168,54 @@ abort(); // LCOV_EXCL_LINE } -void cxMempoolFree(CxMempool *pool) { - if (pool == NULL) return; - struct cx_mempool_memory_s *mem; +static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) { + const bool has_destr = pool->destr; + const bool has_destr2 = pool->destr2; for (size_t i = 0; i < pool->size; i++) { - mem = pool->data[i]; + struct cx_mempool_memory_s *mem = pool->data[i]; if (mem->destructor) { mem->destructor(mem->c); } + if (has_destr) { + pool->destr(mem->c); + } + if (has_destr2) { + pool->destr2(pool->destr2_data, mem->c); + } cxFreeDefault(mem); } +} + +static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) { + for (size_t i = 0; i < pool->registered_size; i++) { + struct cx_mempool_foreign_memory_s info = pool->registered[i]; + if (info.destr2_data == NULL) { + if (info.destr) { + info.destr(info.mem); + } + } else { + info.destr2(info.destr2_data, info.mem); + } + } +} + +static cx_allocator_class cx_mempool_simple_allocator_class = { + cx_mempool_malloc_simple, + cx_mempool_realloc_simple, + cx_mempool_calloc_simple, + cx_mempool_free_simple +}; + +void cxMempoolFree(CxMempool *pool) { + if (pool == NULL) return; + if (pool->allocator->cl == &cx_mempool_simple_allocator_class) { + cx_mempool_free_all_simple(pool); + } else { + // TODO: implement + } + cx_mempool_free_foreign(pool); cxFreeDefault(pool->data); + cxFreeDefault(pool->registered); cxFreeDefault((void*) pool->allocator); cxFreeDefault(pool); } @@ -175,64 +231,56 @@ *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; } -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; + if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) { + return 1; // LCOV_EXCL_LINE + } - fm->mem = memory; - fm->destr = destr; - *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; + pool->registered[pool->registered_size++] = + (struct cx_mempool_foreign_memory_s) { + .mem = memory, + .destr = destr, + .destr2_data = NULL + }; 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 + enum cx_mempool_type type ) { + if (capacity == 0) capacity = 16; size_t poolsize; - if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { + if (cx_szmul(capacity, sizeof(void*), &poolsize)) { errno = EOVERFLOW; return NULL; } - struct cx_mempool_s *pool = - cxMallocDefault(sizeof(struct cx_mempool_s)); - if (pool == NULL) return NULL; - CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator)); if (provided_allocator == NULL) { // LCOV_EXCL_START - cxFreeDefault(pool); return NULL; } // LCOV_EXCL_STOP - provided_allocator->cl = &cx_mempool_allocator_class; + + CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool)); + if (pool == NULL) { + cxFreeDefault(provided_allocator); + return NULL; + } + provided_allocator->data = pool; - pool->allocator = provided_allocator; + if (type == CX_MEMPOOL_TYPE_SIMPLE) { + provided_allocator->cl = &cx_mempool_simple_allocator_class; + } else if (type == CX_MEMPOOL_TYPE_ADVANCED) { + // TODO: implement + } else { + // TODO: implement + } pool->data = cxMallocDefault(poolsize); if (pool->data == NULL) { // LCOV_EXCL_START @@ -243,7 +291,6 @@ pool->size = 0; pool->capacity = capacity; - pool->auto_destr = destr; return pool; } @@ -256,11 +303,16 @@ CxMempool *source, CxMempool *dest ) { - // safety check + // safety checks if (source == dest) return 1; + if (source->allocator->cl != dest->allocator->cl) return 1; // ensure enough capacity in the destination pool - if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { + if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) { + return 1; // LCOV_EXCL_LINE + } + if (cx_mempool_ensure_registered_capacity(dest, + dest->registered_size + source->registered_size)) { return 1; // LCOV_EXCL_LINE } @@ -269,23 +321,32 @@ if (new_source_allocator == NULL) { // LCOV_EXCL_START return 1; } // LCOV_EXCL_STOP - new_source_allocator->cl = &cx_mempool_allocator_class; + new_source_allocator->cl = source->allocator->cl; new_source_allocator->data = source; // transfer all the data - memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size); + memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size); dest->size += source->size; + // transfer all registered memory + memcpy(&dest->registered[dest->registered_size], source->registered, + sizeof(struct cx_mempool_foreign_memory_s) * source->size); + dest->registered_size += source->registered_size; + // register the old allocator with the new pool // we have to remove const-ness for this, but that's okay here CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; transferred_allocator->data = dest; - cxMempoolRegister(dest, transferred_allocator, cx_mempool_free_transferred_allocator); + cxMempoolRegister(dest, transferred_allocator, + cx_mempool_free_transferred_allocator); // prepare the source pool for re-use source->allocator = new_source_allocator; - memset(source->data, 0, source->size * sizeof(source->data[0])); + memset(source->data, 0, source->size * sizeof(void*)); + memset(source->registered, 0, + source->registered_size * sizeof(struct cx_mempool_foreign_memory_s)); source->size = 0; + source->registered_size = 0; return 0; }