2 weeks ago
move ui_customwidget_create to separate file (GTK)
174 | 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/hash_map.h" | |
30 | ||
31 | #include <string.h> | |
32 | #include <assert.h> | |
440 | 33 | #include <errno.h> |
174 | 34 | |
35 | struct cx_hash_map_element_s { | |
36 | /** A pointer to the next element in the current bucket. */ | |
37 | struct cx_hash_map_element_s *next; | |
38 | ||
39 | /** The corresponding key. */ | |
40 | CxHashKey key; | |
41 | ||
42 | /** The value data. */ | |
43 | char data[]; | |
44 | }; | |
45 | ||
46 | static void cx_hash_map_clear(struct cx_map_s *map) { | |
47 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; | |
440 | 48 | for (size_t i = 0; i < hash_map->bucket_count; i++) { |
174 | 49 | struct cx_hash_map_element_s *elem = hash_map->buckets[i]; |
50 | if (elem != NULL) { | |
51 | do { | |
52 | struct cx_hash_map_element_s *next = elem->next; | |
53 | // invoke the destructor | |
54 | cx_invoke_destructor(map, elem->data); | |
55 | // free the key data | |
324 | 56 | cxFree(map->collection.allocator, (void *) elem->key.data); |
174 | 57 | // free the node |
324 | 58 | cxFree(map->collection.allocator, elem); |
174 | 59 | // proceed |
60 | elem = next; | |
61 | } while (elem != NULL); | |
62 | ||
63 | // do not leave a dangling pointer | |
64 | hash_map->buckets[i] = NULL; | |
65 | } | |
66 | } | |
324 | 67 | map->collection.size = 0; |
174 | 68 | } |
69 | ||
70 | static void cx_hash_map_destructor(struct cx_map_s *map) { | |
71 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; | |
72 | ||
73 | // free the buckets | |
74 | cx_hash_map_clear(map); | |
324 | 75 | cxFree(map->collection.allocator, hash_map->buckets); |
174 | 76 | |
77 | // free the map structure | |
324 | 78 | cxFree(map->collection.allocator, map); |
174 | 79 | } |
80 | ||
81 | static int cx_hash_map_put( | |
82 | CxMap *map, | |
83 | CxHashKey key, | |
84 | void *value | |
85 | ) { | |
86 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; | |
324 | 87 | const CxAllocator *allocator = map->collection.allocator; |
174 | 88 | |
89 | unsigned hash = key.hash; | |
90 | if (hash == 0) { | |
91 | cx_hash_murmur(&key); | |
92 | hash = key.hash; | |
93 | } | |
94 | ||
95 | size_t slot = hash % hash_map->bucket_count; | |
96 | struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; | |
97 | struct cx_hash_map_element_s *prev = NULL; | |
98 | ||
99 | while (elm != NULL && elm->key.hash < hash) { | |
100 | prev = elm; | |
101 | elm = elm->next; | |
102 | } | |
103 | ||
104 | if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && | |
105 | memcmp(elm->key.data, key.data, key.len) == 0) { | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
106 | // overwrite existing element, but call destructors first |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
107 | cx_invoke_destructor(map, elm->data); |
324 | 108 | if (map->collection.store_pointer) { |
174 | 109 | memcpy(elm->data, &value, sizeof(void *)); |
110 | } else { | |
324 | 111 | memcpy(elm->data, value, map->collection.elem_size); |
174 | 112 | } |
113 | } else { | |
114 | // allocate new element | |
115 | struct cx_hash_map_element_s *e = cxMalloc( | |
116 | allocator, | |
324 | 117 | sizeof(struct cx_hash_map_element_s) + map->collection.elem_size |
174 | 118 | ); |
440 | 119 | if (e == NULL) return -1; |
174 | 120 | |
121 | // write the value | |
324 | 122 | if (map->collection.store_pointer) { |
174 | 123 | memcpy(e->data, &value, sizeof(void *)); |
124 | } else { | |
324 | 125 | memcpy(e->data, value, map->collection.elem_size); |
174 | 126 | } |
127 | ||
128 | // copy the key | |
129 | void *kd = cxMalloc(allocator, key.len); | |
440 | 130 | if (kd == NULL) return -1; |
174 | 131 | memcpy(kd, key.data, key.len); |
132 | e->key.data = kd; | |
133 | e->key.len = key.len; | |
134 | e->key.hash = hash; | |
135 | ||
136 | // insert the element into the linked list | |
137 | if (prev == NULL) { | |
138 | hash_map->buckets[slot] = e; | |
139 | } else { | |
140 | prev->next = e; | |
141 | } | |
142 | e->next = elm; | |
143 | ||
144 | // increase the size | |
324 | 145 | map->collection.size++; |
174 | 146 | } |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | static void cx_hash_map_unlink( | |
152 | struct cx_hash_map_s *hash_map, | |
153 | size_t slot, | |
154 | struct cx_hash_map_element_s *prev, | |
155 | struct cx_hash_map_element_s *elm | |
156 | ) { | |
157 | // unlink | |
158 | if (prev == NULL) { | |
159 | hash_map->buckets[slot] = elm->next; | |
160 | } else { | |
161 | prev->next = elm->next; | |
162 | } | |
163 | // free element | |
324 | 164 | cxFree(hash_map->base.collection.allocator, (void *) elm->key.data); |
165 | cxFree(hash_map->base.collection.allocator, elm); | |
174 | 166 | // decrease size |
324 | 167 | hash_map->base.collection.size--; |
174 | 168 | } |
169 | ||
170 | /** | |
171 | * Helper function to avoid code duplication. | |
172 | * | |
440 | 173 | * If @p remove is true, and @p targetbuf is @c NULL, the element |
174 | * will be destroyed when found. | |
175 | * | |
176 | * If @p remove is true, and @p targetbuf is set, the element will | |
177 | * be copied to that buffer and no destructor function is called. | |
178 | * | |
179 | * If @p remove is false, @p targetbuf must not be non-null and | |
180 | * either the pointer, when the map is storing pointers, is copied | |
181 | * to the target buffer, or a pointer to the stored object will | |
182 | * be copied to the target buffer. | |
183 | * | |
174 | 184 | * @param map the map |
185 | * @param key the key to look up | |
440 | 186 | * @param targetbuf see description |
174 | 187 | * @param remove flag indicating whether the looked up entry shall be removed |
440 | 188 | * @return zero, if the key was found, non-zero otherwise |
174 | 189 | */ |
440 | 190 | static int cx_hash_map_get_remove( |
174 | 191 | CxMap *map, |
192 | CxHashKey key, | |
440 | 193 | void *targetbuf, |
194 | bool remove | |
174 | 195 | ) { |
196 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; | |
197 | ||
198 | unsigned hash = key.hash; | |
199 | if (hash == 0) { | |
200 | cx_hash_murmur(&key); | |
201 | hash = key.hash; | |
202 | } | |
203 | ||
204 | size_t slot = hash % hash_map->bucket_count; | |
205 | struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; | |
206 | struct cx_hash_map_element_s *prev = NULL; | |
207 | while (elm && elm->key.hash <= hash) { | |
208 | if (elm->key.hash == hash && elm->key.len == key.len) { | |
209 | if (memcmp(elm->key.data, key.data, key.len) == 0) { | |
440 | 210 | if (remove) { |
211 | if (targetbuf == NULL) { | |
212 | cx_invoke_destructor(map, elm->data); | |
213 | } else { | |
214 | memcpy(targetbuf, elm->data, map->collection.elem_size); | |
215 | } | |
216 | cx_hash_map_unlink(hash_map, slot, prev, elm); | |
174 | 217 | } else { |
440 | 218 | assert(targetbuf != NULL); |
219 | void *data = NULL; | |
324 | 220 | if (map->collection.store_pointer) { |
174 | 221 | data = *(void **) elm->data; |
222 | } else { | |
223 | data = elm->data; | |
224 | } | |
440 | 225 | memcpy(targetbuf, &data, sizeof(void *)); |
174 | 226 | } |
440 | 227 | return 0; |
174 | 228 | } |
229 | } | |
230 | prev = elm; | |
231 | elm = prev->next; | |
232 | } | |
233 | ||
440 | 234 | return 1; |
174 | 235 | } |
236 | ||
237 | static void *cx_hash_map_get( | |
324 | 238 | const CxMap *map, |
174 | 239 | CxHashKey key |
240 | ) { | |
241 | // we can safely cast, because we know the map stays untouched | |
440 | 242 | void *ptr = NULL; |
243 | int found = cx_hash_map_get_remove((CxMap *) map, key, &ptr, false); | |
244 | return found == 0 ? ptr : NULL; | |
174 | 245 | } |
246 | ||
440 | 247 | static int cx_hash_map_remove( |
174 | 248 | CxMap *map, |
249 | CxHashKey key, | |
440 | 250 | void *targetbuf |
174 | 251 | ) { |
440 | 252 | return cx_hash_map_get_remove(map, key, targetbuf, true); |
174 | 253 | } |
254 | ||
324 | 255 | static void *cx_hash_map_iter_current_entry(const void *it) { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
256 | const CxMapIterator *iter = it; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
257 | // we have to cast away const, because of the signature |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
258 | return (void*) &iter->entry; |
174 | 259 | } |
260 | ||
324 | 261 | static void *cx_hash_map_iter_current_key(const void *it) { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
262 | const CxMapIterator *iter = it; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
263 | struct cx_hash_map_element_s *elm = iter->elem; |
174 | 264 | return &elm->key; |
265 | } | |
266 | ||
324 | 267 | static void *cx_hash_map_iter_current_value(const void *it) { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
268 | const CxMapIterator *iter = it; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
269 | const CxMap *map = iter->map.c; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
270 | struct cx_hash_map_element_s *elm = iter->elem; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
271 | if (map->collection.store_pointer) { |
174 | 272 | return *(void **) elm->data; |
273 | } else { | |
274 | return elm->data; | |
275 | } | |
276 | } | |
277 | ||
324 | 278 | static bool cx_hash_map_iter_valid(const void *it) { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
279 | const CxMapIterator *iter = it; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
280 | return iter->elem != NULL; |
174 | 281 | } |
282 | ||
283 | static void cx_hash_map_iter_next(void *it) { | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
284 | CxMapIterator *iter = it; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
285 | CxMap *map = iter->map.m; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
286 | struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
287 | struct cx_hash_map_element_s *elm = iter->elem; |
174 | 288 | |
289 | // remove current element, if asked | |
290 | if (iter->base.remove) { | |
291 | ||
292 | // clear the flag | |
293 | iter->base.remove = false; | |
294 | ||
295 | // determine the next element | |
296 | struct cx_hash_map_element_s *next = elm->next; | |
297 | ||
298 | // search the previous element | |
299 | struct cx_hash_map_element_s *prev = NULL; | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
300 | if (hmap->buckets[iter->slot] != elm) { |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
301 | prev = hmap->buckets[iter->slot]; |
174 | 302 | while (prev->next != elm) { |
303 | prev = prev->next; | |
304 | } | |
305 | } | |
306 | ||
307 | // destroy | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
308 | cx_invoke_destructor(map, elm->data); |
174 | 309 | |
310 | // unlink | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
311 | cx_hash_map_unlink(hmap, iter->slot, prev, elm); |
174 | 312 | |
313 | // advance | |
314 | elm = next; | |
315 | } else { | |
316 | // just advance | |
317 | elm = elm->next; | |
318 | iter->index++; | |
319 | } | |
320 | ||
321 | // search the next bucket, if required | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
322 | while (elm == NULL && ++iter->slot < hmap->bucket_count) { |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
323 | elm = hmap->buckets[iter->slot]; |
174 | 324 | } |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
325 | iter->elem = elm; |
174 | 326 | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
327 | // copy data to a location where the iterator can point to |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
328 | // we need to do it here, because the iterator function call |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
329 | // must not modify the iterator (the parameter is const) |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
330 | if (elm != NULL) { |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
331 | iter->entry.key = &elm->key; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
332 | if (iter->map.c->collection.store_pointer) { |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
333 | iter->entry.value = *(void **) elm->data; |
174 | 334 | } else { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
335 | iter->entry.value = elm->data; |
174 | 336 | } |
337 | } | |
338 | } | |
339 | ||
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
340 | static CxMapIterator cx_hash_map_iterator( |
324 | 341 | const CxMap *map, |
174 | 342 | enum cx_map_iterator_type type |
343 | ) { | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
344 | CxMapIterator iter; |
174 | 345 | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
346 | iter.map.c = map; |
324 | 347 | iter.elem_count = map->collection.size; |
174 | 348 | |
349 | switch (type) { | |
350 | case CX_MAP_ITERATOR_PAIRS: | |
324 | 351 | iter.elem_size = sizeof(CxMapEntry); |
174 | 352 | iter.base.current = cx_hash_map_iter_current_entry; |
353 | break; | |
354 | case CX_MAP_ITERATOR_KEYS: | |
324 | 355 | iter.elem_size = sizeof(CxHashKey); |
174 | 356 | iter.base.current = cx_hash_map_iter_current_key; |
357 | break; | |
358 | case CX_MAP_ITERATOR_VALUES: | |
324 | 359 | iter.elem_size = map->collection.elem_size; |
174 | 360 | iter.base.current = cx_hash_map_iter_current_value; |
361 | break; | |
362 | default: | |
440 | 363 | assert(false); // LCOV_EXCL_LINE |
174 | 364 | } |
365 | ||
324 | 366 | iter.base.valid = cx_hash_map_iter_valid; |
367 | iter.base.next = cx_hash_map_iter_next; | |
174 | 368 | iter.base.remove = false; |
369 | iter.base.mutating = false; | |
370 | ||
371 | iter.slot = 0; | |
372 | iter.index = 0; | |
373 | ||
324 | 374 | if (map->collection.size > 0) { |
174 | 375 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
376 | struct cx_hash_map_element_s *elm = hash_map->buckets[0]; | |
377 | while (elm == NULL) { | |
378 | elm = hash_map->buckets[++iter.slot]; | |
379 | } | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
380 | iter.elem = elm; |
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
381 | iter.entry.key = &elm->key; |
324 | 382 | if (map->collection.store_pointer) { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
383 | iter.entry.value = *(void **) elm->data; |
174 | 384 | } else { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
385 | iter.entry.value = elm->data; |
174 | 386 | } |
387 | } else { | |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
388 | iter.elem = NULL; |
174 | 389 | } |
390 | ||
391 | return iter; | |
392 | } | |
393 | ||
394 | static cx_map_class cx_hash_map_class = { | |
395 | cx_hash_map_destructor, | |
396 | cx_hash_map_clear, | |
397 | cx_hash_map_put, | |
398 | cx_hash_map_get, | |
399 | cx_hash_map_remove, | |
400 | cx_hash_map_iterator, | |
401 | }; | |
402 | ||
403 | CxMap *cxHashMapCreate( | |
324 | 404 | const CxAllocator *allocator, |
174 | 405 | size_t itemsize, |
406 | size_t buckets | |
407 | ) { | |
440 | 408 | if (allocator == NULL) { |
409 | allocator = cxDefaultAllocator; | |
410 | } | |
411 | ||
174 | 412 | if (buckets == 0) { |
413 | // implementation defined default | |
414 | buckets = 16; | |
415 | } | |
416 | ||
417 | struct cx_hash_map_s *map = cxCalloc(allocator, 1, | |
418 | sizeof(struct cx_hash_map_s)); | |
419 | if (map == NULL) return NULL; | |
420 | ||
421 | // initialize hash map members | |
422 | map->bucket_count = buckets; | |
423 | map->buckets = cxCalloc(allocator, buckets, | |
424 | sizeof(struct cx_hash_map_element_s *)); | |
440 | 425 | if (map->buckets == NULL) { // LCOV_EXCL_START |
174 | 426 | cxFree(allocator, map); |
427 | return NULL; | |
440 | 428 | } // LCOV_EXCL_STOP |
174 | 429 | |
430 | // initialize base members | |
431 | map->base.cl = &cx_hash_map_class; | |
324 | 432 | map->base.collection.allocator = allocator; |
174 | 433 | |
434 | if (itemsize > 0) { | |
324 | 435 | map->base.collection.elem_size = itemsize; |
174 | 436 | } else { |
471
063a9f29098c
ucx update + fix doc attach/detach + fix ui_set with unbound values
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
440
diff
changeset
|
437 | map->base.collection.elem_size = sizeof(void *); |
324 | 438 | map->base.collection.store_pointer = true; |
174 | 439 | } |
440 | ||
441 | return (CxMap *) map; | |
442 | } | |
443 | ||
444 | int cxMapRehash(CxMap *map) { | |
445 | struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; | |
324 | 446 | if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) { |
174 | 447 | |
324 | 448 | size_t new_bucket_count = (map->collection.size * 5) >> 1; |
440 | 449 | if (new_bucket_count < hash_map->bucket_count) { |
450 | errno = EOVERFLOW; | |
451 | return 1; | |
452 | } | |
174 | 453 | struct cx_hash_map_element_s **new_buckets = cxCalloc( |
324 | 454 | map->collection.allocator, |
174 | 455 | new_bucket_count, sizeof(struct cx_hash_map_element_s *) |
456 | ); | |
457 | ||
440 | 458 | if (new_buckets == NULL) return 1; |
174 | 459 | |
460 | // iterate through the elements and assign them to their new slots | |
440 | 461 | for (size_t slot = 0; slot < hash_map->bucket_count; slot++) { |
174 | 462 | struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
463 | while (elm != NULL) { | |
464 | struct cx_hash_map_element_s *next = elm->next; | |
465 | size_t new_slot = elm->key.hash % new_bucket_count; | |
466 | ||
467 | // find position where to insert | |
468 | struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot]; | |
469 | struct cx_hash_map_element_s *bucket_prev = NULL; | |
470 | while (bucket_next != NULL && | |
471 | bucket_next->key.hash < elm->key.hash) { | |
472 | bucket_prev = bucket_next; | |
473 | bucket_next = bucket_next->next; | |
474 | } | |
475 | ||
476 | // insert | |
477 | if (bucket_prev == NULL) { | |
478 | elm->next = new_buckets[new_slot]; | |
479 | new_buckets[new_slot] = elm; | |
480 | } else { | |
481 | bucket_prev->next = elm; | |
482 | elm->next = bucket_next; | |
483 | } | |
484 | ||
485 | // advance | |
486 | elm = next; | |
487 | } | |
488 | } | |
489 | ||
490 | // assign result to the map | |
491 | hash_map->bucket_count = new_bucket_count; | |
324 | 492 | cxFree(map->collection.allocator, hash_map->buckets); |
174 | 493 | hash_map->buckets = new_buckets; |
494 | } | |
495 | return 0; | |
496 | } |