You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
5.9 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include "utils.h"
#define HASH_TABLE_MIN_HASH_SIZE 16
#define HASH_TABLE_MAX_HASH_SIZE 10240
#define HASH_TABLE_MIN_LOAD_FACTOR 0.1
#define HASH_TABLE_MAX_LOAD_FACTOR 0.8
#define HASH_TABLE_HASH_FUNC(tbl, key) ((key) % (tbl)->hash_sz)
HashTable *hashtbl_create()
{
HashTable *tbl = (HashTable *)skynet_malloc(sizeof(HashTable));
tbl->hash_sz = HASH_TABLE_MIN_HASH_SIZE;
tbl->count = 0;
tbl->min_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MIN_LOAD_FACTOR);
tbl->max_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MAX_LOAD_FACTOR);
HashTableNode **nodes = (HashTableNode **)skynet_malloc(sizeof(HashTableNode *) * tbl->hash_sz);
int n;
for (n = 0; n < tbl->hash_sz; n++)
{
nodes[n] = NULL;
}
tbl->nodes = nodes;
return tbl;
}
void hashtbl_destroy(HashTable *tbl)
{
int n;
HashTableNode *p, *next_p;
for (n = 0; n < tbl->hash_sz; n++)
{
p = tbl->nodes[n];
tbl->nodes[n] = NULL;
while (p != NULL)
{
next_p = p->next;
skynet_free(p);
p = next_p;
}
}
skynet_free(tbl->nodes);
skynet_free(tbl);
return;
}
static int _hashtbl_resize(HashTable *tbl, int new_hash_sz)
{
HashTableNode **new_nodes = (HashTableNode **)skynet_malloc(sizeof(HashTableNode *) * new_hash_sz);
int old_hash_sz = tbl->hash_sz;
HashTableNode **old_nodes = tbl->nodes;
tbl->nodes = new_nodes;
tbl->hash_sz = new_hash_sz;
tbl->min_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MIN_LOAD_FACTOR);
tbl->max_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MAX_LOAD_FACTOR);
int n, hash_key;
HashTableNode *p, *next_p;
for (n = 0; n < new_hash_sz; n++)
{
new_nodes[n] = NULL;
}
for (n = 0; n < old_hash_sz; n++)
{
p = old_nodes[n];
old_nodes[n] = NULL;
while (p != NULL)
{
next_p = p->next;
hash_key = p->key % new_hash_sz;
p->next = new_nodes[hash_key];
new_nodes[hash_key] = p;
p = next_p;
}
}
skynet_free(old_nodes);
return 0;
}
static int _hashtbl_insert(HashTable *tbl, int hash_key, uint64_t key, void *value)
{
HashTableNode *node = skynet_malloc(sizeof(HashTableNode));
node->key = key;
node->value = value;
node->next = tbl->nodes[hash_key];
tbl->nodes[hash_key] = node;
tbl->count++;
if (tbl->hash_sz < HASH_TABLE_MAX_HASH_SIZE && tbl->count > tbl->max_resize_cnt)
{
_hashtbl_resize(tbl, tbl->hash_sz * 2);
}
return 0;
}
int hashtbl_insert(HashTable *tbl, uint64_t key, void *value)
{
int hash_key = HASH_TABLE_HASH_FUNC(tbl, key);
HashTableNode *p = tbl->nodes[hash_key];
while (p != NULL)
{
if (p->key == key)
{
printf("table_insert fail, key<%llu>, already exist\n", key);
assert(0);
return 1;
}
p = p->next;
}
return _hashtbl_insert(tbl, hash_key, key, value);
}
int hashtbl_upsert(HashTable *tbl, uint64_t key, void *value)
{
int hash_key = HASH_TABLE_HASH_FUNC(tbl, key);
HashTableNode *p = tbl->nodes[hash_key];
while (p != NULL)
{
if (p->key == key)
{
p->value = value;
return 0;
}
p = p->next;
}
return _hashtbl_insert(tbl, hash_key, key, value);
}
int hashtbl_has(HashTable *tbl, uint64_t key)
{
HashTableNode *p = tbl->nodes[HASH_TABLE_HASH_FUNC(tbl, key)];
while (p != NULL)
{
if (p->key == key)
{
return 1;
}
p = p->next;
}
return 0;
}
void *hashtbl_get(HashTable *tbl, uint64_t key)
{
HashTableNode *p = tbl->nodes[HASH_TABLE_HASH_FUNC(tbl, key)];
while (p != NULL)
{
if (p->key == key)
{
return p->value;
}
p = p->next;
}
return 0;
}
int hashtbl_remove(HashTable *tbl, uint64_t key)
{
int hash_key = HASH_TABLE_HASH_FUNC(tbl, key);
HashTableNode *free_p;
HashTableNode **p = &(tbl->nodes[hash_key]);
int hash_sz = tbl->hash_sz;
while ((*p) != NULL)
{
if ((*p)->key != key)
{
p = &((*p)->next);
continue;
}
free_p = *p;
*p = free_p->next;
skynet_free(free_p);
free_p = NULL;
tbl->count--;
if ((hash_sz > HASH_TABLE_MIN_LOAD_FACTOR) && (tbl->count < tbl->min_resize_cnt))
{
int min_hash_sz = (int)(tbl->count / HASH_TABLE_MAX_LOAD_FACTOR) + 1;
if (min_hash_sz < HASH_TABLE_MIN_HASH_SIZE)
{
min_hash_sz = HASH_TABLE_MIN_HASH_SIZE;
}
int max_hash_sz = 2 * min_hash_sz;
while (hash_sz >= min_hash_sz)
{
if (hash_sz < max_hash_sz)
{
break;
}
hash_sz = hash_sz / 2;
}
_hashtbl_resize(tbl, hash_sz);
}
return 1;
}
return 0;
}
void hashtbl_iter_reset(HashTableIter *iter)
{
iter->hash_sz = -1;
iter->count = 0;
iter->node = NULL;
return;
}
int hashtbl_iter(HashTable *tbl, HashTableIter *iter)
{
if (tbl->count <= iter->count)
{
return 0;
}
if (iter->node)
{
iter->node = iter->node->next;
}
while (!iter->node)
{
iter->hash_sz++;
if (iter->hash_sz >= tbl->hash_sz)
{
break;
}
iter->node = tbl->nodes[iter->hash_sz];
}
if (!iter->node)
{
return 0;
}
iter->count++;
return 1;
}
void hashtbl_foreach(HashTable *tbl, HashTableIterFunc func, void *ud)
{
HashTableIter iter;
hashtbl_iter_reset(&iter);
while (hashtbl_iter(tbl, &iter))
{
func(ud, iter.node->key, iter.node->value);
}
}