2 * Wireshark Memory Manager Hash Map
3 * Copyright 2014, Evan Huus <eapache@gmail.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include "wmem_core.h"
16 #include "wmem_list.h"
18 #include "wmem_map_int.h"
19 #include "wmem_user_cb.h"
21 static uint32_t x
; /* Used for universal integer hashing (see the HASH macro) */
23 /* Used for the wmem_strong_hash() function */
24 static uint32_t preseed
;
25 static uint32_t postseed
;
28 wmem_init_hashing(void)
31 if (G_UNLIKELY(x
== 0))
34 preseed
= g_random_int();
35 postseed
= g_random_int();
38 typedef struct _wmem_map_item_t
{
41 struct _wmem_map_item_t
*next
;
45 unsigned count
; /* number of items stored */
47 /* The base-2 logarithm of the actual size of the table. We store this
48 * value for efficiency in hashing, since finding the actual capacity
49 * becomes just a left-shift (see the CAPACITY macro) whereas taking
50 * logarithms is expensive. */
53 wmem_map_item_t
**table
;
58 unsigned metadata_scope_cb_id
;
59 unsigned data_scope_cb_id
;
61 wmem_allocator_t
*metadata_allocator
;
62 wmem_allocator_t
*data_allocator
;
65 /* As per the comment on the 'capacity' member of the wmem_map_t struct, this is
66 * the base-2 logarithm, meaning the actual default capacity is 2^5 = 32 */
67 #define WMEM_MAP_DEFAULT_CAPACITY 5
69 /* Macro for calculating the real capacity of the map by using a left-shift to
70 * do the 2^x operation. */
71 #define CAPACITY(MAP) (((size_t)1) << (MAP)->capacity)
73 /* Efficient universal integer hashing:
74 * https://en.wikipedia.org/wiki/Universal_hashing#Avoiding_modular_arithmetic
76 #define HASH(MAP, KEY) \
77 ((uint32_t)(((MAP)->hash_func(KEY) * x) >> (32 - (MAP)->capacity)))
80 wmem_map_init_table(wmem_map_t
*map
)
83 map
->capacity
= WMEM_MAP_DEFAULT_CAPACITY
;
84 map
->table
= wmem_alloc0_array(map
->data_allocator
, wmem_map_item_t
*, CAPACITY(map
));
88 wmem_map_new(wmem_allocator_t
*allocator
,
89 GHashFunc hash_func
, GEqualFunc eql_func
)
93 map
= wmem_new(allocator
, wmem_map_t
);
95 map
->hash_func
= hash_func
;
96 map
->eql_func
= eql_func
;
97 map
->metadata_allocator
= allocator
;
98 map
->data_allocator
= allocator
;
106 wmem_map_reset_cb(wmem_allocator_t
*allocator _U_
, wmem_cb_event_t event
,
109 wmem_map_t
*map
= (wmem_map_t
*)user_data
;
114 if (event
== WMEM_CB_DESTROY_EVENT
) {
115 wmem_unregister_callback(map
->metadata_allocator
, map
->metadata_scope_cb_id
);
116 wmem_free(map
->metadata_allocator
, map
);
123 wmem_map_destroy_cb(wmem_allocator_t
*allocator _U_
, wmem_cb_event_t event _U_
,
126 wmem_map_t
*map
= (wmem_map_t
*)user_data
;
128 wmem_unregister_callback(map
->data_allocator
, map
->data_scope_cb_id
);
134 wmem_map_new_autoreset(wmem_allocator_t
*metadata_scope
, wmem_allocator_t
*data_scope
,
135 GHashFunc hash_func
, GEqualFunc eql_func
)
139 map
= wmem_new(metadata_scope
, wmem_map_t
);
141 map
->hash_func
= hash_func
;
142 map
->eql_func
= eql_func
;
143 map
->metadata_allocator
= metadata_scope
;
144 map
->data_allocator
= data_scope
;
148 map
->metadata_scope_cb_id
= wmem_register_callback(metadata_scope
, wmem_map_destroy_cb
, map
);
149 map
->data_scope_cb_id
= wmem_register_callback(data_scope
, wmem_map_reset_cb
, map
);
155 wmem_map_grow(wmem_map_t
*map
)
157 wmem_map_item_t
**old_table
, *cur
, *nxt
;
161 /* store the old table and capacity */
162 old_table
= map
->table
;
163 old_cap
= CAPACITY(map
);
165 /* double the size (capacity is base-2 logarithm, so this just means
166 * increment it) and allocate new table */
168 map
->table
= wmem_alloc0_array(map
->data_allocator
, wmem_map_item_t
*, CAPACITY(map
));
170 /* copy all the elements over from the old table */
171 for (i
=0; i
<old_cap
; i
++) {
175 slot
= HASH(map
, cur
->key
);
176 cur
->next
= map
->table
[slot
];
177 map
->table
[slot
] = cur
;
182 /* free the old table */
183 wmem_free(map
->data_allocator
, old_table
);
187 wmem_map_insert(wmem_map_t
*map
, const void *key
, void *value
)
189 wmem_map_item_t
**item
;
192 /* Make sure we have a table */
193 if (map
->table
== NULL
) {
194 wmem_map_init_table(map
);
197 /* get a pointer to the slot */
198 item
= &(map
->table
[HASH(map
, key
)]);
200 /* check existing items in that slot */
202 if (map
->eql_func(key
, (*item
)->key
)) {
203 /* replace and return old value for this key */
204 old_val
= (*item
)->value
;
205 (*item
)->value
= value
;
208 item
= &((*item
)->next
);
211 /* insert new item */
212 (*item
) = wmem_new(map
->data_allocator
, wmem_map_item_t
);
215 (*item
)->value
= value
;
216 (*item
)->next
= NULL
;
220 /* increase size if we are over-full */
221 if (map
->count
>= CAPACITY(map
)) {
225 /* no previous entry, return NULL */
230 wmem_map_contains(wmem_map_t
*map
, const void *key
)
232 wmem_map_item_t
*item
;
234 /* Make sure we have map and a table */
235 if (map
== NULL
|| map
->table
== NULL
) {
239 /* find correct slot */
240 item
= map
->table
[HASH(map
, key
)];
242 /* scan list of items in this slot for the correct value */
244 if (map
->eql_func(key
, item
->key
)) {
254 wmem_map_lookup(wmem_map_t
*map
, const void *key
)
256 wmem_map_item_t
*item
;
258 /* Make sure we have map and a table */
259 if (map
== NULL
|| map
->table
== NULL
) {
263 /* find correct slot */
264 item
= map
->table
[HASH(map
, key
)];
266 /* scan list of items in this slot for the correct value */
268 if (map
->eql_func(key
, item
->key
)) {
278 wmem_map_lookup_extended(wmem_map_t
*map
, const void *key
, const void **orig_key
, void **value
)
280 wmem_map_item_t
*item
;
282 /* Make sure we have map and a table */
283 if (map
== NULL
|| map
->table
== NULL
) {
287 /* find correct slot */
288 item
= map
->table
[HASH(map
, key
)];
290 /* scan list of items in this slot for the correct value */
292 if (map
->eql_func(key
, item
->key
)) {
294 *orig_key
= item
->key
;
297 *value
= item
->value
;
308 wmem_map_remove(wmem_map_t
*map
, const void *key
)
310 wmem_map_item_t
**item
, *tmp
;
313 /* Make sure we have map and a table */
314 if (map
== NULL
|| map
->table
== NULL
) {
318 /* get a pointer to the slot */
319 item
= &(map
->table
[HASH(map
, key
)]);
321 /* check the items in that slot */
323 if (map
->eql_func(key
, (*item
)->key
)) {
328 wmem_free(map
->data_allocator
, tmp
);
332 item
= &((*item
)->next
);
340 wmem_map_steal(wmem_map_t
*map
, const void *key
)
342 wmem_map_item_t
**item
, *tmp
;
344 /* Make sure we have map and a table */
345 if (map
== NULL
|| map
->table
== NULL
) {
349 /* get a pointer to the slot */
350 item
= &(map
->table
[HASH(map
, key
)]);
352 /* check the items in that slot */
354 if (map
->eql_func(key
, (*item
)->key
)) {
361 item
= &((*item
)->next
);
369 wmem_map_get_keys(wmem_allocator_t
*list_allocator
, wmem_map_t
*map
)
372 wmem_map_item_t
*cur
;
373 wmem_list_t
* list
= wmem_list_new(list_allocator
);
375 if (map
->table
!= NULL
) {
376 capacity
= CAPACITY(map
);
378 /* copy all the elements into the list over from table */
379 for (i
=0; i
<capacity
; i
++) {
382 wmem_list_prepend(list
, (void*)cur
->key
);
392 wmem_map_foreach(wmem_map_t
*map
, GHFunc foreach_func
, void * user_data
)
394 wmem_map_item_t
*cur
;
397 /* Make sure we have a table */
398 if (map
== NULL
|| map
->table
== NULL
) {
402 for (i
= 0; i
< CAPACITY(map
); i
++) {
405 foreach_func((void *)cur
->key
, (void *)cur
->value
, user_data
);
412 wmem_map_find(wmem_map_t
*map
, GHRFunc foreach_func
, void * user_data
)
414 wmem_map_item_t
**item
;
417 /* Make sure we have a table */
418 if (map
== NULL
|| map
->table
== NULL
) {
422 for (i
= 0; i
< CAPACITY(map
); i
++) {
423 item
= &(map
->table
[i
]);
425 if (foreach_func((void *)(*item
)->key
, (void *)(*item
)->value
, user_data
)) {
426 return (*item
)->value
;
428 item
= &((*item
)->next
);
436 wmem_map_foreach_remove(wmem_map_t
*map
, GHRFunc foreach_func
, void * user_data
)
438 wmem_map_item_t
**item
, *tmp
;
439 unsigned i
, deleted
= 0;
441 /* Make sure we have a table */
442 if (map
== NULL
|| map
->table
== NULL
) {
446 for (i
= 0; i
< CAPACITY(map
); i
++) {
447 item
= &(map
->table
[i
]);
449 if (foreach_func((void *)(*item
)->key
, (void *)(*item
)->value
, user_data
)) {
452 wmem_free(map
->data_allocator
, tmp
);
456 item
= &((*item
)->next
);
464 wmem_map_size(wmem_map_t
*map
)
469 /* Borrowed from Perl 5.18. This is based on Bob Jenkin's one-at-a-time
470 * algorithm with some additional randomness seeded in. It is believed to be
471 * generally secure against collision attacks. See
472 * http://blog.booking.com/hardening-perls-hash-function.html
475 wmem_strong_hash(const uint8_t *buf
, const size_t len
)
477 const uint8_t * const end
= (const uint8_t *)buf
+ len
;
478 uint32_t hash
= preseed
+ (uint32_t)len
;
481 hash
+= (hash
<< 10);
486 hash
+= (hash
<< 10);
488 hash
+= ((uint8_t*)&postseed
)[0];
490 hash
+= (hash
<< 10);
492 hash
+= ((uint8_t*)&postseed
)[1];
494 hash
+= (hash
<< 10);
496 hash
+= ((uint8_t*)&postseed
)[2];
498 hash
+= (hash
<< 10);
500 hash
+= ((uint8_t*)&postseed
)[3];
502 hash
+= (hash
<< 10);
506 hash
^= (hash
>> 11);
507 return (hash
+ (hash
<< 15));
511 wmem_str_hash(const void *key
)
513 return wmem_strong_hash((const uint8_t *)key
, strlen((const char *)key
));
517 wmem_int64_hash(const void *key
)
519 return wmem_strong_hash((const uint8_t *)key
, sizeof(uint64_t));
523 wmem_double_hash(const void *key
)
525 return wmem_strong_hash((const uint8_t *)key
, sizeof(double));
529 * Editor modelines - https://www.wireshark.org/tools/modelines.html
534 * indent-tabs-mode: nil
537 * vi: set shiftwidth=4 tabstop=8 expandtab:
538 * :indentSize=4:tabSize=8:noTabs=true: