| 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 } |