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 |