| 29 #include "cx/mempool.h" |
29 #include "cx/mempool.h" |
| 30 |
30 |
| 31 #include <string.h> |
31 #include <string.h> |
| 32 #include <errno.h> |
32 #include <errno.h> |
| 33 |
33 |
| 34 struct cx_mempool_memory_s { |
34 static int cx_mempool_ensure_capacity( |
| 35 /** The destructor. */ |
35 struct cx_mempool_s *pool, |
| 36 cx_destructor_func destructor; |
36 size_t needed_capacity |
| 37 /** The actual memory. */ |
37 ) { |
| 38 char c[]; |
38 if (needed_capacity <= pool->capacity) return 0; |
| 39 }; |
39 size_t newcap = pool->capacity >= 1000 ? |
| 40 |
40 pool->capacity + 1000 : pool->capacity * 2; |
| 41 static void *cx_mempool_malloc( |
41 size_t newmsize; |
| |
42 // LCOV_EXCL_START |
| |
43 if (pool->capacity > newcap |
| |
44 || cx_szmul(newcap, sizeof(void*), &newmsize)) { |
| |
45 errno = EOVERFLOW; |
| |
46 return 1; |
| |
47 } // LCOV_EXCL_STOP |
| |
48 void **newdata = cxRealloc(pool->base_allocator, pool->data, newmsize); |
| |
49 if (newdata == NULL) return 1; |
| |
50 pool->data = newdata; |
| |
51 pool->capacity = newcap; |
| |
52 return 0; |
| |
53 } |
| |
54 |
| |
55 static int cx_mempool_ensure_registered_capacity( |
| |
56 struct cx_mempool_s *pool, |
| |
57 size_t needed_capacity |
| |
58 ) { |
| |
59 if (needed_capacity <= pool->registered_capacity) return 0; |
| |
60 // we do not expect so many registrations |
| |
61 size_t newcap = pool->registered_capacity + 8; |
| |
62 size_t newmsize; |
| |
63 // LCOV_EXCL_START |
| |
64 if (pool->registered_capacity > newcap || cx_szmul(newcap, |
| |
65 sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) { |
| |
66 errno = EOVERFLOW; |
| |
67 return 1; |
| |
68 } // LCOV_EXCL_STOP |
| |
69 void *newdata = cxRealloc(pool->base_allocator, pool->registered, newmsize); |
| |
70 if (newdata == NULL) return 1; |
| |
71 pool->registered = newdata; |
| |
72 pool->registered_capacity = newcap; |
| |
73 return 0; |
| |
74 } |
| |
75 |
| |
76 static void *cx_mempool_malloc_simple( |
| 42 void *p, |
77 void *p, |
| 43 size_t n |
78 size_t n |
| 44 ) { |
79 ) { |
| 45 struct cx_mempool_s *pool = p; |
80 struct cx_mempool_s *pool = p; |
| 46 |
81 |
| 47 if (pool->size >= pool->capacity) { |
82 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
| 48 size_t newcap = pool->capacity - (pool->capacity % 16) + 16; |
83 return NULL; // LCOV_EXCL_LINE |
| 49 size_t newmsize; |
84 } |
| 50 if (pool->capacity > newcap || cx_szmul(newcap, |
85 |
| 51 sizeof(struct cx_mempool_memory_s*), &newmsize)) { |
86 struct cx_mempool_memory_s *mem = |
| 52 errno = EOVERFLOW; |
87 cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory_s) + n); |
| 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 } |
|
| 60 |
|
| 61 struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); |
|
| 62 if (mem == NULL) return NULL; |
88 if (mem == NULL) return NULL; |
| 63 |
89 mem->destructor = NULL; |
| 64 mem->destructor = pool->auto_destr; |
|
| 65 pool->data[pool->size] = mem; |
90 pool->data[pool->size] = mem; |
| 66 pool->size++; |
91 pool->size++; |
| 67 |
92 |
| 68 return mem->c; |
93 return mem->c; |
| 69 } |
94 } |
| 70 |
95 |
| 71 static void *cx_mempool_calloc( |
96 static void *cx_mempool_calloc_simple( |
| 72 void *p, |
97 void *p, |
| 73 size_t nelem, |
98 size_t nelem, |
| 74 size_t elsize |
99 size_t elsize |
| 75 ) { |
100 ) { |
| 76 size_t msz; |
101 size_t msz; |
| 77 if (cx_szmul(nelem, elsize, &msz)) { |
102 if (cx_szmul(nelem, elsize, &msz)) { |
| 78 errno = EOVERFLOW; |
103 errno = EOVERFLOW; |
| 79 return NULL; |
104 return NULL; |
| 80 } |
105 } |
| 81 void *ptr = cx_mempool_malloc(p, msz); |
106 void *ptr = cx_mempool_malloc_simple(p, msz); |
| 82 if (ptr == NULL) return NULL; |
107 if (ptr == NULL) return NULL; |
| 83 memset(ptr, 0, nelem * elsize); |
108 memset(ptr, 0, nelem * elsize); |
| 84 return ptr; |
109 return ptr; |
| 85 } |
110 } |
| 86 |
111 |
| 87 static void *cx_mempool_realloc( |
112 static void cx_mempool_free_simple( |
| |
113 void *p, |
| |
114 void *ptr |
| |
115 ) { |
| |
116 if (!ptr) return; |
| |
117 struct cx_mempool_s *pool = p; |
| |
118 |
| |
119 struct cx_mempool_memory_s *mem = |
| |
120 (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s)); |
| |
121 |
| |
122 for (size_t i = 0; i < pool->size; i++) { |
| |
123 if (mem == pool->data[i]) { |
| |
124 if (mem->destructor) { |
| |
125 mem->destructor(mem->c); |
| |
126 } |
| |
127 if (pool->destr) { |
| |
128 pool->destr(mem->c); |
| |
129 } |
| |
130 if (pool->destr2) { |
| |
131 pool->destr2(pool->destr2_data, mem->c); |
| |
132 } |
| |
133 cxFree(pool->base_allocator, mem); |
| |
134 size_t last_index = pool->size - 1; |
| |
135 if (i != last_index) { |
| |
136 pool->data[i] = pool->data[last_index]; |
| |
137 pool->data[last_index] = NULL; |
| |
138 } |
| |
139 pool->size--; |
| |
140 return; |
| |
141 } |
| |
142 } |
| |
143 abort(); // LCOV_EXCL_LINE |
| |
144 } |
| |
145 |
| |
146 static void *cx_mempool_realloc_simple( |
| 88 void *p, |
147 void *p, |
| 89 void *ptr, |
148 void *ptr, |
| 90 size_t n |
149 size_t n |
| 91 ) { |
150 ) { |
| 92 struct cx_mempool_s *pool = p; |
151 if (ptr == NULL) { |
| 93 |
152 return cx_mempool_malloc_simple(p, n); |
| 94 struct cx_mempool_memory_s *mem, *newm; |
153 } |
| 95 mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); |
154 if (n == 0) { |
| 96 newm = realloc(mem, n + sizeof(cx_destructor_func)); |
155 cx_mempool_free_simple(p, ptr); |
| |
156 return NULL; |
| |
157 } |
| |
158 struct cx_mempool_s *pool = p; |
| |
159 |
| |
160 const unsigned overhead = sizeof(struct cx_mempool_memory_s); |
| |
161 struct cx_mempool_memory_s *mem = |
| |
162 (void *) (((char *) ptr) - overhead); |
| |
163 struct cx_mempool_memory_s *newm = |
| |
164 cxRealloc(pool->base_allocator, mem, n + overhead); |
| 97 |
165 |
| 98 if (newm == NULL) return NULL; |
166 if (newm == NULL) return NULL; |
| 99 if (mem != newm) { |
167 if (mem != newm) { |
| 100 for (size_t i = 0; i < pool->size; i++) { |
168 for (size_t i = 0; i < pool->size; i++) { |
| 101 if (pool->data[i] == mem) { |
169 if (pool->data[i] == mem) { |
| 102 pool->data[i] = newm; |
170 pool->data[i] = newm; |
| 103 return ((char*)newm) + sizeof(cx_destructor_func); |
171 return ((char*)newm) + overhead; |
| 104 } |
172 } |
| 105 } |
173 } |
| 106 abort(); // LCOV_EXCL_LINE |
174 abort(); // LCOV_EXCL_LINE |
| 107 } else { |
175 } else { |
| 108 return ptr; |
176 // unfortunately glibc() realloc seems to always move |
| 109 } |
177 return ptr; // LCOV_EXCL_LINE |
| 110 } |
178 } |
| 111 |
179 } |
| 112 static void cx_mempool_free( |
180 |
| |
181 static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) { |
| |
182 const bool has_destr = pool->destr; |
| |
183 const bool has_destr2 = pool->destr2; |
| |
184 for (size_t i = 0; i < pool->size; i++) { |
| |
185 struct cx_mempool_memory_s *mem = pool->data[i]; |
| |
186 if (mem->destructor) { |
| |
187 mem->destructor(mem->c); |
| |
188 } |
| |
189 if (has_destr) { |
| |
190 pool->destr(mem->c); |
| |
191 } |
| |
192 if (has_destr2) { |
| |
193 pool->destr2(pool->destr2_data, mem->c); |
| |
194 } |
| |
195 cxFree(pool->base_allocator, mem); |
| |
196 } |
| |
197 } |
| |
198 |
| |
199 static cx_allocator_class cx_mempool_simple_allocator_class = { |
| |
200 cx_mempool_malloc_simple, |
| |
201 cx_mempool_realloc_simple, |
| |
202 cx_mempool_calloc_simple, |
| |
203 cx_mempool_free_simple |
| |
204 }; |
| |
205 |
| |
206 static void *cx_mempool_malloc_advanced( |
| |
207 void *p, |
| |
208 size_t n |
| |
209 ) { |
| |
210 struct cx_mempool_s *pool = p; |
| |
211 |
| |
212 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
| |
213 return NULL; // LCOV_EXCL_LINE |
| |
214 } |
| |
215 |
| |
216 struct cx_mempool_memory2_s *mem = |
| |
217 cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory2_s) + n); |
| |
218 if (mem == NULL) return NULL; |
| |
219 mem->destructor = NULL; |
| |
220 mem->data = NULL; |
| |
221 pool->data[pool->size] = mem; |
| |
222 pool->size++; |
| |
223 |
| |
224 return mem->c; |
| |
225 } |
| |
226 |
| |
227 static void *cx_mempool_calloc_advanced( |
| |
228 void *p, |
| |
229 size_t nelem, |
| |
230 size_t elsize |
| |
231 ) { |
| |
232 size_t msz; |
| |
233 if (cx_szmul(nelem, elsize, &msz)) { |
| |
234 errno = EOVERFLOW; |
| |
235 return NULL; |
| |
236 } |
| |
237 void *ptr = cx_mempool_malloc_advanced(p, msz); |
| |
238 if (ptr == NULL) return NULL; |
| |
239 memset(ptr, 0, nelem * elsize); |
| |
240 return ptr; |
| |
241 } |
| |
242 |
| |
243 static void cx_mempool_free_advanced( |
| 113 void *p, |
244 void *p, |
| 114 void *ptr |
245 void *ptr |
| 115 ) { |
246 ) { |
| 116 if (!ptr) return; |
247 if (!ptr) return; |
| 117 struct cx_mempool_s *pool = p; |
248 struct cx_mempool_s *pool = p; |
| 118 |
249 |
| 119 struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) |
250 struct cx_mempool_memory2_s *mem = |
| 120 ((char *) ptr - sizeof(cx_destructor_func)); |
251 (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s)); |
| 121 |
252 |
| 122 for (size_t i = 0; i < pool->size; i++) { |
253 for (size_t i = 0; i < pool->size; i++) { |
| 123 if (mem == pool->data[i]) { |
254 if (mem == pool->data[i]) { |
| 124 if (mem->destructor) { |
255 if (mem->destructor) { |
| 125 mem->destructor(mem->c); |
256 mem->destructor(mem->data, mem->c); |
| 126 } |
257 } |
| 127 free(mem); |
258 if (pool->destr) { |
| |
259 pool->destr(mem->c); |
| |
260 } |
| |
261 if (pool->destr2) { |
| |
262 pool->destr2(pool->destr2_data, mem->c); |
| |
263 } |
| |
264 cxFree(pool->base_allocator, mem); |
| 128 size_t last_index = pool->size - 1; |
265 size_t last_index = pool->size - 1; |
| 129 if (i != last_index) { |
266 if (i != last_index) { |
| 130 pool->data[i] = pool->data[last_index]; |
267 pool->data[i] = pool->data[last_index]; |
| 131 pool->data[last_index] = NULL; |
268 pool->data[last_index] = NULL; |
| 132 } |
269 } |
| 135 } |
272 } |
| 136 } |
273 } |
| 137 abort(); // LCOV_EXCL_LINE |
274 abort(); // LCOV_EXCL_LINE |
| 138 } |
275 } |
| 139 |
276 |
| |
277 static void *cx_mempool_realloc_advanced( |
| |
278 void *p, |
| |
279 void *ptr, |
| |
280 size_t n |
| |
281 ) { |
| |
282 if (ptr == NULL) { |
| |
283 return cx_mempool_malloc_advanced(p, n); |
| |
284 } |
| |
285 if (n == 0) { |
| |
286 cx_mempool_free_advanced(p, ptr); |
| |
287 return NULL; |
| |
288 } |
| |
289 struct cx_mempool_s *pool = p; |
| |
290 |
| |
291 const unsigned overhead = sizeof(struct cx_mempool_memory2_s); |
| |
292 struct cx_mempool_memory2_s *mem = |
| |
293 (void *) (((char *) ptr) - overhead); |
| |
294 struct cx_mempool_memory2_s *newm = |
| |
295 cxRealloc(pool->base_allocator, mem, n + overhead); |
| |
296 |
| |
297 if (newm == NULL) return NULL; |
| |
298 if (mem != newm) { |
| |
299 for (size_t i = 0; i < pool->size; i++) { |
| |
300 if (pool->data[i] == mem) { |
| |
301 pool->data[i] = newm; |
| |
302 return ((char*)newm) + overhead; |
| |
303 } |
| |
304 } |
| |
305 abort(); // LCOV_EXCL_LINE |
| |
306 } else { |
| |
307 // unfortunately glibc() realloc seems to always move |
| |
308 return ptr; // LCOV_EXCL_LINE |
| |
309 } |
| |
310 } |
| |
311 |
| |
312 static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) { |
| |
313 const bool has_destr = pool->destr; |
| |
314 const bool has_destr2 = pool->destr2; |
| |
315 for (size_t i = 0; i < pool->size; i++) { |
| |
316 struct cx_mempool_memory2_s *mem = pool->data[i]; |
| |
317 if (mem->destructor) { |
| |
318 mem->destructor(mem->data, mem->c); |
| |
319 } |
| |
320 if (has_destr) { |
| |
321 pool->destr(mem->c); |
| |
322 } |
| |
323 if (has_destr2) { |
| |
324 pool->destr2(pool->destr2_data, mem->c); |
| |
325 } |
| |
326 cxFree(pool->base_allocator, mem); |
| |
327 } |
| |
328 } |
| |
329 |
| |
330 static cx_allocator_class cx_mempool_advanced_allocator_class = { |
| |
331 cx_mempool_malloc_advanced, |
| |
332 cx_mempool_realloc_advanced, |
| |
333 cx_mempool_calloc_advanced, |
| |
334 cx_mempool_free_advanced |
| |
335 }; |
| |
336 |
| |
337 |
| |
338 static void *cx_mempool_malloc_pure( |
| |
339 void *p, |
| |
340 size_t n |
| |
341 ) { |
| |
342 struct cx_mempool_s *pool = p; |
| |
343 |
| |
344 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
| |
345 return NULL; // LCOV_EXCL_LINE |
| |
346 } |
| |
347 |
| |
348 void *mem = cxMalloc(pool->base_allocator, n); |
| |
349 if (mem == NULL) return NULL; |
| |
350 pool->data[pool->size] = mem; |
| |
351 pool->size++; |
| |
352 |
| |
353 return mem; |
| |
354 } |
| |
355 |
| |
356 static void *cx_mempool_calloc_pure( |
| |
357 void *p, |
| |
358 size_t nelem, |
| |
359 size_t elsize |
| |
360 ) { |
| |
361 size_t msz; |
| |
362 if (cx_szmul(nelem, elsize, &msz)) { |
| |
363 errno = EOVERFLOW; |
| |
364 return NULL; |
| |
365 } |
| |
366 void *ptr = cx_mempool_malloc_pure(p, msz); |
| |
367 if (ptr == NULL) return NULL; |
| |
368 memset(ptr, 0, nelem * elsize); |
| |
369 return ptr; |
| |
370 } |
| |
371 |
| |
372 static void cx_mempool_free_pure( |
| |
373 void *p, |
| |
374 void *ptr |
| |
375 ) { |
| |
376 if (!ptr) return; |
| |
377 struct cx_mempool_s *pool = p; |
| |
378 |
| |
379 for (size_t i = 0; i < pool->size; i++) { |
| |
380 if (ptr == pool->data[i]) { |
| |
381 if (pool->destr) { |
| |
382 pool->destr(ptr); |
| |
383 } |
| |
384 if (pool->destr2) { |
| |
385 pool->destr2(pool->destr2_data, ptr); |
| |
386 } |
| |
387 cxFree(pool->base_allocator, ptr); |
| |
388 size_t last_index = pool->size - 1; |
| |
389 if (i != last_index) { |
| |
390 pool->data[i] = pool->data[last_index]; |
| |
391 pool->data[last_index] = NULL; |
| |
392 } |
| |
393 pool->size--; |
| |
394 return; |
| |
395 } |
| |
396 } |
| |
397 abort(); // LCOV_EXCL_LINE |
| |
398 } |
| |
399 |
| |
400 static void *cx_mempool_realloc_pure( |
| |
401 void *p, |
| |
402 void *ptr, |
| |
403 size_t n |
| |
404 ) { |
| |
405 if (ptr == NULL) { |
| |
406 return cx_mempool_malloc_pure(p, n); |
| |
407 } |
| |
408 if (n == 0) { |
| |
409 cx_mempool_free_pure(p, ptr); |
| |
410 return NULL; |
| |
411 } |
| |
412 struct cx_mempool_s *pool = p; |
| |
413 void *newm = cxRealloc(pool->base_allocator, ptr, n); |
| |
414 if (newm == NULL) return NULL; |
| |
415 if (ptr != newm) { |
| |
416 for (size_t i = 0; i < pool->size; i++) { |
| |
417 if (pool->data[i] == ptr) { |
| |
418 pool->data[i] = newm; |
| |
419 return newm; |
| |
420 } |
| |
421 } |
| |
422 abort(); // LCOV_EXCL_LINE |
| |
423 } else { |
| |
424 // unfortunately glibc() realloc seems to always move |
| |
425 return ptr; // LCOV_EXCL_LINE |
| |
426 } |
| |
427 } |
| |
428 |
| |
429 static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) { |
| |
430 const bool has_destr = pool->destr; |
| |
431 const bool has_destr2 = pool->destr2; |
| |
432 for (size_t i = 0; i < pool->size; i++) { |
| |
433 void *mem = pool->data[i]; |
| |
434 if (has_destr) { |
| |
435 pool->destr(mem); |
| |
436 } |
| |
437 if (has_destr2) { |
| |
438 pool->destr2(pool->destr2_data, mem); |
| |
439 } |
| |
440 cxFree(pool->base_allocator, mem); |
| |
441 } |
| |
442 } |
| |
443 |
| |
444 static cx_allocator_class cx_mempool_pure_allocator_class = { |
| |
445 cx_mempool_malloc_pure, |
| |
446 cx_mempool_realloc_pure, |
| |
447 cx_mempool_calloc_pure, |
| |
448 cx_mempool_free_pure |
| |
449 }; |
| |
450 |
| |
451 static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) { |
| |
452 for (size_t i = 0; i < pool->registered_size; i++) { |
| |
453 struct cx_mempool_foreign_memory_s info = pool->registered[i]; |
| |
454 if (info.destr2_data == NULL) { |
| |
455 if (info.destr) { |
| |
456 info.destr(info.mem); |
| |
457 } |
| |
458 } else { |
| |
459 info.destr2(info.destr2_data, info.mem); |
| |
460 } |
| |
461 } |
| |
462 } |
| |
463 |
| 140 void cxMempoolFree(CxMempool *pool) { |
464 void cxMempoolFree(CxMempool *pool) { |
| 141 if (pool == NULL) return; |
465 if (pool == NULL) return; |
| 142 struct cx_mempool_memory_s *mem; |
466 if (pool->allocator->cl == &cx_mempool_simple_allocator_class) { |
| 143 for (size_t i = 0; i < pool->size; i++) { |
467 cx_mempool_free_all_simple(pool); |
| 144 mem = pool->data[i]; |
468 } else if (pool->allocator->cl == &cx_mempool_advanced_allocator_class) { |
| 145 if (mem->destructor) { |
469 cx_mempool_free_all_advanced(pool); |
| 146 mem->destructor(mem->c); |
470 } else { |
| 147 } |
471 cx_mempool_free_all_pure(pool); |
| 148 free(mem); |
472 } |
| 149 } |
473 cx_mempool_free_foreign(pool); |
| 150 free(pool->data); |
474 cxFree(pool->base_allocator, pool->data); |
| 151 free((void*) pool->allocator); |
475 cxFree(pool->base_allocator, pool->registered); |
| 152 free(pool); |
476 cxFree(pool->base_allocator, (void*) pool->allocator); |
| |
477 cxFree(pool->base_allocator, pool); |
| 153 } |
478 } |
| 154 |
479 |
| 155 void cxMempoolSetDestructor( |
480 void cxMempoolSetDestructor( |
| 156 void *ptr, |
481 void *ptr, |
| 157 cx_destructor_func func |
482 cx_destructor_func func |
| 158 ) { |
483 ) { |
| 159 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; |
484 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; |
| 160 } |
485 } |
| 161 |
486 |
| |
487 void cxMempoolSetDestructor2( |
| |
488 void *ptr, |
| |
489 cx_destructor_func2 func, |
| |
490 void *data |
| |
491 ) { |
| |
492 struct cx_mempool_memory2_s *info = |
| |
493 (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s)); |
| |
494 info->destructor = func; |
| |
495 info->data = data; |
| |
496 } |
| |
497 |
| 162 void cxMempoolRemoveDestructor(void *ptr) { |
498 void cxMempoolRemoveDestructor(void *ptr) { |
| 163 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; |
499 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; |
| 164 } |
500 } |
| 165 |
501 |
| 166 struct cx_mempool_foreign_mem_s { |
502 void cxMempoolRemoveDestructor2(void *ptr) { |
| 167 cx_destructor_func destr; |
503 struct cx_mempool_memory2_s *info = |
| 168 void* mem; |
504 (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s)); |
| 169 }; |
505 info->destructor = NULL; |
| 170 |
506 info->data = NULL; |
| 171 static void cx_mempool_destr_foreign_mem(void* ptr) { |
|
| 172 struct cx_mempool_foreign_mem_s *fm = ptr; |
|
| 173 fm->destr(fm->mem); |
|
| 174 } |
507 } |
| 175 |
508 |
| 176 int cxMempoolRegister( |
509 int cxMempoolRegister( |
| 177 CxMempool *pool, |
510 CxMempool *pool, |
| 178 void *memory, |
511 void *memory, |
| 179 cx_destructor_func destr |
512 cx_destructor_func destr |
| 180 ) { |
513 ) { |
| 181 struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( |
514 if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) { |
| 182 pool, |
515 return 1; // LCOV_EXCL_LINE |
| 183 sizeof(struct cx_mempool_foreign_mem_s) |
516 } |
| 184 ); |
517 |
| 185 if (fm == NULL) return 1; |
518 pool->registered[pool->registered_size++] = |
| 186 |
519 (struct cx_mempool_foreign_memory_s) { |
| 187 fm->mem = memory; |
520 .mem = memory, |
| 188 fm->destr = destr; |
521 .destr = destr, |
| 189 *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; |
522 .destr2_data = NULL |
| |
523 }; |
| 190 |
524 |
| 191 return 0; |
525 return 0; |
| 192 } |
526 } |
| 193 |
527 |
| 194 static cx_allocator_class cx_mempool_allocator_class = { |
528 int cxMempoolRegister2( |
| 195 cx_mempool_malloc, |
529 CxMempool *pool, |
| 196 cx_mempool_realloc, |
530 void *memory, |
| 197 cx_mempool_calloc, |
531 cx_destructor_func2 destr, |
| 198 cx_mempool_free |
532 void *data |
| 199 }; |
533 ) { |
| |
534 if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) { |
| |
535 return 1; // LCOV_EXCL_LINE |
| |
536 } |
| |
537 |
| |
538 pool->registered[pool->registered_size++] = |
| |
539 (struct cx_mempool_foreign_memory_s) { |
| |
540 .mem = memory, |
| |
541 .destr2 = destr, |
| |
542 .destr2_data = data |
| |
543 }; |
| |
544 |
| |
545 return 0; |
| |
546 } |
| 200 |
547 |
| 201 CxMempool *cxMempoolCreate( |
548 CxMempool *cxMempoolCreate( |
| 202 size_t capacity, |
549 size_t capacity, |
| 203 cx_destructor_func destr |
550 enum cx_mempool_type type |
| 204 ) { |
551 ) { |
| |
552 if (capacity == 0) capacity = 16; |
| 205 size_t poolsize; |
553 size_t poolsize; |
| 206 if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { |
554 if (cx_szmul(capacity, sizeof(void*), &poolsize)) { |
| |
555 // LCOV_EXCL_START |
| 207 errno = EOVERFLOW; |
556 errno = EOVERFLOW; |
| 208 return NULL; |
557 return NULL; |
| 209 } |
558 } // LCOV_EXCL_STOP |
| 210 |
559 |
| 211 struct cx_mempool_s *pool = |
560 CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator)); |
| 212 malloc(sizeof(struct cx_mempool_s)); |
|
| 213 if (pool == NULL) return NULL; |
|
| 214 |
|
| 215 CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); |
|
| 216 if (provided_allocator == NULL) { // LCOV_EXCL_START |
561 if (provided_allocator == NULL) { // LCOV_EXCL_START |
| 217 free(pool); |
|
| 218 return NULL; |
562 return NULL; |
| 219 } // LCOV_EXCL_STOP |
563 } // LCOV_EXCL_STOP |
| 220 provided_allocator->cl = &cx_mempool_allocator_class; |
564 |
| |
565 CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool)); |
| |
566 if (pool == NULL) { // LCOV_EXCL_START |
| |
567 cxFreeDefault(provided_allocator); |
| |
568 return NULL; |
| |
569 } // LCOV_EXCL_STOP |
| |
570 |
| 221 provided_allocator->data = pool; |
571 provided_allocator->data = pool; |
| 222 |
572 *((const CxAllocator**)&pool->base_allocator) = cxDefaultAllocator; |
| 223 pool->allocator = provided_allocator; |
573 pool->allocator = provided_allocator; |
| 224 |
574 if (type == CX_MEMPOOL_TYPE_SIMPLE) { |
| 225 pool->data = malloc(poolsize); |
575 provided_allocator->cl = &cx_mempool_simple_allocator_class; |
| |
576 } else if (type == CX_MEMPOOL_TYPE_ADVANCED) { |
| |
577 provided_allocator->cl = &cx_mempool_advanced_allocator_class; |
| |
578 } else { |
| |
579 provided_allocator->cl = &cx_mempool_pure_allocator_class; |
| |
580 } |
| |
581 |
| |
582 pool->data = cxMallocDefault(poolsize); |
| 226 if (pool->data == NULL) { // LCOV_EXCL_START |
583 if (pool->data == NULL) { // LCOV_EXCL_START |
| 227 free(provided_allocator); |
584 cxFreeDefault(provided_allocator); |
| 228 free(pool); |
585 cxFreeDefault(pool); |
| 229 return NULL; |
586 return NULL; |
| 230 } // LCOV_EXCL_STOP |
587 } // LCOV_EXCL_STOP |
| 231 |
588 |
| 232 pool->size = 0; |
589 pool->size = 0; |
| 233 pool->capacity = capacity; |
590 pool->capacity = capacity; |
| 234 pool->auto_destr = destr; |
|
| 235 |
591 |
| 236 return pool; |
592 return pool; |
| 237 } |
593 } |
| |
594 |
| |
595 void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc) { |
| |
596 pool->destr = fnc; |
| |
597 } |
| |
598 |
| |
599 void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data) { |
| |
600 pool->destr2 = fnc; |
| |
601 pool->destr2_data = data; |
| |
602 } |
| |
603 |
| |
604 static void cx_mempool_free_transferred_allocator(void *base_al, void *al) { |
| |
605 cxFree(base_al, al); |
| |
606 } |
| |
607 |
| |
608 int cxMempoolTransfer( |
| |
609 CxMempool *source, |
| |
610 CxMempool *dest |
| |
611 ) { |
| |
612 // safety checks |
| |
613 if (source == dest) return 1; |
| |
614 if (source->allocator->cl != dest->allocator->cl) return 1; |
| |
615 if (source->base_allocator->cl != dest->base_allocator->cl) return 1; |
| |
616 |
| |
617 // ensure enough capacity in the destination pool |
| |
618 if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) { |
| |
619 return 1; // LCOV_EXCL_LINE |
| |
620 } |
| |
621 if (cx_mempool_ensure_registered_capacity(dest, |
| |
622 dest->registered_size + source->registered_size)) { |
| |
623 return 1; // LCOV_EXCL_LINE |
| |
624 } |
| |
625 |
| |
626 // allocate a replacement allocator for the source pool |
| |
627 CxAllocator *new_source_allocator = |
| |
628 cxMalloc(source->base_allocator, sizeof(CxAllocator)); |
| |
629 if (new_source_allocator == NULL) { // LCOV_EXCL_START |
| |
630 return 1; |
| |
631 } // LCOV_EXCL_STOP |
| |
632 new_source_allocator->cl = source->allocator->cl; |
| |
633 new_source_allocator->data = source; |
| |
634 |
| |
635 // transfer all the data |
| |
636 if (source->size > 0) { |
| |
637 memcpy(&dest->data[dest->size], source->data, |
| |
638 sizeof(void*)*source->size); |
| |
639 dest->size += source->size; |
| |
640 } |
| |
641 |
| |
642 // transfer all registered memory |
| |
643 if (source->registered_size > 0) { |
| |
644 memcpy(&dest->registered[dest->registered_size], source->registered, |
| |
645 sizeof(struct cx_mempool_foreign_memory_s) |
| |
646 * source->registered_size); |
| |
647 dest->registered_size += source->registered_size; |
| |
648 } |
| |
649 |
| |
650 // register the old allocator with the new pool |
| |
651 // we have to remove const-ness for this, but that's okay here |
| |
652 // also register the base allocator, s.t. the pool knows how to free it |
| |
653 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; |
| |
654 transferred_allocator->data = dest; |
| |
655 cxMempoolRegister2(dest, transferred_allocator, |
| |
656 cx_mempool_free_transferred_allocator, (void*)source->base_allocator); |
| |
657 |
| |
658 // prepare the source pool for re-use |
| |
659 source->allocator = new_source_allocator; |
| |
660 memset(source->data, 0, source->size * sizeof(void*)); |
| |
661 memset(source->registered, 0, |
| |
662 source->registered_size * sizeof(struct cx_mempool_foreign_memory_s)); |
| |
663 source->size = 0; |
| |
664 source->registered_size = 0; |
| |
665 |
| |
666 return 0; |
| |
667 } |
| |
668 |
| |
669 int cxMempoolTransferObject( |
| |
670 CxMempool *source, |
| |
671 CxMempool *dest, |
| |
672 const void *obj |
| |
673 ) { |
| |
674 // safety checks |
| |
675 if (source == dest) return 1; |
| |
676 if (source->allocator->cl != dest->allocator->cl) return 1; |
| |
677 if (source->base_allocator->cl != dest->base_allocator->cl) return 1; |
| |
678 |
| |
679 // search for the object |
| |
680 for (size_t i = 0; i < source->size; i++) { |
| |
681 struct cx_mempool_memory_s *mem = source->data[i]; |
| |
682 if (mem->c == obj) { |
| |
683 // first, make sure that the dest pool can take the object |
| |
684 if (cx_mempool_ensure_capacity(dest, dest->size + 1)) { |
| |
685 return 1; // LCOV_EXCL_LINE |
| |
686 } |
| |
687 // remove from the source pool |
| |
688 size_t last_index = source->size - 1; |
| |
689 if (i != last_index) { |
| |
690 source->data[i] = source->data[last_index]; |
| |
691 source->data[last_index] = NULL; |
| |
692 } |
| |
693 source->size--; |
| |
694 // add to the target pool |
| |
695 dest->data[dest->size++] = mem; |
| |
696 return 0; |
| |
697 } |
| |
698 } |
| |
699 // search in the registered objects |
| |
700 for (size_t i = 0; i < source->registered_size; i++) { |
| |
701 struct cx_mempool_foreign_memory_s *mem = &source->registered[i]; |
| |
702 if (mem->mem == obj) { |
| |
703 // first, make sure that the dest pool can take the object |
| |
704 if (cx_mempool_ensure_registered_capacity(dest, |
| |
705 dest->registered_size + 1)) { |
| |
706 return 1; // LCOV_EXCL_LINE |
| |
707 } |
| |
708 dest->registered[dest->registered_size++] = *mem; |
| |
709 // remove from the source pool |
| |
710 size_t last_index = source->registered_size - 1; |
| |
711 if (i != last_index) { |
| |
712 source->registered[i] = source->registered[last_index]; |
| |
713 memset(&source->registered[last_index], 0, |
| |
714 sizeof(struct cx_mempool_foreign_memory_s)); |
| |
715 } |
| |
716 source->registered_size--; |
| |
717 return 0; |
| |
718 } |
| |
719 } |
| |
720 // not found |
| |
721 return 1; |
| |
722 } |