| 113 // allocate new element |
113 // allocate new element |
| 114 struct cx_hash_map_element_s *e = cxMalloc( |
114 struct cx_hash_map_element_s *e = cxMalloc( |
| 115 allocator, |
115 allocator, |
| 116 sizeof(struct cx_hash_map_element_s) + map->collection.elem_size |
116 sizeof(struct cx_hash_map_element_s) + map->collection.elem_size |
| 117 ); |
117 ); |
| 118 if (e == NULL) { |
118 if (e == NULL) return -1; |
| 119 return -1; |
|
| 120 } |
|
| 121 |
119 |
| 122 // write the value |
120 // write the value |
| 123 if (map->collection.store_pointer) { |
121 if (map->collection.store_pointer) { |
| 124 memcpy(e->data, &value, sizeof(void *)); |
122 memcpy(e->data, &value, sizeof(void *)); |
| 125 } else { |
123 } else { |
| 126 memcpy(e->data, value, map->collection.elem_size); |
124 memcpy(e->data, value, map->collection.elem_size); |
| 127 } |
125 } |
| 128 |
126 |
| 129 // copy the key |
127 // copy the key |
| 130 void *kd = cxMalloc(allocator, key.len); |
128 void *kd = cxMalloc(allocator, key.len); |
| 131 if (kd == NULL) { |
129 if (kd == NULL) return -1; |
| 132 return -1; |
|
| 133 } |
|
| 134 memcpy(kd, key.data, key.len); |
130 memcpy(kd, key.data, key.len); |
| 135 e->key.data = kd; |
131 e->key.data = kd; |
| 136 e->key.len = key.len; |
132 e->key.len = key.len; |
| 137 e->key.hash = hash; |
133 e->key.hash = hash; |
| 138 |
134 |
| 171 } |
167 } |
| 172 |
168 |
| 173 /** |
169 /** |
| 174 * Helper function to avoid code duplication. |
170 * Helper function to avoid code duplication. |
| 175 * |
171 * |
| |
172 * If \p remove is true, and \p targetbuf is \c NULL, the element |
| |
173 * will be destroyed when found. |
| |
174 * |
| |
175 * If \p remove is true, and \p targetbuf is set, the element will |
| |
176 * be copied to that buffer and no destructor function is called. |
| |
177 * |
| |
178 * If \p remove is false, \p targetbuf must not be non-null and |
| |
179 * either the pointer, when the map is storing pointers, is copied |
| |
180 * to the target buffer, or a pointer to the stored object will |
| |
181 * be copied to the target buffer. |
| |
182 * |
| 176 * @param map the map |
183 * @param map the map |
| 177 * @param key the key to look up |
184 * @param key the key to look up |
| |
185 * @param targetbuf see description |
| 178 * @param remove flag indicating whether the looked up entry shall be removed |
186 * @param remove flag indicating whether the looked up entry shall be removed |
| 179 * @param destroy flag indicating whether the destructor shall be invoked |
187 * @return zero, if the key was found, non-zero otherwise |
| 180 * @return a pointer to the value corresponding to the key or \c NULL |
|
| 181 */ |
188 */ |
| 182 static void *cx_hash_map_get_remove( |
189 static int cx_hash_map_get_remove( |
| 183 CxMap *map, |
190 CxMap *map, |
| 184 CxHashKey key, |
191 CxHashKey key, |
| 185 bool remove, |
192 void *targetbuf, |
| 186 bool destroy |
193 bool remove |
| 187 ) { |
194 ) { |
| 188 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
195 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
| 189 |
196 |
| 190 unsigned hash = key.hash; |
197 unsigned hash = key.hash; |
| 191 if (hash == 0) { |
198 if (hash == 0) { |
| 197 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
204 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
| 198 struct cx_hash_map_element_s *prev = NULL; |
205 struct cx_hash_map_element_s *prev = NULL; |
| 199 while (elm && elm->key.hash <= hash) { |
206 while (elm && elm->key.hash <= hash) { |
| 200 if (elm->key.hash == hash && elm->key.len == key.len) { |
207 if (elm->key.hash == hash && elm->key.len == key.len) { |
| 201 if (memcmp(elm->key.data, key.data, key.len) == 0) { |
208 if (memcmp(elm->key.data, key.data, key.len) == 0) { |
| 202 void *data = NULL; |
209 if (remove) { |
| 203 if (destroy) { |
210 if (targetbuf == NULL) { |
| 204 cx_invoke_destructor(map, elm->data); |
211 cx_invoke_destructor(map, elm->data); |
| |
212 } else { |
| |
213 memcpy(targetbuf, elm->data, map->collection.elem_size); |
| |
214 } |
| |
215 cx_hash_map_unlink(hash_map, slot, prev, elm); |
| 205 } else { |
216 } else { |
| |
217 assert(targetbuf != NULL); |
| |
218 void *data = NULL; |
| 206 if (map->collection.store_pointer) { |
219 if (map->collection.store_pointer) { |
| 207 data = *(void **) elm->data; |
220 data = *(void **) elm->data; |
| 208 } else { |
221 } else { |
| 209 data = elm->data; |
222 data = elm->data; |
| 210 } |
223 } |
| |
224 memcpy(targetbuf, &data, sizeof(void *)); |
| 211 } |
225 } |
| 212 if (remove) { |
226 return 0; |
| 213 cx_hash_map_unlink(hash_map, slot, prev, elm); |
|
| 214 } |
|
| 215 return data; |
|
| 216 } |
227 } |
| 217 } |
228 } |
| 218 prev = elm; |
229 prev = elm; |
| 219 elm = prev->next; |
230 elm = prev->next; |
| 220 } |
231 } |
| 221 |
232 |
| 222 return NULL; |
233 return 1; |
| 223 } |
234 } |
| 224 |
235 |
| 225 static void *cx_hash_map_get( |
236 static void *cx_hash_map_get( |
| 226 CxMap const *map, |
237 const CxMap *map, |
| 227 CxHashKey key |
238 CxHashKey key |
| 228 ) { |
239 ) { |
| 229 // we can safely cast, because we know the map stays untouched |
240 // we can safely cast, because we know the map stays untouched |
| 230 return cx_hash_map_get_remove((CxMap *) map, key, false, false); |
241 void *ptr = NULL; |
| 231 } |
242 int found = cx_hash_map_get_remove((CxMap *) map, key, &ptr, false); |
| 232 |
243 return found == 0 ? ptr : NULL; |
| 233 static void *cx_hash_map_remove( |
244 } |
| |
245 |
| |
246 static int cx_hash_map_remove( |
| 234 CxMap *map, |
247 CxMap *map, |
| 235 CxHashKey key, |
248 CxHashKey key, |
| 236 bool destroy |
249 void *targetbuf |
| 237 ) { |
250 ) { |
| 238 return cx_hash_map_get_remove(map, key, true, destroy); |
251 return cx_hash_map_get_remove(map, key, targetbuf, true); |
| 239 } |
252 } |
| 240 |
253 |
| 241 static void *cx_hash_map_iter_current_entry(void const *it) { |
254 static void *cx_hash_map_iter_current_entry(const void *it) { |
| 242 struct cx_iterator_s const *iter = it; |
255 const struct cx_iterator_s *iter = it; |
| 243 // struct has to have a compatible signature |
256 // struct has to have a compatible signature |
| 244 return (struct cx_map_entry_s *) &(iter->kv_data); |
257 return (struct cx_map_entry_s *) &(iter->kv_data); |
| 245 } |
258 } |
| 246 |
259 |
| 247 static void *cx_hash_map_iter_current_key(void const *it) { |
260 static void *cx_hash_map_iter_current_key(const void *it) { |
| 248 struct cx_iterator_s const *iter = it; |
261 const struct cx_iterator_s *iter = it; |
| 249 struct cx_hash_map_element_s *elm = iter->elem_handle; |
262 struct cx_hash_map_element_s *elm = iter->elem_handle; |
| 250 return &elm->key; |
263 return &elm->key; |
| 251 } |
264 } |
| 252 |
265 |
| 253 static void *cx_hash_map_iter_current_value(void const *it) { |
266 static void *cx_hash_map_iter_current_value(const void *it) { |
| 254 struct cx_iterator_s const *iter = it; |
267 const struct cx_iterator_s *iter = it; |
| 255 struct cx_hash_map_s const *map = iter->src_handle.c; |
268 const struct cx_hash_map_s *map = iter->src_handle.c; |
| 256 struct cx_hash_map_element_s *elm = iter->elem_handle; |
269 struct cx_hash_map_element_s *elm = iter->elem_handle; |
| 257 if (map->base.collection.store_pointer) { |
270 if (map->base.collection.store_pointer) { |
| 258 return *(void **) elm->data; |
271 return *(void **) elm->data; |
| 259 } else { |
272 } else { |
| 260 return elm->data; |
273 return elm->data; |
| 261 } |
274 } |
| 262 } |
275 } |
| 263 |
276 |
| 264 static bool cx_hash_map_iter_valid(void const *it) { |
277 static bool cx_hash_map_iter_valid(const void *it) { |
| 265 struct cx_iterator_s const *iter = it; |
278 const struct cx_iterator_s *iter = it; |
| 266 return iter->elem_handle != NULL; |
279 return iter->elem_handle != NULL; |
| 267 } |
280 } |
| 268 |
281 |
| 269 static void cx_hash_map_iter_next(void *it) { |
282 static void cx_hash_map_iter_next(void *it) { |
| 270 struct cx_iterator_s *iter = it; |
283 struct cx_iterator_s *iter = it; |
| 404 |
421 |
| 405 // initialize hash map members |
422 // initialize hash map members |
| 406 map->bucket_count = buckets; |
423 map->bucket_count = buckets; |
| 407 map->buckets = cxCalloc(allocator, buckets, |
424 map->buckets = cxCalloc(allocator, buckets, |
| 408 sizeof(struct cx_hash_map_element_s *)); |
425 sizeof(struct cx_hash_map_element_s *)); |
| 409 if (map->buckets == NULL) { |
426 if (map->buckets == NULL) { // LCOV_EXCL_START |
| 410 cxFree(allocator, map); |
427 cxFree(allocator, map); |
| 411 return NULL; |
428 return NULL; |
| 412 } |
429 } // LCOV_EXCL_STOP |
| 413 |
430 |
| 414 // initialize base members |
431 // initialize base members |
| 415 map->base.cl = &cx_hash_map_class; |
432 map->base.cl = &cx_hash_map_class; |
| 416 map->base.collection.allocator = allocator; |
433 map->base.collection.allocator = allocator; |
| 417 |
434 |
| 429 int cxMapRehash(CxMap *map) { |
446 int cxMapRehash(CxMap *map) { |
| 430 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
447 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
| 431 if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) { |
448 if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) { |
| 432 |
449 |
| 433 size_t new_bucket_count = (map->collection.size * 5) >> 1; |
450 size_t new_bucket_count = (map->collection.size * 5) >> 1; |
| |
451 if (new_bucket_count < hash_map->bucket_count) { |
| |
452 errno = EOVERFLOW; |
| |
453 return 1; |
| |
454 } |
| 434 struct cx_hash_map_element_s **new_buckets = cxCalloc( |
455 struct cx_hash_map_element_s **new_buckets = cxCalloc( |
| 435 map->collection.allocator, |
456 map->collection.allocator, |
| 436 new_bucket_count, sizeof(struct cx_hash_map_element_s *) |
457 new_bucket_count, sizeof(struct cx_hash_map_element_s *) |
| 437 ); |
458 ); |
| 438 |
459 |
| 439 if (new_buckets == NULL) { |
460 if (new_buckets == NULL) return 1; |
| 440 return 1; |
|
| 441 } |
|
| 442 |
461 |
| 443 // iterate through the elements and assign them to their new slots |
462 // iterate through the elements and assign them to their new slots |
| 444 cx_for_n(slot, hash_map->bucket_count) { |
463 for (size_t slot = 0; slot < hash_map->bucket_count; slot++) { |
| 445 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
464 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
| 446 while (elm != NULL) { |
465 while (elm != NULL) { |
| 447 struct cx_hash_map_element_s *next = elm->next; |
466 struct cx_hash_map_element_s *next = elm->next; |
| 448 size_t new_slot = elm->key.hash % new_bucket_count; |
467 size_t new_slot = elm->key.hash % new_bucket_count; |
| 449 |
468 |