GCC Code Coverage Report


Directory: ./
File: map.c
Date: 2025-12-31 16:19:05
Exec Total Coverage
Lines: 177 177 100.0%
Functions: 29 29 100.0%
Branches: 102 120 85.0%

Line Branch Exec Source
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2023 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/map.h"
30 #include <string.h>
31
32 #include "cx/list.h"
33
34 // <editor-fold desc="empty map implementation">
35
36 2 static void cx_empty_map_noop(CX_UNUSED CxMap *map) {
37 // this is a noop, but MUST be implemented
38 2 }
39
40 1 static void *cx_empty_map_get(
41 CX_UNUSED const CxMap *map,
42 CX_UNUSED CxHashKey key
43 ) {
44 1 return NULL;
45 }
46
47 24 static bool cx_empty_map_iter_valid(CX_UNUSED const void *iter) {
48 24 return false;
49 }
50
51 12 static CxMapIterator cx_empty_map_iterator(
52 const struct cx_map_s *map,
53 CX_UNUSED enum cx_map_iterator_type type
54 ) {
55 12 CxMapIterator iter = {0};
56 12 iter.map = (CxMap*) map;
57 12 iter.base.valid = cx_empty_map_iter_valid;
58 12 return iter;
59 }
60
61 static struct cx_map_class_s cx_empty_map_class = {
62 cx_empty_map_noop,
63 cx_empty_map_noop,
64 NULL,
65 cx_empty_map_get,
66 NULL,
67 cx_empty_map_iterator
68 };
69
70 CxMap cx_empty_map = {
71 {
72 NULL,
73 0,
74 0,
75 NULL,
76 NULL,
77 NULL,
78 NULL,
79 NULL,
80 NULL,
81 false,
82 true
83 },
84 &cx_empty_map_class
85 };
86
87 CxMap *const cxEmptyMap = &cx_empty_map;
88
89 // </editor-fold>
90
91 10 void cxMapClear(CxMap *map) {
92 10 map->cl->clear(map);
93 10 }
94
95 489 size_t cxMapSize(const CxMap *map) {
96 489 return map->collection.size;
97 }
98
99 28 CxMapIterator cxMapIteratorValues(const CxMap *map) {
100
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 26 times.
28 if (map == NULL) map = cxEmptyMap;
101 28 return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
102 }
103
104 27 CxMapIterator cxMapIteratorKeys(const CxMap *map) {
105
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 25 times.
27 if (map == NULL) map = cxEmptyMap;
106 27 return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
107 }
108
109 254 CxMapIterator cxMapIterator(const CxMap *map) {
110
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 252 times.
254 if (map == NULL) map = cxEmptyMap;
111 254 return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
112 }
113
114 1038 int cx_map_put(CxMap *map, CxHashKey key, void *value) {
115 1038 return map->cl->put(map, key, value).key == NULL;
116 }
117
118 187 void *cx_map_emplace(CxMap *map, CxHashKey key) {
119 187 const CxMapEntry entry = map->cl->put(map, key, NULL);
120
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 187 times.
187 if (entry.key == NULL) return NULL;
121 187 return entry.value;
122 }
123
124 634 void *cx_map_get(const CxMap *map, CxHashKey key) {
125 634 return map->cl->get(map, key);
126 }
127
128 44 int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
129 44 return map->cl->remove(map, key, targetbuf);
130 }
131
132 251 void cxMapFree(CxMap *map) {
133
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 251 times.
251 if (map == NULL) return;
134 251 map->cl->deallocate(map);
135 }
136
137 6 static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) {
138 6 cx_destructor_func destr_bak = map->collection.simple_destructor;
139 6 cx_destructor_func2 destr2_bak = map->collection.advanced_destructor;
140 6 map->collection.simple_destructor = NULL;
141 6 map->collection.advanced_destructor = NULL;
142 6 cxMapRemove(map, key);
143 6 map->collection.simple_destructor = destr_bak;
144 6 map->collection.advanced_destructor = destr2_bak;
145 6 }
146
147 15 static void* cx_map_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
148 15 size_t elem_size = *(size_t*)data;
149
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 15 times.
15 if (dst == NULL) dst = cxMalloc(al, elem_size);
150
1/2
✓ Branch 0 (4→5) taken 15 times.
✗ Branch 1 (4→6) not taken.
15 if (dst != NULL) memcpy(dst, src, elem_size);
151 15 return dst;
152 }
153
154 #define use_shallow_clone_func(map) cx_map_shallow_clone_func, NULL, (void*)&((map)->collection.elem_size)
155
156 26 int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func,
157 const CxAllocator *clone_allocator, void *data) {
158
2/2
✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 23 times.
26 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
159 26 CxMapIterator src_iter = cxMapIterator(src);
160
2/2
✓ Branch 0 (24→6) taken 138 times.
✓ Branch 1 (24→25) taken 25 times.
163 for (size_t i = 0; i < cxMapSize(src); i++) {
161 138 const CxMapEntry *entry = cxIteratorCurrent(src_iter);
162 276 void **dst_mem = cxMapEmplace(dst, *(entry->key));
163
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→12) taken 138 times.
138 if (dst_mem == NULL) {
164 return 1; // LCOV_EXCL_LINE
165 }
166
2/2
✓ Branch 0 (12→13) taken 128 times.
✓ Branch 1 (12→14) taken 10 times.
138 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
167 138 void *dst_ptr = clone_func(target, entry->value, clone_allocator, data);
168
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→19) taken 137 times.
138 if (dst_ptr == NULL) {
169 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
170 1 return 1;
171 }
172
2/2
✓ Branch 0 (19→20) taken 128 times.
✓ Branch 1 (19→21) taken 9 times.
137 if (cxCollectionStoresPointers(dst)) {
173 128 *dst_mem = dst_ptr;
174 }
175 137 cxIteratorNext(src_iter);
176 }
177 25 return 0;
178 }
179
180 5 int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
181 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
182
2/2
✓ Branch 0 (2→3) taken 4 times.
✓ Branch 1 (2→4) taken 1 times.
5 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
183
184 5 CxMapIterator src_iter = cxMapIterator(minuend);
185
3/4
✓ Branch 0 (27→28) taken 17 times.
✓ Branch 1 (27→30) taken 4 times.
✓ Branch 2 (29→6) taken 17 times.
✗ Branch 3 (29→30) not taken.
21 cx_foreach(const CxMapEntry *, entry, src_iter) {
186
2/2
✓ Branch 0 (9→10) taken 7 times.
✓ Branch 1 (9→11) taken 10 times.
34 if (cxMapContains(subtrahend, *entry->key)) {
187 7 continue;
188 }
189 20 void** dst_mem = cxMapEmplace(dst, *entry->key);
190
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 10 times.
10 if (dst_mem == NULL) {
191 return 1; // LCOV_EXCL_LINE
192 }
193
2/2
✓ Branch 0 (16→17) taken 4 times.
✓ Branch 1 (16→18) taken 6 times.
10 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
194 10 void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);
195
2/2
✓ Branch 0 (20→21) taken 1 times.
✓ Branch 1 (20→23) taken 9 times.
10 if (dst_ptr == NULL) {
196 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
197 1 return 1;
198 }
199
2/2
✓ Branch 0 (23→24) taken 4 times.
✓ Branch 1 (23→25) taken 5 times.
9 if (cxCollectionStoresPointers(dst)) {
200 4 *dst_mem = dst_ptr;
201 }
202 }
203 4 return 0;
204 }
205
206 4 int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
207 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
208
1/2
✓ Branch 0 (2→3) taken 4 times.
✗ Branch 1 (2→4) not taken.
4 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
209
210 4 CxMapIterator src_iter = cxMapIterator(src);
211
3/4
✓ Branch 0 (25→26) taken 13 times.
✓ Branch 1 (25→28) taken 3 times.
✓ Branch 2 (27→6) taken 13 times.
✗ Branch 3 (27→28) not taken.
16 cx_foreach(const CxMapEntry *, entry, src_iter) {
212
2/2
✓ Branch 0 (7→8) taken 4 times.
✓ Branch 1 (7→9) taken 9 times.
13 if (cxListContains(keys, entry->key)) {
213 4 continue;
214 }
215 18 void** dst_mem = cxMapEmplace(dst, *entry->key);
216
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→14) taken 9 times.
9 if (dst_mem == NULL) {
217 return 1; // LCOV_EXCL_LINE
218 }
219
2/2
✓ Branch 0 (14→15) taken 2 times.
✓ Branch 1 (14→16) taken 7 times.
9 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
220 9 void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);
221
2/2
✓ Branch 0 (18→19) taken 1 times.
✓ Branch 1 (18→21) taken 8 times.
9 if (dst_ptr == NULL) {
222 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
223 1 return 1;
224 }
225
2/2
✓ Branch 0 (21→22) taken 2 times.
✓ Branch 1 (21→23) taken 6 times.
8 if (cxCollectionStoresPointers(dst)) {
226 2 *dst_mem = dst_ptr;
227 }
228 }
229 3 return 0;
230 }
231
232 5 int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
233 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
234
2/2
✓ Branch 0 (2→3) taken 4 times.
✓ Branch 1 (2→4) taken 1 times.
5 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
235
236 5 CxMapIterator src_iter = cxMapIterator(src);
237
3/4
✓ Branch 0 (27→28) taken 17 times.
✓ Branch 1 (27→30) taken 4 times.
✓ Branch 2 (29→6) taken 17 times.
✗ Branch 3 (29→30) not taken.
21 cx_foreach(const CxMapEntry *, entry, src_iter) {
238
2/2
✓ Branch 0 (9→10) taken 8 times.
✓ Branch 1 (9→11) taken 9 times.
34 if (!cxMapContains(other, *entry->key)) {
239 8 continue;
240 }
241 18 void** dst_mem = cxMapEmplace(dst, *entry->key);
242
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 9 times.
9 if (dst_mem == NULL) {
243 return 1; // LCOV_EXCL_LINE
244 }
245
2/2
✓ Branch 0 (16→17) taken 4 times.
✓ Branch 1 (16→18) taken 5 times.
9 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
246 9 void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);
247
2/2
✓ Branch 0 (20→21) taken 1 times.
✓ Branch 1 (20→23) taken 8 times.
9 if (dst_ptr == NULL) {
248 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
249 1 return 1;
250 }
251
2/2
✓ Branch 0 (23→24) taken 4 times.
✓ Branch 1 (23→25) taken 4 times.
8 if (cxCollectionStoresPointers(dst)) {
252 4 *dst_mem = dst_ptr;
253 }
254 }
255 4 return 0;
256 }
257
258 4 int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
259 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
260
1/2
✓ Branch 0 (2→3) taken 4 times.
✗ Branch 1 (2→4) not taken.
4 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
261
262 4 CxMapIterator src_iter = cxMapIterator(src);
263
3/4
✓ Branch 0 (25→26) taken 14 times.
✓ Branch 1 (25→28) taken 3 times.
✓ Branch 2 (27→6) taken 14 times.
✗ Branch 3 (27→28) not taken.
17 cx_foreach(const CxMapEntry *, entry, src_iter) {
264
2/2
✓ Branch 0 (7→8) taken 6 times.
✓ Branch 1 (7→9) taken 8 times.
14 if (!cxListContains(keys, entry->key)) {
265 6 continue;
266 }
267 16 void** dst_mem = cxMapEmplace(dst, *entry->key);
268
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→14) taken 8 times.
8 if (dst_mem == NULL) {
269 return 1; // LCOV_EXCL_LINE
270 }
271
2/2
✓ Branch 0 (14→15) taken 2 times.
✓ Branch 1 (14→16) taken 6 times.
8 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
272 8 void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);
273
2/2
✓ Branch 0 (18→19) taken 1 times.
✓ Branch 1 (18→21) taken 7 times.
8 if (dst_ptr == NULL) {
274 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
275 1 return 1;
276 }
277
2/2
✓ Branch 0 (21→22) taken 2 times.
✓ Branch 1 (21→23) taken 5 times.
7 if (cxCollectionStoresPointers(dst)) {
278 2 *dst_mem = dst_ptr;
279 }
280 }
281 3 return 0;
282 }
283
284 4 int cxMapUnion(CxMap *dst, const CxMap *src,
285 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
286
2/2
✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 1 times.
4 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
287
288 4 CxMapIterator src_iter = cxMapIterator(src);
289
3/4
✓ Branch 0 (27→28) taken 13 times.
✓ Branch 1 (27→30) taken 3 times.
✓ Branch 2 (29→6) taken 13 times.
✗ Branch 3 (29→30) not taken.
16 cx_foreach(const CxMapEntry *, entry, src_iter) {
290
2/2
✓ Branch 0 (9→10) taken 4 times.
✓ Branch 1 (9→11) taken 9 times.
26 if (cxMapContains(dst, *entry->key)) {
291 4 continue;
292 }
293 18 void** dst_mem = cxMapEmplace(dst, *entry->key);
294
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 9 times.
9 if (dst_mem == NULL) {
295 return 1; // LCOV_EXCL_LINE
296 }
297
2/2
✓ Branch 0 (16→17) taken 2 times.
✓ Branch 1 (16→18) taken 7 times.
9 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
298 9 void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);
299
2/2
✓ Branch 0 (20→21) taken 1 times.
✓ Branch 1 (20→23) taken 8 times.
9 if (dst_ptr == NULL) {
300 1 cx_map_remove_uninitialized_entry(dst, *(entry->key));
301 1 return 1;
302 }
303
2/2
✓ Branch 0 (23→24) taken 2 times.
✓ Branch 1 (23→25) taken 6 times.
8 if (cxCollectionStoresPointers(dst)) {
304 2 *dst_mem = dst_ptr;
305 }
306 }
307 3 return 0;
308 }
309
310 1 int cxMapCloneShallow(CxMap *dst, const CxMap *src) {
311 1 return cxMapClone(dst, src, use_shallow_clone_func(src));
312 }
313
314 1 int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) {
315 1 return cxMapDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend));
316 }
317
318 1 int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys) {
319 1 return cxMapListDifference(dst, src, keys, use_shallow_clone_func(src));
320 }
321
322 1 int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other) {
323 1 return cxMapIntersection(dst, src, other, use_shallow_clone_func(src));
324 }
325
326 1 int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys) {
327 1 return cxMapListIntersection(dst, src, keys, use_shallow_clone_func(src));
328 }
329
330 1 int cxMapUnionShallow(CxMap *dst, const CxMap *src) {
331 1 return cxMapUnion(dst, src, use_shallow_clone_func(src));
332 }
333
334 125 int cxMapCompare(const CxMap *map, const CxMap *other) {
335 // compare map sizes
336 125 const size_t size_left = cxMapSize(map);
337 125 const size_t size_right = cxMapSize(other);
338
2/2
✓ Branch 0 (4→5) taken 10 times.
✓ Branch 1 (4→6) taken 115 times.
125 if (size_left < size_right) {
339 10 return -1;
340
2/2
✓ Branch 0 (6→7) taken 8 times.
✓ Branch 1 (6→8) taken 107 times.
115 } else if (size_left > size_right) {
341 8 return 1;
342 }
343
344 // iterate through the first map
345 107 CxMapIterator iter = cxMapIterator(map);
346
3/4
✓ Branch 0 (22→23) taken 297 times.
✓ Branch 1 (22→25) taken 87 times.
✓ Branch 2 (24→10) taken 297 times.
✗ Branch 3 (24→25) not taken.
384 cx_foreach(const CxMapEntry *, entry, iter) {
347 297 const void *value_left = entry->value;
348 594 const void *value_right = cxMapGet(other, *entry->key);
349 // if the other map does not have the key, we are done
350
2/2
✓ Branch 0 (13→14) taken 8 times.
✓ Branch 1 (13→15) taken 289 times.
297 if (value_right == NULL) {
351 8 return -1;
352 }
353 // compare the values
354
2/2
✓ Branch 0 (15→16) taken 40 times.
✓ Branch 1 (15→17) taken 249 times.
289 const int d = cx_invoke_compare_func(map, value_left, value_right);
355
2/2
✓ Branch 0 (18→19) taken 12 times.
✓ Branch 1 (18→20) taken 277 times.
289 if (d != 0) {
356 12 return d;
357 }
358 }
359
360 87 return 0;
361 }
362