Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */