36 cx_destructor_func destructor; |
36 cx_destructor_func destructor; |
37 /** The actual memory. */ |
37 /** The actual memory. */ |
38 char c[]; |
38 char c[]; |
39 }; |
39 }; |
40 |
40 |
|
41 static int cx_mempool_ensure_capacity( |
|
42 struct cx_mempool_s *pool, |
|
43 size_t needed_capacity |
|
44 ) { |
|
45 if (needed_capacity <= pool->capacity) return 0; |
|
46 size_t newcap = pool->capacity >= 1000 ? |
|
47 pool->capacity + 1000 : pool->capacity * 2; |
|
48 size_t newmsize; |
|
49 if (pool->capacity > newcap || cx_szmul(newcap, |
|
50 sizeof(struct cx_mempool_memory_s*), &newmsize)) { |
|
51 errno = EOVERFLOW; |
|
52 return 1; |
|
53 } |
|
54 struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize); |
|
55 if (newdata == NULL) return 1; |
|
56 pool->data = newdata; |
|
57 pool->capacity = newcap; |
|
58 return 0; |
|
59 } |
|
60 |
41 static void *cx_mempool_malloc( |
61 static void *cx_mempool_malloc( |
42 void *p, |
62 void *p, |
43 size_t n |
63 size_t n |
44 ) { |
64 ) { |
45 struct cx_mempool_s *pool = p; |
65 struct cx_mempool_s *pool = p; |
46 |
66 |
47 if (pool->size >= pool->capacity) { |
67 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
48 size_t newcap = pool->capacity - (pool->capacity % 16) + 16; |
68 return NULL; |
49 size_t newmsize; |
|
50 if (pool->capacity > newcap || cx_szmul(newcap, |
|
51 sizeof(struct cx_mempool_memory_s*), &newmsize)) { |
|
52 errno = EOVERFLOW; |
|
53 return NULL; |
|
54 } |
|
55 struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize); |
|
56 if (newdata == NULL) return NULL; |
|
57 pool->data = newdata; |
|
58 pool->capacity = newcap; |
|
59 } |
69 } |
60 |
70 |
61 struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); |
71 struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); |
62 if (mem == NULL) return NULL; |
72 if (mem == NULL) return NULL; |
63 |
73 |
233 pool->capacity = capacity; |
243 pool->capacity = capacity; |
234 pool->auto_destr = destr; |
244 pool->auto_destr = destr; |
235 |
245 |
236 return pool; |
246 return pool; |
237 } |
247 } |
|
248 |
|
249 int cxMempoolTransfer( |
|
250 CxMempool *source, |
|
251 CxMempool *dest |
|
252 ) { |
|
253 // safety check |
|
254 if (source == dest) return 1; |
|
255 |
|
256 // ensure enough capacity in the destination pool |
|
257 if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { |
|
258 return 1; |
|
259 } |
|
260 |
|
261 // allocate a replacement allocator for the source pool |
|
262 CxAllocator *new_source_allocator = malloc(sizeof(CxAllocator)); |
|
263 if (new_source_allocator == NULL) { // LCOV_EXCL_START |
|
264 return 1; |
|
265 } // LCOV_EXCL_STOP |
|
266 new_source_allocator->cl = &cx_mempool_allocator_class; |
|
267 new_source_allocator->data = source; |
|
268 |
|
269 // transfer all the data |
|
270 memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size); |
|
271 dest->size += source->size; |
|
272 |
|
273 // register the old allocator with the new pool |
|
274 // we have to remove const-ness for this, but that's okay here |
|
275 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; |
|
276 transferred_allocator->data = dest; |
|
277 cxMempoolRegister(dest, transferred_allocator, free); |
|
278 |
|
279 // prepare the source pool for re-use |
|
280 source->allocator = new_source_allocator; |
|
281 memset(source->data, 0, source->size * sizeof(source->data[0])); |
|
282 source->size = 0; |
|
283 |
|
284 return 0; |
|
285 } |