GCC Code Coverage Report


Directory: ./
File: mempool.c
Date: 2025-12-31 16:19:05
Exec Total Coverage
Lines: 341 341 100.0%
Functions: 31 31 100.0%
Branches: 149 192 77.6%

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