2014-04-22 00:30:06 +00:00
|
|
|
/* wmem_map.c
|
|
|
|
* Wireshark Memory Manager Hash Map
|
|
|
|
* Copyright 2014, Evan Huus <eapache@gmail.com>
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
2018-02-08 16:31:35 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
2014-04-22 00:30:06 +00:00
|
|
|
*/
|
2017-01-26 04:07:23 +00:00
|
|
|
#include "config.h"
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include "wmem_core.h"
|
2017-02-09 17:25:43 +00:00
|
|
|
#include "wmem_list.h"
|
2014-04-22 00:30:06 +00:00
|
|
|
#include "wmem_map.h"
|
2014-08-15 07:59:42 +00:00
|
|
|
#include "wmem_map_int.h"
|
2017-01-26 04:07:23 +00:00
|
|
|
#include "wmem_user_cb.h"
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
static guint32 x; /* Used for universal integer hashing (see the HASH macro) */
|
|
|
|
|
|
|
|
/* Used for the wmem_strong_hash() function */
|
|
|
|
static guint32 preseed;
|
|
|
|
static guint32 postseed;
|
|
|
|
|
|
|
|
void
|
|
|
|
wmem_init_hashing(void)
|
|
|
|
{
|
|
|
|
x = g_random_int();
|
2018-04-22 18:13:46 +00:00
|
|
|
if (G_UNLIKELY(x == 0))
|
2014-04-27 21:54:32 +00:00
|
|
|
x = 1;
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
preseed = g_random_int();
|
|
|
|
postseed = g_random_int();
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _wmem_map_item_t {
|
|
|
|
const void *key;
|
|
|
|
void *value;
|
|
|
|
struct _wmem_map_item_t *next;
|
|
|
|
} wmem_map_item_t;
|
|
|
|
|
|
|
|
struct _wmem_map_t {
|
|
|
|
guint count; /* number of items stored */
|
|
|
|
|
|
|
|
/* The base-2 logarithm of the actual size of the table. We store this
|
|
|
|
* value for efficiency in hashing, since finding the actual capacity
|
|
|
|
* becomes just a left-shift (see the CAPACITY macro) whereas taking
|
|
|
|
* logarithms is expensive. */
|
2016-07-16 16:44:47 +00:00
|
|
|
size_t capacity;
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
wmem_map_item_t **table;
|
|
|
|
|
|
|
|
GHashFunc hash_func;
|
|
|
|
GEqualFunc eql_func;
|
|
|
|
|
2020-06-07 21:41:39 +00:00
|
|
|
guint metadata_scope_cb_id;
|
|
|
|
guint data_scope_cb_id;
|
2017-01-26 04:07:23 +00:00
|
|
|
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_allocator_t *metadata_allocator;
|
|
|
|
wmem_allocator_t *data_allocator;
|
2014-04-22 00:30:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* As per the comment on the 'capacity' member of the wmem_map_t struct, this is
|
|
|
|
* the base-2 logarithm, meaning the actual default capacity is 2^5 = 32 */
|
|
|
|
#define WMEM_MAP_DEFAULT_CAPACITY 5
|
|
|
|
|
|
|
|
/* Macro for calculating the real capacity of the map by using a left-shift to
|
|
|
|
* do the 2^x operation. */
|
2018-02-28 02:50:33 +00:00
|
|
|
#define CAPACITY(MAP) (((size_t)1) << (MAP)->capacity)
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
/* Efficient universal integer hashing:
|
|
|
|
* https://en.wikipedia.org/wiki/Universal_hashing#Avoiding_modular_arithmetic
|
|
|
|
*/
|
|
|
|
#define HASH(MAP, KEY) \
|
|
|
|
((guint32)(((MAP)->hash_func(KEY) * x) >> (32 - (MAP)->capacity)))
|
|
|
|
|
2017-01-26 04:07:23 +00:00
|
|
|
static void
|
|
|
|
wmem_map_init_table(wmem_map_t *map)
|
|
|
|
{
|
|
|
|
map->count = 0;
|
|
|
|
map->capacity = WMEM_MAP_DEFAULT_CAPACITY;
|
2020-06-07 21:41:39 +00:00
|
|
|
map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map));
|
2017-01-26 04:07:23 +00:00
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
wmem_map_t *
|
|
|
|
wmem_map_new(wmem_allocator_t *allocator,
|
|
|
|
GHashFunc hash_func, GEqualFunc eql_func)
|
|
|
|
{
|
|
|
|
wmem_map_t *map;
|
|
|
|
|
|
|
|
map = wmem_new(allocator, wmem_map_t);
|
|
|
|
|
|
|
|
map->hash_func = hash_func;
|
|
|
|
map->eql_func = eql_func;
|
2020-06-07 21:41:39 +00:00
|
|
|
map->metadata_allocator = allocator;
|
|
|
|
map->data_allocator = allocator;
|
2017-01-28 17:31:29 +00:00
|
|
|
map->count = 0;
|
|
|
|
map->table = NULL;
|
2017-01-26 04:07:23 +00:00
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
wmem_map_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
wmem_map_t *map = (wmem_map_t*)user_data;
|
|
|
|
|
|
|
|
map->count = 0;
|
|
|
|
map->table = NULL;
|
|
|
|
|
|
|
|
if (event == WMEM_CB_DESTROY_EVENT) {
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_unregister_callback(map->metadata_allocator, map->metadata_scope_cb_id);
|
|
|
|
wmem_free(map->metadata_allocator, map);
|
2017-01-26 04:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
wmem_map_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
wmem_map_t *map = (wmem_map_t*)user_data;
|
|
|
|
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_unregister_callback(map->data_allocator, map->data_scope_cb_id);
|
2017-01-26 04:07:23 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
wmem_map_t *
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_map_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope,
|
2017-01-26 04:07:23 +00:00
|
|
|
GHashFunc hash_func, GEqualFunc eql_func)
|
|
|
|
{
|
|
|
|
wmem_map_t *map;
|
|
|
|
|
2020-06-07 21:41:39 +00:00
|
|
|
map = wmem_new(metadata_scope, wmem_map_t);
|
2017-01-26 04:07:23 +00:00
|
|
|
|
|
|
|
map->hash_func = hash_func;
|
|
|
|
map->eql_func = eql_func;
|
2020-06-07 21:41:39 +00:00
|
|
|
map->metadata_allocator = metadata_scope;
|
|
|
|
map->data_allocator = data_scope;
|
2017-01-28 17:31:29 +00:00
|
|
|
map->count = 0;
|
|
|
|
map->table = NULL;
|
2017-01-26 04:07:23 +00:00
|
|
|
|
2020-06-07 21:41:39 +00:00
|
|
|
map->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_map_destroy_cb, map);
|
|
|
|
map->data_scope_cb_id = wmem_register_callback(data_scope, wmem_map_reset_cb, map);
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
wmem_map_grow(wmem_map_t *map)
|
|
|
|
{
|
|
|
|
wmem_map_item_t **old_table, *cur, *nxt;
|
2016-07-16 16:44:47 +00:00
|
|
|
size_t old_cap, i;
|
|
|
|
guint slot;
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
/* store the old table and capacity */
|
|
|
|
old_table = map->table;
|
|
|
|
old_cap = CAPACITY(map);
|
|
|
|
|
|
|
|
/* double the size (capacity is base-2 logarithm, so this just means
|
|
|
|
* increment it) and allocate new table */
|
|
|
|
map->capacity++;
|
2020-06-07 21:41:39 +00:00
|
|
|
map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map));
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
/* copy all the elements over from the old table */
|
|
|
|
for (i=0; i<old_cap; i++) {
|
|
|
|
cur = old_table[i];
|
|
|
|
while (cur) {
|
|
|
|
nxt = cur->next;
|
|
|
|
slot = HASH(map, cur->key);
|
|
|
|
cur->next = map->table[slot];
|
|
|
|
map->table[slot] = cur;
|
|
|
|
cur = nxt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free the old table */
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_free(map->data_allocator, old_table);
|
2014-04-22 00:30:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
wmem_map_insert(wmem_map_t *map, const void *key, void *value)
|
|
|
|
{
|
|
|
|
wmem_map_item_t **item;
|
|
|
|
void *old_val;
|
|
|
|
|
2017-01-26 04:07:23 +00:00
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
wmem_map_init_table(map);
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
/* get a pointer to the slot */
|
|
|
|
item = &(map->table[HASH(map, key)]);
|
|
|
|
|
|
|
|
/* check existing items in that slot */
|
|
|
|
while (*item) {
|
|
|
|
if (map->eql_func(key, (*item)->key)) {
|
|
|
|
/* replace and return old value for this key */
|
|
|
|
old_val = (*item)->value;
|
|
|
|
(*item)->value = value;
|
|
|
|
return old_val;
|
|
|
|
}
|
|
|
|
item = &((*item)->next);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* insert new item */
|
2020-06-07 21:41:39 +00:00
|
|
|
(*item) = wmem_new(map->data_allocator, wmem_map_item_t);
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
(*item)->key = key;
|
|
|
|
(*item)->value = value;
|
|
|
|
(*item)->next = NULL;
|
|
|
|
|
|
|
|
map->count++;
|
|
|
|
|
|
|
|
/* increase size if we are over-full */
|
|
|
|
if (map->count >= CAPACITY(map)) {
|
|
|
|
wmem_map_grow(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no previous entry, return NULL */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-02-06 04:31:58 +00:00
|
|
|
gboolean
|
|
|
|
wmem_map_contains(wmem_map_t *map, const void *key)
|
|
|
|
{
|
|
|
|
wmem_map_item_t *item;
|
|
|
|
|
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find correct slot */
|
|
|
|
item = map->table[HASH(map, key)];
|
|
|
|
|
|
|
|
/* scan list of items in this slot for the correct value */
|
|
|
|
while (item) {
|
|
|
|
if (map->eql_func(key, item->key)) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
void *
|
|
|
|
wmem_map_lookup(wmem_map_t *map, const void *key)
|
|
|
|
{
|
|
|
|
wmem_map_item_t *item;
|
|
|
|
|
2017-01-26 04:07:23 +00:00
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
/* find correct slot */
|
|
|
|
item = map->table[HASH(map, key)];
|
|
|
|
|
|
|
|
/* scan list of items in this slot for the correct value */
|
|
|
|
while (item) {
|
|
|
|
if (map->eql_func(key, item->key)) {
|
|
|
|
return item->value;
|
|
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-02-06 04:31:58 +00:00
|
|
|
gboolean
|
|
|
|
wmem_map_lookup_extended(wmem_map_t *map, const void *key, const void **orig_key, void **value)
|
|
|
|
{
|
|
|
|
wmem_map_item_t *item;
|
|
|
|
|
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find correct slot */
|
|
|
|
item = map->table[HASH(map, key)];
|
|
|
|
|
|
|
|
/* scan list of items in this slot for the correct value */
|
|
|
|
while (item) {
|
|
|
|
if (map->eql_func(key, item->key)) {
|
|
|
|
if (orig_key) {
|
|
|
|
*orig_key = item->key;
|
|
|
|
}
|
|
|
|
if (value) {
|
|
|
|
*value = item->value;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
void *
|
|
|
|
wmem_map_remove(wmem_map_t *map, const void *key)
|
|
|
|
{
|
|
|
|
wmem_map_item_t **item, *tmp;
|
|
|
|
void *value;
|
|
|
|
|
2017-01-26 04:07:23 +00:00
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
/* get a pointer to the slot */
|
|
|
|
item = &(map->table[HASH(map, key)]);
|
|
|
|
|
|
|
|
/* check the items in that slot */
|
|
|
|
while (*item) {
|
|
|
|
if (map->eql_func(key, (*item)->key)) {
|
|
|
|
/* found it */
|
|
|
|
tmp = (*item);
|
|
|
|
value = tmp->value;
|
|
|
|
(*item) = tmp->next;
|
2020-06-07 21:41:39 +00:00
|
|
|
wmem_free(map->data_allocator, tmp);
|
2014-04-22 00:30:06 +00:00
|
|
|
map->count--;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
item = &((*item)->next);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* didn't find it */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-09 15:14:46 +00:00
|
|
|
gboolean
|
|
|
|
wmem_map_steal(wmem_map_t *map, const void *key)
|
|
|
|
{
|
|
|
|
wmem_map_item_t **item, *tmp;
|
|
|
|
|
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get a pointer to the slot */
|
|
|
|
item = &(map->table[HASH(map, key)]);
|
|
|
|
|
|
|
|
/* check the items in that slot */
|
|
|
|
while (*item) {
|
|
|
|
if (map->eql_func(key, (*item)->key)) {
|
|
|
|
/* found it */
|
|
|
|
tmp = (*item);
|
|
|
|
(*item) = tmp->next;
|
|
|
|
map->count--;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
item = &((*item)->next);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* didn't find it */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-02-09 17:25:43 +00:00
|
|
|
wmem_list_t*
|
|
|
|
wmem_map_get_keys(wmem_allocator_t *list_allocator, wmem_map_t *map)
|
|
|
|
{
|
|
|
|
size_t capacity, i;
|
|
|
|
wmem_map_item_t *cur;
|
|
|
|
wmem_list_t* list = wmem_list_new(list_allocator);
|
|
|
|
|
|
|
|
if (map->table != NULL) {
|
|
|
|
capacity = CAPACITY(map);
|
|
|
|
|
|
|
|
/* copy all the elements into the list over from table */
|
|
|
|
for (i=0; i<capacity; i++) {
|
|
|
|
cur = map->table[i];
|
|
|
|
while (cur) {
|
|
|
|
wmem_list_prepend(list, (void*)cur->key);
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2016-04-20 16:33:50 +00:00
|
|
|
void
|
|
|
|
wmem_map_foreach(wmem_map_t *map, GHFunc foreach_func, gpointer user_data)
|
|
|
|
{
|
|
|
|
wmem_map_item_t *cur;
|
|
|
|
unsigned i;
|
|
|
|
|
2017-01-26 04:07:23 +00:00
|
|
|
/* Make sure we have a table */
|
|
|
|
if (map->table == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-20 16:33:50 +00:00
|
|
|
for (i = 0; i < CAPACITY(map); i++) {
|
|
|
|
cur = map->table[i];
|
|
|
|
while (cur) {
|
|
|
|
foreach_func((gpointer)cur->key, (gpointer)cur->value, user_data);
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-21 16:11:50 +00:00
|
|
|
guint
|
|
|
|
wmem_map_size(wmem_map_t *map)
|
|
|
|
{
|
|
|
|
return map->count;
|
|
|
|
}
|
|
|
|
|
2014-04-22 00:30:06 +00:00
|
|
|
/* Borrowed from Perl 5.18. This is based on Bob Jenkin's one-at-a-time
|
|
|
|
* algorithm with some additional randomness seeded in. It is believed to be
|
|
|
|
* generally secure against collision attacks. See
|
|
|
|
* http://blog.booking.com/hardening-perls-hash-function.html
|
|
|
|
*/
|
|
|
|
guint32
|
|
|
|
wmem_strong_hash(const guint8 *buf, const size_t len)
|
|
|
|
{
|
|
|
|
const guint8 * const end = (const guint8 *)buf + len;
|
2014-04-23 12:57:43 +00:00
|
|
|
guint32 hash = preseed + (guint32)len;
|
2014-04-22 00:30:06 +00:00
|
|
|
|
|
|
|
while (buf < end) {
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
hash += *buf++;
|
|
|
|
}
|
|
|
|
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
hash += ((guint8*)&postseed)[0];
|
|
|
|
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
hash += ((guint8*)&postseed)[1];
|
|
|
|
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
hash += ((guint8*)&postseed)[2];
|
|
|
|
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
hash += ((guint8*)&postseed)[3];
|
|
|
|
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
|
|
|
|
hash += (hash << 3);
|
|
|
|
hash ^= (hash >> 11);
|
|
|
|
return (hash + (hash << 15));
|
|
|
|
}
|
|
|
|
|
|
|
|
guint
|
|
|
|
wmem_str_hash(gconstpointer key)
|
|
|
|
{
|
|
|
|
return wmem_strong_hash((const guint8 *)key, strlen((const char *)key));
|
|
|
|
}
|
|
|
|
|
|
|
|
guint
|
|
|
|
wmem_int64_hash(gconstpointer key)
|
|
|
|
{
|
|
|
|
return wmem_strong_hash((const guint8 *)key, sizeof(guint64));
|
|
|
|
}
|
|
|
|
|
|
|
|
guint
|
|
|
|
wmem_double_hash(gconstpointer key)
|
|
|
|
{
|
|
|
|
return wmem_strong_hash((const guint8 *)key, sizeof(double));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-07-26 18:43:17 +00:00
|
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
2014-04-22 00:30:06 +00:00
|
|
|
*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|