/src/wireshark/wsutil/wmem/wmem_map.c
Line | Count | Source |
1 | | /* wmem_map.c |
2 | | * Wireshark Memory Manager Hash Map |
3 | | * Copyright 2014, Evan Huus <eapache@gmail.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | #include "config.h" |
12 | | |
13 | | #include <glib.h> |
14 | | |
15 | | #ifdef HAVE_XXHASH |
16 | | #include <xxhash.h> |
17 | | #endif /* HAVE_XXHASH */ |
18 | | |
19 | | #include "wmem_core.h" |
20 | | #include "wmem_list.h" |
21 | | #include "wmem_map.h" |
22 | | #include "wmem_map_int.h" |
23 | | #include "wmem_user_cb.h" |
24 | | |
25 | | #include "wsutil/ws_assert.h" |
26 | | #include "wsutil/bits_ctz.h" |
27 | | |
28 | | static uint32_t x; /* Used for universal integer hashing (see the HASH macro) */ |
29 | | |
30 | | /* Used for the wmem_strong_hash() function */ |
31 | | static uint32_t preseed; |
32 | | static uint32_t postseed; |
33 | | |
34 | | void |
35 | | wmem_init_hashing(void) |
36 | 16 | { |
37 | | /* Get a random odd integer to multiply the hash by. */ |
38 | 16 | x = g_random_int(); |
39 | 16 | if ((x % 2) == 0) |
40 | 5 | x += 1; |
41 | | |
42 | 16 | preseed = g_random_int(); |
43 | 16 | postseed = g_random_int(); |
44 | 16 | } |
45 | | |
46 | | typedef struct _wmem_map_item_t { |
47 | | const void *key; |
48 | | void *value; |
49 | | struct _wmem_map_item_t *next; |
50 | | |
51 | | /* Store the full hash to speed up collisions and resizing, avoiding |
52 | | * recalculating it or in some cases doing the equality function. |
53 | | * In the g_direct_hash case (especially if the equality function |
54 | | * is g_direct_equal), we don't need to store it, but it's probably |
55 | | * not worth implementing a parallel API for the direct map case. |
56 | | */ |
57 | | uint32_t hash; |
58 | | } wmem_map_item_t; |
59 | | |
60 | | struct _wmem_map_t { |
61 | | /* Number of items stored. */ |
62 | | size_t count; |
63 | | |
64 | | /* The base-2 logarithm of the actual size of the table. We store this |
65 | | * value for efficiency in hashing, since finding the actual capacity |
66 | | * becomes just a left-shift (see the CAPACITY macro) whereas taking |
67 | | * logarithms is expensive. Limited to 32 (GHashFunc returns an unsigned, |
68 | | * which might be 32 bits; also see how the HASH is shifted.) */ |
69 | | unsigned capacity; |
70 | | unsigned min_capacity; |
71 | | |
72 | | wmem_map_item_t **table; |
73 | | wmem_map_item_t *items; |
74 | | |
75 | | /* Next unused item in the items array */ |
76 | | wmem_map_item_t *next_item; |
77 | | |
78 | | /* A stack of items that had keys removed from the map without |
79 | | * replacing their values. For more speed, we could instead just leave such |
80 | | * items orphaned (not decrementing map->count but keeping a count of |
81 | | * deleted items so that wmem_map_size is accurate). The map would be more |
82 | | * likely to reach the item count limit (2^32) with many removals and |
83 | | * insertions (but our largest uses do not remove items so that might be |
84 | | * acceptable.) |
85 | | */ |
86 | | wmem_stack_t *deleted_items; |
87 | | |
88 | | GHashFunc hash_func; |
89 | | GEqualFunc eql_func; |
90 | | |
91 | | unsigned metadata_scope_cb_id; |
92 | | unsigned data_scope_cb_id; |
93 | | |
94 | | wmem_allocator_t *metadata_allocator; |
95 | | wmem_allocator_t *data_allocator; |
96 | | }; |
97 | | |
98 | | /* As per the comment on the 'capacity' member of the wmem_map_t struct, this is |
99 | | * the base-2 logarithm, meaning the actual default capacity is 2^5 = 32 */ |
100 | 281k | #define WMEM_MAP_DEFAULT_CAPACITY 5 |
101 | | |
102 | | /* Macro for calculating the real capacity of the map by using a left-shift to |
103 | | * do the 2^x operation. */ |
104 | 5.63M | #define CAPACITY(MAP) (((size_t)1) << (MAP)->capacity) |
105 | | |
106 | | /* Efficient universal integer hashing: |
107 | | * https://en.wikipedia.org/wiki/Universal_hashing#Avoiding_modular_arithmetic |
108 | | */ |
109 | | #define HASH(MAP, KEY) \ |
110 | 10.3M | ((uint32_t)((MAP)->hash_func(KEY) * x)) |
111 | | |
112 | 10.8M | #define MASK_HASH(MAP, HASH) ((uint32_t)((HASH) >> (32 - (MAP)->capacity))) |
113 | | |
114 | | static void |
115 | | wmem_map_init_table(wmem_map_t *map) |
116 | 229k | { |
117 | 229k | map->count = 0; |
118 | 229k | map->capacity = map->min_capacity; |
119 | 229k | map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map)); |
120 | | /* We do *not* need to 0 these, unlike the pointers. */ |
121 | 229k | map->items = wmem_alloc_array(map->data_allocator, wmem_map_item_t, CAPACITY(map)); |
122 | 229k | map->next_item = map->items; |
123 | 229k | } |
124 | | |
125 | | static bool |
126 | | wmem_map_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, |
127 | | void *user_data) |
128 | 0 | { |
129 | 0 | wmem_map_t *map = (wmem_map_t*)user_data; |
130 | |
|
131 | 0 | if (map->data_scope_cb_id) { |
132 | 0 | wmem_unregister_callback(map->data_allocator, map->data_scope_cb_id); |
133 | 0 | } |
134 | |
|
135 | 0 | return false; |
136 | 0 | } |
137 | | |
138 | | wmem_map_t * |
139 | | wmem_map_new(wmem_allocator_t *allocator, |
140 | | GHashFunc hash_func, GEqualFunc eql_func) |
141 | 278k | { |
142 | 278k | wmem_map_t *map; |
143 | | |
144 | 278k | map = wmem_new(allocator, wmem_map_t); |
145 | | |
146 | 278k | map->hash_func = hash_func; |
147 | 278k | map->eql_func = eql_func; |
148 | 278k | map->metadata_allocator = allocator; |
149 | 278k | map->data_allocator = allocator; |
150 | 278k | map->count = 0; |
151 | 278k | map->min_capacity = WMEM_MAP_DEFAULT_CAPACITY; |
152 | 278k | map->table = NULL; |
153 | 278k | map->items = NULL; |
154 | 278k | map->next_item = NULL; |
155 | 278k | map->deleted_items = wmem_stack_new(allocator); |
156 | | |
157 | | // The first callback ID wmem_register_callback assigns is 1, so |
158 | | // 0 means unused. |
159 | 278k | map->data_scope_cb_id = 0; |
160 | 278k | map->metadata_scope_cb_id = 0; |
161 | | |
162 | 278k | return map; |
163 | 278k | } |
164 | | |
165 | | static bool |
166 | | wmem_map_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event, |
167 | | void *user_data) |
168 | 0 | { |
169 | 0 | wmem_map_t *map = (wmem_map_t*)user_data; |
170 | |
|
171 | 0 | map->count = 0; |
172 | 0 | map->table = NULL; |
173 | 0 | map->items = NULL; |
174 | 0 | map->next_item = NULL; |
175 | 0 | while (wmem_stack_count(map->deleted_items)) |
176 | 0 | wmem_stack_pop(map->deleted_items); |
177 | |
|
178 | 0 | if (event == WMEM_CB_DESTROY_EVENT) { |
179 | 0 | wmem_unregister_callback(map->metadata_allocator, map->metadata_scope_cb_id); |
180 | 0 | wmem_destroy_stack(map->deleted_items); |
181 | 0 | wmem_free(map->metadata_allocator, map); |
182 | 0 | } |
183 | |
|
184 | 0 | return true; |
185 | 0 | } |
186 | | |
187 | | wmem_map_t * |
188 | | wmem_map_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope, |
189 | | GHashFunc hash_func, GEqualFunc eql_func) |
190 | 2.62k | { |
191 | 2.62k | wmem_map_t *map; |
192 | | |
193 | 2.62k | map = wmem_new(metadata_scope, wmem_map_t); |
194 | | |
195 | 2.62k | map->hash_func = hash_func; |
196 | 2.62k | map->eql_func = eql_func; |
197 | 2.62k | map->metadata_allocator = metadata_scope; |
198 | 2.62k | map->data_allocator = data_scope; |
199 | 2.62k | map->count = 0; |
200 | 2.62k | map->min_capacity = WMEM_MAP_DEFAULT_CAPACITY; |
201 | 2.62k | map->table = NULL; |
202 | 2.62k | map->items = NULL; |
203 | 2.62k | map->next_item = NULL; |
204 | 2.62k | map->deleted_items = wmem_stack_new(metadata_scope); |
205 | | |
206 | 2.62k | map->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_map_destroy_cb, map); |
207 | 2.62k | map->data_scope_cb_id = wmem_register_callback(data_scope, wmem_map_reset_cb, map); |
208 | | |
209 | 2.62k | return map; |
210 | 2.62k | } |
211 | | |
212 | | static inline void |
213 | | wmem_map_grow(wmem_map_t *map, unsigned new_capacity) |
214 | 7.14k | { |
215 | 7.14k | wmem_map_item_t **old_table, *cur, *nxt; |
216 | 7.14k | size_t old_cap, i; |
217 | 7.14k | unsigned slot; |
218 | | |
219 | 7.14k | if (new_capacity > 32) { |
220 | | // Run time error |
221 | | // XXX - If we really need to support more than 2^32 items, |
222 | | // we can allocate a new items array without changing |
223 | | // the number of slots in this case. |
224 | 0 | ws_error("wmem_map does not support more than 2^32 items"); |
225 | 0 | return; |
226 | 0 | } |
227 | | |
228 | 7.14k | if (new_capacity < map->capacity) { |
229 | 0 | ws_info("wmem_map does not support shrinking"); |
230 | 0 | return; |
231 | 0 | } |
232 | | |
233 | | /* store the old table and capacity */ |
234 | 7.14k | old_table = map->table; |
235 | 7.14k | old_cap = CAPACITY(map); |
236 | | |
237 | | /* double the size (capacity is base-2 logarithm, so this just means |
238 | | * increment it) and allocate new table */ |
239 | 7.14k | map->capacity = new_capacity; |
240 | 7.14k | map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map)); |
241 | | /* allocate new items, continuing to use the existing items. */ |
242 | | /* XXX - If this is called when the map is not full (i.e., when |
243 | | * map->count != old_cap, which can only happen if calling |
244 | | * wmem_map_reserve after inserting items), then some items are |
245 | | * orphaned. Alternatively we could do a more expensive copy in that |
246 | | * case. */ |
247 | 7.14k | map->items = wmem_alloc_array(map->data_allocator, wmem_map_item_t, CAPACITY(map) - map->count); |
248 | 7.14k | map->next_item = map->items; |
249 | | |
250 | | /* copy all the elements over from the old table */ |
251 | 458k | for (i=0; i<old_cap; i++) { |
252 | 451k | cur = old_table[i]; |
253 | 902k | while (cur) { |
254 | 451k | nxt = cur->next; |
255 | 451k | slot = MASK_HASH(map, cur->hash); |
256 | 451k | cur->next = map->table[slot]; |
257 | 451k | map->table[slot] = cur; |
258 | 451k | cur = nxt; |
259 | 451k | } |
260 | 451k | } |
261 | | |
262 | | /* free the old table */ |
263 | 7.14k | wmem_free(map->data_allocator, old_table); |
264 | 7.14k | } |
265 | | |
266 | | void |
267 | | wmem_map_destroy(wmem_map_t *map, bool free_keys _U_, bool free_values _U_) |
268 | 0 | { |
269 | | // TODO: call wmem_map_foreach_remove to free the keys and values |
270 | | // if asked. |
271 | 0 | if (map->deleted_items) { |
272 | | // Handle case where something calls wmem_map_destroy in its own |
273 | | // callback after the wmem_map_destroy_cb has been called. (This |
274 | | // function unregisters the callback so the reverse direction can't |
275 | | // happen.) |
276 | 0 | wmem_destroy_stack(map->deleted_items); |
277 | 0 | } |
278 | 0 | map->deleted_items = NULL; |
279 | 0 | if (map->metadata_allocator) { |
280 | 0 | wmem_unregister_callback(map->metadata_allocator, map->metadata_scope_cb_id); |
281 | 0 | } |
282 | 0 | if (map->data_allocator) { |
283 | 0 | wmem_unregister_callback(map->data_allocator, map->data_scope_cb_id); |
284 | 0 | } |
285 | 0 | wmem_free(map->data_allocator, map->table); |
286 | | // The arrays of items created before the last time the map grew the map |
287 | | // are orphaned and get freed when the data_allocator does. |
288 | 0 | wmem_free(map->data_allocator, map->items); |
289 | 0 | wmem_free(map->metadata_allocator, map); |
290 | 0 | } |
291 | | |
292 | | void * |
293 | | wmem_map_insert(wmem_map_t *map, const void *key, void *value) |
294 | 5.82M | { |
295 | 5.82M | wmem_map_item_t **item; |
296 | 5.82M | void *old_val; |
297 | | |
298 | | /* Make sure we have a table */ |
299 | 5.82M | if (map->table == NULL) { |
300 | 229k | wmem_map_init_table(map); |
301 | 229k | } |
302 | | |
303 | | /* get a pointer to the slot */ |
304 | 5.82M | uint32_t hash = HASH(map, key); |
305 | 5.82M | item = &(map->table[MASK_HASH(map, hash)]); |
306 | | |
307 | | /* check existing items in that slot */ |
308 | 7.17M | while (*item) { |
309 | 1.70M | if ((hash == (*item)->hash) && map->eql_func(key, (*item)->key)) { |
310 | | /* replace and return old value for this key */ |
311 | 352k | old_val = (*item)->value; |
312 | 352k | (*item)->value = value; |
313 | 352k | return old_val; |
314 | 352k | } |
315 | 1.34M | item = &((*item)->next); |
316 | 1.34M | } |
317 | | |
318 | | /* insert new item */ |
319 | 5.47M | if (G_UNLIKELY(wmem_stack_count(map->deleted_items))) { |
320 | 296 | *item = wmem_stack_pop(map->deleted_items); |
321 | 5.47M | } else { |
322 | 5.47M | ws_assert(map->next_item); |
323 | 5.47M | *item = map->next_item++; |
324 | 5.47M | } |
325 | | |
326 | 5.47M | (*item)->key = key; |
327 | 5.47M | (*item)->value = value; |
328 | 5.47M | (*item)->next = NULL; |
329 | 5.47M | (*item)->hash = hash; |
330 | | |
331 | 5.47M | map->count++; |
332 | | |
333 | | /* increase size if we are over-full */ |
334 | 5.47M | if (map->count >= CAPACITY(map)) { |
335 | 7.14k | wmem_map_grow(map, map->capacity + 1); |
336 | 7.14k | } |
337 | | |
338 | | /* no previous entry, return NULL */ |
339 | 5.47M | return NULL; |
340 | 5.82M | } |
341 | | |
342 | | bool |
343 | | wmem_map_contains(const wmem_map_t *map, const void *key) |
344 | 224k | { |
345 | 224k | wmem_map_item_t *item; |
346 | | |
347 | | /* Make sure we have map and a table */ |
348 | 224k | if (map == NULL || map->table == NULL) { |
349 | 10.1k | return false; |
350 | 10.1k | } |
351 | | |
352 | | /* find correct slot */ |
353 | 214k | uint32_t hash = HASH(map, key); |
354 | 214k | item = map->table[MASK_HASH(map, hash)]; |
355 | | |
356 | | /* scan list of items in this slot for the correct value */ |
357 | 252k | while (item) { |
358 | 156k | if ((hash == item->hash) && map->eql_func(key, item->key)) { |
359 | 119k | return true; |
360 | 119k | } |
361 | 37.4k | item = item->next; |
362 | 37.4k | } |
363 | | |
364 | 95.1k | return false; |
365 | 214k | } |
366 | | |
367 | | void * |
368 | | wmem_map_lookup(const wmem_map_t *map, const void *key) |
369 | 5.26M | { |
370 | 5.26M | wmem_map_item_t *item; |
371 | | |
372 | | /* Make sure we have map and a table */ |
373 | 5.26M | if (map == NULL || map->table == NULL) { |
374 | 932k | return NULL; |
375 | 932k | } |
376 | | |
377 | | /* find correct slot */ |
378 | 4.33M | uint32_t hash = HASH(map, key); |
379 | 4.33M | item = map->table[MASK_HASH(map, hash)]; |
380 | | |
381 | | /* scan list of items in this slot for the correct value */ |
382 | 5.98M | while (item) { |
383 | 3.54M | if ((hash == item->hash) && map->eql_func(key, item->key)) { |
384 | 1.90M | return item->value; |
385 | 1.90M | } |
386 | 1.64M | item = item->next; |
387 | 1.64M | } |
388 | | |
389 | 2.43M | return NULL; |
390 | 4.33M | } |
391 | | |
392 | | bool |
393 | | wmem_map_lookup_extended(const wmem_map_t *map, const void *key, const void **orig_key, void **value) |
394 | 6.41k | { |
395 | 6.41k | wmem_map_item_t *item; |
396 | | |
397 | | /* Make sure we have map and a table */ |
398 | 6.41k | if (map == NULL || map->table == NULL) { |
399 | 158 | return false; |
400 | 158 | } |
401 | | |
402 | | /* find correct slot */ |
403 | 6.25k | uint32_t hash = HASH(map, key); |
404 | 6.25k | item = map->table[MASK_HASH(map, hash)]; |
405 | | |
406 | | /* scan list of items in this slot for the correct value */ |
407 | 6.86k | while (item) { |
408 | 667 | if ((hash == item->hash) && map->eql_func(key, item->key)) { |
409 | 60 | if (orig_key) { |
410 | 60 | *orig_key = item->key; |
411 | 60 | } |
412 | 60 | if (value) { |
413 | 60 | *value = item->value; |
414 | 60 | } |
415 | 60 | return true; |
416 | 60 | } |
417 | 607 | item = item->next; |
418 | 607 | } |
419 | | |
420 | 6.19k | return false; |
421 | 6.25k | } |
422 | | |
423 | | void * |
424 | | wmem_map_remove(wmem_map_t *map, const void *key) |
425 | 387 | { |
426 | 387 | wmem_map_item_t **item, *tmp; |
427 | 387 | void *value; |
428 | | |
429 | | /* Make sure we have map and a table */ |
430 | 387 | if (map == NULL || map->table == NULL) { |
431 | 45 | return NULL; |
432 | 45 | } |
433 | | |
434 | | /* get a pointer to the slot */ |
435 | 342 | uint32_t hash = HASH(map, key); |
436 | 342 | item = &(map->table[MASK_HASH(map, hash)]); |
437 | | |
438 | | /* check the items in that slot */ |
439 | 362 | while (*item) { |
440 | 318 | if ((hash == (*item)->hash) && map->eql_func(key, (*item)->key)) { |
441 | | /* found it */ |
442 | 298 | tmp = (*item); |
443 | 298 | value = tmp->value; |
444 | 298 | (*item) = tmp->next; |
445 | 298 | wmem_stack_push(map->deleted_items, tmp); |
446 | 298 | map->count--; |
447 | 298 | return value; |
448 | 298 | } |
449 | 20 | item = &((*item)->next); |
450 | 20 | } |
451 | | |
452 | | /* didn't find it */ |
453 | 44 | return NULL; |
454 | 342 | } |
455 | | |
456 | | bool |
457 | | wmem_map_steal(wmem_map_t *map, const void *key) |
458 | 6 | { |
459 | 6 | wmem_map_item_t **item, *tmp; |
460 | | |
461 | | /* Make sure we have map and a table */ |
462 | 6 | if (map == NULL || map->table == NULL) { |
463 | 0 | return false; |
464 | 0 | } |
465 | | |
466 | | /* get a pointer to the slot */ |
467 | 6 | uint32_t hash = HASH(map, key); |
468 | 6 | item = &(map->table[MASK_HASH(map, hash)]); |
469 | | |
470 | | /* check the items in that slot */ |
471 | 8 | while (*item) { |
472 | 8 | if ((hash == (*item)->hash) && map->eql_func(key, (*item)->key)) { |
473 | | /* found it */ |
474 | 6 | tmp = (*item); |
475 | 6 | (*item) = tmp->next; |
476 | 6 | wmem_stack_push(map->deleted_items, tmp); |
477 | 6 | map->count--; |
478 | 6 | return true; |
479 | 6 | } |
480 | 2 | item = &((*item)->next); |
481 | 2 | } |
482 | | |
483 | | /* didn't find it */ |
484 | 0 | return false; |
485 | 6 | } |
486 | | |
487 | | wmem_list_t* |
488 | | wmem_map_get_keys(wmem_allocator_t *list_allocator, const wmem_map_t *map) |
489 | 0 | { |
490 | 0 | size_t capacity, i; |
491 | 0 | wmem_map_item_t *cur; |
492 | 0 | wmem_list_t* list = wmem_list_new(list_allocator); |
493 | |
|
494 | 0 | if (map->table != NULL) { |
495 | 0 | capacity = CAPACITY(map); |
496 | | |
497 | | /* copy all the elements into the list over from table */ |
498 | 0 | for (i=0; i<capacity; i++) { |
499 | 0 | cur = map->table[i]; |
500 | 0 | while (cur) { |
501 | 0 | wmem_list_prepend(list, (void*)cur->key); |
502 | 0 | cur = cur->next; |
503 | 0 | } |
504 | 0 | } |
505 | 0 | } |
506 | |
|
507 | 0 | return list; |
508 | 0 | } |
509 | | |
510 | | wmem_list_t* |
511 | | wmem_map_get_keys_sorted(wmem_allocator_t *list_allocator, const wmem_map_t *map, GCompareFunc compare_func) |
512 | 2.54k | { |
513 | 2.54k | size_t capacity, i; |
514 | 2.54k | wmem_map_item_t *cur; |
515 | 2.54k | wmem_list_t* list = wmem_list_new(list_allocator); |
516 | | |
517 | 2.54k | if (map->table != NULL) { |
518 | 2.54k | capacity = CAPACITY(map); |
519 | | |
520 | | /* copy all the elements into the list over from table */ |
521 | 114k | for (i=0; i<capacity; i++) { |
522 | 111k | cur = map->table[i]; |
523 | 149k | while (cur) { |
524 | 38.0k | wmem_list_insert_sorted(list, (void*)cur->key, compare_func); |
525 | 38.0k | cur = cur->next; |
526 | 38.0k | } |
527 | 111k | } |
528 | 2.54k | } |
529 | | |
530 | 2.54k | return list; |
531 | 2.54k | } |
532 | | |
533 | | void |
534 | | wmem_map_foreach(const wmem_map_t *map, GHFunc foreach_func, void * user_data) |
535 | 7.54k | { |
536 | 7.54k | wmem_map_item_t *cur; |
537 | 7.54k | unsigned i; |
538 | | |
539 | | /* Make sure we have a table */ |
540 | 7.54k | if (map == NULL || map->table == NULL) { |
541 | 3.09k | return; |
542 | 3.09k | } |
543 | | |
544 | 150k | for (i = 0; i < CAPACITY(map); i++) { |
545 | 146k | cur = map->table[i]; |
546 | 183k | while (cur) { |
547 | 37.1k | foreach_func((void *)cur->key, (void *)cur->value, user_data); |
548 | 37.1k | cur = cur->next; |
549 | 37.1k | } |
550 | 146k | } |
551 | 4.45k | } |
552 | | |
553 | | void* |
554 | | wmem_map_find(const wmem_map_t *map, GHRFunc foreach_func, void * user_data) |
555 | 0 | { |
556 | 0 | wmem_map_item_t **item; |
557 | 0 | unsigned i; |
558 | | |
559 | | /* Make sure we have a table */ |
560 | 0 | if (map == NULL || map->table == NULL) { |
561 | 0 | return 0; |
562 | 0 | } |
563 | | |
564 | 0 | for (i = 0; i < CAPACITY(map); i++) { |
565 | 0 | item = &(map->table[i]); |
566 | 0 | while (*item) { |
567 | 0 | if (foreach_func((void *)(*item)->key, (void *)(*item)->value, user_data)) { |
568 | 0 | return (*item)->value; |
569 | 0 | } else { |
570 | 0 | item = &((*item)->next); |
571 | 0 | } |
572 | 0 | } |
573 | 0 | } |
574 | 0 | return NULL; |
575 | 0 | } |
576 | | |
577 | | unsigned |
578 | | wmem_map_foreach_remove(wmem_map_t *map, GHRFunc foreach_func, void * user_data) |
579 | 0 | { |
580 | 0 | wmem_map_item_t **item, *tmp; |
581 | 0 | unsigned i, deleted = 0; |
582 | | |
583 | | /* Make sure we have a table */ |
584 | 0 | if (map == NULL || map->table == NULL) { |
585 | 0 | return 0; |
586 | 0 | } |
587 | | |
588 | 0 | for (i = 0; i < CAPACITY(map); i++) { |
589 | 0 | item = &(map->table[i]); |
590 | 0 | while (*item) { |
591 | 0 | if (foreach_func((void *)(*item)->key, (void *)(*item)->value, user_data)) { |
592 | 0 | tmp = *item; |
593 | 0 | *item = tmp->next; |
594 | 0 | if (map->deleted_items) |
595 | 0 | wmem_stack_push(map->deleted_items, tmp); |
596 | 0 | map->count--; |
597 | 0 | deleted++; |
598 | 0 | } else { |
599 | 0 | item = &((*item)->next); |
600 | 0 | } |
601 | 0 | } |
602 | 0 | } |
603 | 0 | return deleted; |
604 | 0 | } |
605 | | |
606 | | unsigned |
607 | | wmem_map_size(const wmem_map_t *map) |
608 | 5.55k | { |
609 | 5.55k | return (unsigned)map->count; |
610 | 5.55k | } |
611 | | |
612 | | size_t |
613 | | wmem_map_reserve(wmem_map_t *map, uint64_t capacity) |
614 | 48 | { |
615 | 48 | ws_return_val_if(!capacity, ((size_t)1) << map->min_capacity); |
616 | | |
617 | 48 | map->min_capacity = (unsigned)ws_ilog2(capacity) + 1; |
618 | | |
619 | 48 | map->min_capacity = MAX(map->min_capacity, WMEM_MAP_DEFAULT_CAPACITY); |
620 | | |
621 | 48 | if (map->table) { |
622 | | /* XXX - Should reserving after an item has been inserted be allowed? |
623 | | * Either we orphan some items in the old array or have to do a more |
624 | | * expensive copy operation. |
625 | | */ |
626 | 0 | ws_warning("Capacity should be reserved when first creating a map."); |
627 | 0 | wmem_map_grow(map, map->min_capacity); |
628 | 0 | } |
629 | | |
630 | 48 | map->min_capacity = MIN(map->min_capacity, 32); |
631 | | |
632 | 48 | return ((size_t)1) << map->min_capacity; |
633 | 48 | } |
634 | | |
635 | | /* Borrowed from Perl 5.18. This is based on Bob Jenkin's one-at-a-time |
636 | | * algorithm with some additional randomness seeded in. It is believed to be |
637 | | * generally secure against collision attacks. See |
638 | | * http://blog.booking.com/hardening-perls-hash-function.html |
639 | | */ |
640 | | uint32_t |
641 | | wmem_strong_hash(const uint8_t *buf, const size_t len) |
642 | 168k | { |
643 | | #ifdef HAVE_XXHASH |
644 | | return (uint32_t)XXH3_64bits_withSeed(buf, len, postseed); |
645 | | #else |
646 | 168k | const uint8_t * const end = (const uint8_t *)buf + len; |
647 | 168k | uint32_t hash = preseed + (uint32_t)len; |
648 | | |
649 | 1.16M | while (buf < end) { |
650 | 1.00M | hash += (hash << 10); |
651 | 1.00M | hash ^= (hash >> 6); |
652 | 1.00M | hash += *buf++; |
653 | 1.00M | } |
654 | | |
655 | 168k | hash += (hash << 10); |
656 | 168k | hash ^= (hash >> 6); |
657 | 168k | hash += ((uint8_t*)&postseed)[0]; |
658 | | |
659 | 168k | hash += (hash << 10); |
660 | 168k | hash ^= (hash >> 6); |
661 | 168k | hash += ((uint8_t*)&postseed)[1]; |
662 | | |
663 | 168k | hash += (hash << 10); |
664 | 168k | hash ^= (hash >> 6); |
665 | 168k | hash += ((uint8_t*)&postseed)[2]; |
666 | | |
667 | 168k | hash += (hash << 10); |
668 | 168k | hash ^= (hash >> 6); |
669 | 168k | hash += ((uint8_t*)&postseed)[3]; |
670 | | |
671 | 168k | hash += (hash << 10); |
672 | 168k | hash ^= (hash >> 6); |
673 | | |
674 | 168k | hash += (hash << 3); |
675 | 168k | hash ^= (hash >> 11); |
676 | 168k | return (hash + (hash << 15)); |
677 | 168k | #endif /* HAVE_XXHASH */ |
678 | 168k | } |
679 | | |
680 | | unsigned |
681 | | wmem_str_hash(const void *key) |
682 | 4.25M | { |
683 | | #ifdef HAVE_XXHASH |
684 | | return (uint32_t)XXH3_64bits_withSeed((const uint8_t*)key, strlen((const char*)key), postseed); |
685 | | #else |
686 | 4.25M | return g_str_hash(key); |
687 | 4.25M | #endif |
688 | 4.25M | } |
689 | | |
690 | | /* No need for a strong hash here, for our purpose fast functions are more important */ |
691 | | unsigned |
692 | | wmem_int64_hash(const void *key) |
693 | 0 | { |
694 | 0 | return g_int64_hash(key); |
695 | 0 | } |
696 | | |
697 | | unsigned |
698 | | wmem_double_hash(const void *key) |
699 | 0 | { |
700 | 0 | return g_double_hash(key); |
701 | 0 | } |
702 | | |
703 | | /* |
704 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
705 | | * |
706 | | * Local variables: |
707 | | * c-basic-offset: 4 |
708 | | * tab-width: 8 |
709 | | * indent-tabs-mode: nil |
710 | | * End: |
711 | | * |
712 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
713 | | * :indentSize=4:tabSize=8:noTabs=true: |
714 | | */ |