GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 341 / 38 / 379
Functions: 100.0% 31 / 0 / 31
Branches: 77.6% 149 / 16 / 208

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