dte test coverage


Directory: ./
File: src/util/intmap.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 115 123 93.5%
Functions: 11 11 100.0%
Branches: 43 56 76.8%

Line Branch Exec Source
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "intmap.h"
5 #include "bit.h"
6 #include "debug.h"
7 #include "hash.h"
8
9 const char tombstone[16] = "TOMBSTONE";
10
11 enum {
12 MIN_SIZE = 8,
13 };
14
15 1405 static size_t hash_key(uint32_t k)
16 {
17 1405 k = ((k >> 16) ^ k) * 0x45d9f3b;
18 1405 k = ((k >> 16) ^ k) * 0x45d9f3b;
19 1405 k = (k >> 16) ^ k;
20 1405 return k;
21 }
22
23 WARN_UNUSED_RESULT
24 29 static int intmap_resize(IntMap *map, size_t size)
25 {
26 29 BUG_ON(size < MIN_SIZE);
27 29 BUG_ON(size <= map->count);
28 29 BUG_ON(!IS_POWER_OF_2(size));
29
30 29 IntMapEntry *newtab = calloc(size, sizeof(*newtab));
31
1/2
✓ Branch 0 (8→9) taken 29 times.
✗ Branch 1 (8→21) not taken.
29 if (unlikely(!newtab)) {
32 return ENOMEM;
33 }
34
35 29 IntMapEntry *oldtab = map->entries;
36 29 size_t oldlen = map->mask + 1;
37 29 map->entries = newtab;
38 29 map->mask = size - 1;
39 29 map->tombstones = 0;
40
41
2/2
✓ Branch 0 (9→10) taken 1 times.
✓ Branch 1 (9→21) taken 28 times.
29 if (!oldtab) {
42 return 0;
43 }
44
45 // Copy the entries to the new table
46
2/2
✓ Branch 0 (19→11) taken 8 times.
✓ Branch 1 (19→20) taken 1 times.
9 for (const IntMapEntry *e = oldtab, *end = e + oldlen; e < end; e++) {
47
3/4
✓ Branch 0 (11→12) taken 8 times.
✗ Branch 1 (11→13) not taken.
✓ Branch 2 (12→13) taken 1 times.
✓ Branch 3 (12→14) taken 7 times.
8 if (!e->value || e->value == tombstone) {
48 1 continue;
49 }
50 7 IntMapEntry *newe;
51 9 for (size_t i = hash_key(e->key), j = 1; ; i += j++) {
52 9 newe = newtab + (i & map->mask);
53
2/2
✓ Branch 0 (15→16) taken 2 times.
✓ Branch 1 (15→17) taken 7 times.
9 if (!newe->key) {
54 break;
55 }
56 }
57 7 *newe = *e;
58 }
59
60 1 free(oldtab);
61 1 return 0;
62 }
63
64 WARN_UNUSED_RESULT
65 28 static int intmap_do_init(IntMap *map, size_t size)
66 {
67 // Accommodate the 75% load factor in the table size, to allow
68 // filling to the requested size without needing to resize()
69 28 size += size / 3;
70
71
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 27 times.
28 if (unlikely(size < MIN_SIZE)) {
72 1 size = MIN_SIZE;
73 }
74
75 28 size = next_pow2(size);
76
1/2
✓ Branch 0 (5→6) taken 28 times.
✗ Branch 1 (5→7) not taken.
28 if (unlikely(size == 0)) {
77 return EOVERFLOW;
78 }
79
80 28 *map = (IntMap)INTMAP_INIT;
81 28 return intmap_resize(map, size);
82 }
83
84 27 void intmap_init(IntMap *map, size_t capacity)
85 {
86 27 int err = intmap_do_init(map, capacity);
87
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 27 times.
27 if (unlikely(err)) {
88 fatal_error(__func__, err);
89 }
90 27 }
91
92 17 IntMapEntry *intmap_find(const IntMap *map, uint32_t key)
93 {
94
2/2
✓ Branch 0 (2→3) taken 15 times.
✓ Branch 1 (2→9) taken 2 times.
17 if (unlikely(!map->entries)) {
95 return NULL;
96 }
97
98 15 size_t hash = hash_key(key);
99 15 IntMapEntry *e;
100 27 for (size_t i = hash, j = 1; ; i += j++) {
101 27 e = map->entries + (i & map->mask);
102
2/2
✓ Branch 0 (4→5) taken 24 times.
✓ Branch 1 (4→9) taken 3 times.
27 if (!e->value) {
103 return NULL;
104 }
105
2/2
✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 22 times.
24 if (e->value == tombstone) {
106 2 continue;
107 }
108
2/2
✓ Branch 0 (7→8) taken 10 times.
✓ Branch 1 (7→9) taken 12 times.
22 if (e->key == key) {
109 return e;
110 }
111 }
112
113 BUG("unexpected loop break");
114 }
115
116 2 void *intmap_remove(IntMap *map, uint32_t key)
117 {
118 2 IntMapEntry *e = intmap_find(map, key);
119
1/2
✓ Branch 0 (3→4) taken 2 times.
✗ Branch 1 (3→5) not taken.
2 if (!e) {
120 return NULL;
121 }
122
123 2 void *value = e->value;
124 2 e->key = 0;
125 2 e->value = (char*)tombstone;
126 2 map->count--;
127 2 map->tombstones++;
128 2 return value;
129 }
130
131 WARN_UNUSED_RESULT
132 1383 static int intmap_do_insert(IntMap *map, uint32_t key, void *value, void **old_value)
133 {
134 1383 int err = 0;
135
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 1382 times.
1383 if (unlikely(!map->entries)) {
136 1 err = intmap_do_init(map, 0);
137
1/2
✓ Branch 0 (4→5) taken 1 times.
✗ Branch 1 (4→27) not taken.
1 if (unlikely(err)) {
138 return err;
139 }
140 }
141
142 1383 size_t hash = hash_key(key);
143 1383 bool replacing_tombstone_or_existing_value = false;
144 1383 IntMapEntry *e;
145 2097 for (size_t i = hash, j = 1; ; i += j++) {
146 2097 e = map->entries + (i & map->mask);
147
2/2
✓ Branch 0 (6→7) taken 715 times.
✓ Branch 1 (6→16) taken 1382 times.
2097 if (!e->value) {
148 break;
149 }
150
2/2
✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→11) taken 714 times.
715 if (e->value == tombstone) {
151 1 replacing_tombstone_or_existing_value = true;
152 1 BUG_ON(map->tombstones == 0);
153 1 map->tombstones--;
154 }
155
2/2
✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→15) taken 714 times.
715 if (unlikely(e->key == key)) {
156 1 replacing_tombstone_or_existing_value = true;
157 1 BUG_ON(!e->value);
158 1 *old_value = e->value;
159 1 key = e->key;
160 1 map->count--;
161 1 break;
162 }
163 }
164
165 1383 const size_t max_load = map->mask - (map->mask / 4);
166 1383 e->key = key;
167 1383 e->value = value;
168 1383 map->count++;
169
170
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→27) taken 1382 times.
1383 if (unlikely(map->count + map->tombstones > max_load)) {
171 1 BUG_ON(replacing_tombstone_or_existing_value);
172 1 size_t new_size = map->mask + 1;
173
1/4
✗ Branch 0 (19→20) not taken.
✓ Branch 1 (19→21) taken 1 times.
✗ Branch 2 (20→21) not taken.
✗ Branch 3 (20→23) not taken.
1 if (map->count > map->tombstones || new_size <= 256) {
174 // Only increase the size of the table when the number of
175 // real entries is higher than the number of tombstones
176 1 new_size <<= 1;
177
1/2
✗ Branch 0 (21→22) not taken.
✓ Branch 1 (21→23) taken 1 times.
1 if (unlikely(new_size == 0)) {
178 err = EOVERFLOW;
179 goto error;
180 }
181 }
182 1 err = intmap_resize(map, new_size);
183
1/2
✗ Branch 0 (24→25) not taken.
✓ Branch 1 (24→27) taken 1 times.
1 if (unlikely(err)) {
184 goto error;
185 }
186 }
187
188 return 0;
189
190 error:
191 map->count--;
192 e->key = 0;
193 e->value = NULL;
194 return err;
195 }
196
197 1383 void *intmap_insert_or_replace(IntMap *map, uint32_t key, void *value)
198 {
199 1383 void *replaced_value = NULL;
200 1383 int err = intmap_do_insert(map, key, value, &replaced_value);
201
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 1383 times.
1383 if (unlikely(err)) {
202 fatal_error(__func__, err);
203 }
204 1383 return replaced_value;
205 }
206
207 // Call a FreeFunction declared with an arbitrary pointer parameter type
208 // without -fsanitize=function pedantry
209 NO_SANITIZE("undefined")
210 1380 static void do_free_value(FreeFunction free_value, void *value)
211 {
212 1380 free_value(value);
213 1380 }
214
215 // Remove all entries without freeing the table
216 30 static void intmap_clear(IntMap *map, FreeFunction free_value)
217 {
218
2/2
✓ Branch 0 (2→3) taken 28 times.
✓ Branch 1 (2→12) taken 2 times.
30 if (unlikely(!map->entries)) {
219 return;
220 }
221
222
1/2
✓ Branch 0 (3→4) taken 28 times.
✗ Branch 1 (3→11) not taken.
28 if (free_value) {
223 28 size_t count = 0;
224
2/2
✓ Branch 0 (8→5) taken 1380 times.
✓ Branch 1 (8→9) taken 28 times.
1408 for (IntMapIter it = intmap_iter(map); intmap_next(&it); count++) {
225 1380 do_free_value(free_value, it.entry->value);
226 }
227 28 BUG_ON(count != map->count);
228 }
229
230 28 size_t len = map->mask + 1;
231 28 map->count = 0;
232 28 memset(map->entries, 0, len * sizeof(*map->entries));
233 }
234
235 30 void intmap_free(IntMap *map, FreeFunction free_value)
236 {
237 30 intmap_clear(map, free_value);
238 30 free(map->entries);
239 30 *map = (IntMap)INTMAP_INIT;
240 30 }
241