--- a/src/mempool.c Fri Apr 11 09:15:21 2025 +0200 +++ b/src/mempool.c Fri Apr 11 13:20:07 2025 +0200 @@ -38,24 +38,34 @@ char c[]; }; +static int cx_mempool_ensure_capacity( + struct cx_mempool_s *pool, + size_t needed_capacity +) { + if (needed_capacity <= pool->capacity) return 0; + 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)) { + errno = EOVERFLOW; + return 1; + } + struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize); + if (newdata == NULL) return 1; + pool->data = newdata; + pool->capacity = newcap; + return 0; +} + 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; - size_t newmsize; - if (pool->capacity > newcap || cx_szmul(newcap, - sizeof(struct cx_mempool_memory_s*), &newmsize)) { - errno = EOVERFLOW; - return NULL; - } - struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize); - if (newdata == NULL) return NULL; - pool->data = newdata; - pool->capacity = newcap; + if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { + return NULL; } struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); @@ -235,3 +245,41 @@ return pool; } + +int cxMempoolTransfer( + CxMempool *source, + CxMempool *dest +) { + // safety check + if (source == dest) return 1; + + // ensure enough capacity in the destination pool + if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { + return 1; + } + + // allocate a replacement allocator for the source pool + CxAllocator *new_source_allocator = malloc(sizeof(CxAllocator)); + if (new_source_allocator == NULL) { // LCOV_EXCL_START + return 1; + } // LCOV_EXCL_STOP + new_source_allocator->cl = &cx_mempool_allocator_class; + new_source_allocator->data = source; + + // transfer all the data + memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size); + dest->size += source->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, free); + + // prepare the source pool for re-use + source->allocator = new_source_allocator; + memset(source->data, 0, source->size * sizeof(source->data[0])); + source->size = 0; + + return 0; +}