CAlgHashList.h
#ifndef _C_ALG_HASH_LIST_H_
#define _C_ALG_HASH_LIST_H_
#include
#include
#include
#include "CAlgShareList.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct{
unsigned int k_size;
unsigned int v_size;
unsigned int max_count;
unsigned int trunk_size;
unsigned int trunk_mask;
CAlgShareList free_list;
#ifdef WIN32
#pragma warning(disable:4200)
#endif
CAlgShareList hlist[0];
#ifdef WIN32
#pragma warning(default:4200)
#endif
}CAlgHashList;
typedef struct{
int pos;
CAlgShareList *list;
CAlgShareNode *node_base; //node base address
unsigned char *kv_base; //kv base address
unsigned char *kv;
}HashListRC;
typedef unsigned int (*Alg_Hash_F)(const void* k, unsigned int size);
static inline unsigned int CAlgHashList_Pow2(unsigned int mask)
{
int order = 31;
while((mask & (1<< order))== 0 && order > 0)
order --;
return 1 << order;
}
static inline unsigned int CAlgHashList_MemSize(unsigned int key_size, unsigned int val_size,
unsigned int trunk_size, unsigned int max_count)
{
//make trunk_size is pow of 2
trunk_size = CAlgHashList_Pow2(trunk_size);
return sizeof(CAlgHashList)
+ sizeof(CAlgShareList) * trunk_size
+ sizeof(CAlgShareNode) * max_count
+ (key_size + val_size) * max_count;
}
static inline void CAlgHashList_Create(CAlgHashList* h, unsigned int key_size, unsigned int val_size,
unsigned int trunk_size, unsigned int max_count)
{
unsigned int i;
CAlgShareNode* n = (CAlgShareNode*)((unsigned char*)(h->hlist) + sizeof(CAlgShareList) * trunk_size);
trunk_size = CAlgHashList_Pow2(trunk_size);
h->k_size = key_size;
h->v_size = val_size;
h->trunk_size = trunk_size;
h->max_count = max_count;
h->trunk_mask = h->trunk_size - 1;
CAlgShareList_ReSet(&h->free_list, key_size);
//add ShareNode to free list
n->curr_data = NULL;
CAlgShareList_InsertPointer(&h->free_list, NULL, NULL, n, 0);
for(i=1; i
(n+1)->curr_data = NULL;
CAlgShareList_InsertPointer(&h->free_list,
n, NULL, n+1, i);
}
for(i=0; i
//NOTE: only set key_size, but the list node pointer store key_size + val_size
CAlgShareList_ReSet(&h->hlist[i], key_size);
}
}
static inline unsigned int CAlgHashList_DefaultHash(const void* k, unsigned int k_size)
{
unsigned int i, hash = 0;
const unsigned char *p = (const unsigned char *)k;
for (i = 0; i< k_size; i++, p++) {
hash = hash * 33 + *p;
}
return hash;
}
static inline int CAlgHashList_Find(CAlgHashList* h, const void* k, HashListRC* rc,
Alg_Cmp_F cmp_f, Alg_Hash_F hash_f)
{
CAlgShareList* list;
CAlgShareNode* node_base = (CAlgShareNode*)(&h->hlist[h->trunk_size]);
int pos;
unsigned int hash_v ;
unsigned char* kv_base = ((unsigned char*)node_base) + sizeof(CAlgShareNode)*h->max_count;
unsigned int kv_size = h->k_size + h->v_size;
if(hash_f == NULL ) hash_f = CAlgHashList_DefaultHash;
if(cmp_f == NULL ) cmp_f = memcmp;
hash_v = hash_f(k, h->k_size);
list = &h->hlist[hash_v & h->trunk_mask];
pos = list->head;
rc->kv = NULL;
rc->list = list;
rc->node_base = node_base;
rc->kv_base = kv_base;
rc->pos = -1;
while(pos >=0 && pos < (int)h->max_count)
{
if(cmp_f(k, kv_base+kv_size*pos, h->k_size) == 0)
{
rc->kv = &kv_base[kv_size*pos]; //return key-value pointer
rc->pos = node_base[pos].curr;
rc->list = list;
return 0;
}
pos = node_base[pos].next;
}
return -1;
}
static inline int CAlgHashList_Set(CAlgHashList* h, void* k, void* v,
Alg_Cmp_F cmp_f, Alg_Hash_F hash_f)
{
HashListRC rc;
CAlgShareNode *prev, *next, *n;
CAlgHashList_Find(h, k, &rc, cmp_f, hash_f);
prev = next = NULL;
if(rc.kv)
{
if(v)
{//update value
memcpy(rc.kv + h->k_size, v, h->v_size);
return 0;
//return rc.kv + h->k_size; //return v pointer
}
else
{//remove
//remove from hash trunk list
n= &rc.node_base[rc.pos];
if(n->prev != ALG_SHARE_LIST_INVALID_POS)
prev = &rc.node_base[n->prev];
if(n->next != ALG_SHARE_LIST_INVALID_POS)
next = &rc.node_base[n->next];
CAlgShareList_RemoveNodePointer(rc.list, prev, next, n);
//link this node to free list tail
if(h->free_list.tail != ALG_SHARE_LIST_INVALID_POS)
prev = &rc.node_base[h->free_list.tail];
else
prev = NULL;
n->curr_data = NULL;
CAlgShareList_InsertPointer(&h->free_list, prev, NULL, n, n->curr);
return 0;
}
}
else if(v && h->free_list.elem_count > 0)
{// add
n = &(rc.node_base[h->free_list.head]);
//remove free list head
if(n->next != ALG_SHARE_LIST_INVALID_POS)
next = &(rc.node_base[n->next]);
CAlgShareList_RemoveNodePointer(&h->free_list, NULL, next, n);
//copy data
memcpy(&rc.kv_base[(h->k_size+h->v_size)*n->curr], k, h->k_size);
memcpy(&rc.kv_base[(h->k_size+h->v_size)*n->curr+h->k_size], v, h->v_size);
//link the node to hash trunk list tail
if(rc.list->tail != ALG_SHARE_LIST_INVALID_POS)
prev = &(rc.node_base[rc.list->tail]);
n->curr_data = (void*)rc.list; //node current data points to corresponding list
CAlgShareList_InsertPointer(rc.list, prev, NULL, n, n->curr);
return 0;
}
return -1;
}
static inline void* CAlgHashList_Get(CAlgHashList* h, const void* k,
Alg_Cmp_F cmp_f, Alg_Hash_F hash_f)
{
HashListRC rc;
if(0 == CAlgHashList_Find(h, k, &rc, memcmp, NULL))
return (rc.kv + h->k_size);
return NULL;
}
#define FOR_EACH_HASH_NODE(h, k, v) \
{\
CAlgShareNode* __node = (CAlgShareNode*)(&((h)->hlist[h->trunk_size]));\
unsigned char* __kv_base = ((unsigned char*)__node) \
+ sizeof(CAlgShareNode)*((h)->max_count);\
unsigned int __kv_size = (h)->k_size + (h)->v_size; \
unsigned int __ii; \
for(__ii=0; __iimax_count; __ii++, __node++){ \
if(__node->curr_data == NULL) continue; \
if(k) memcpy(k, __kv_base+__kv_size*__node->curr, h->k_size); \
if(v) memcpy(v, __kv_base+__kv_size*__node->curr + h->k_size, h->v_size);
#define FOR_EACH_HASH_NODE_END() }}
#define FOR_EACH_HASH_LIST(h, k, v) \
{\
CAlgShareNode* __node_base = (CAlgShareNode*)(&((h)->hlist[h->trunk_size]));\
CAlgShareNode* __node;\
unsigned char* __kv_base = ((unsigned char*)__node_base) \
+ sizeof(CAlgShareNode)*((h)->max_count);\
CAlgShareList* __thelist ; \
unsigned int __kv_size = (h)->k_size + (h)->v_size; \
unsigned int __ii;\
int __pos;\
for(__ii=0; __iitrunk_size; __ii++){\
__thelist = &h->hlist[__ii];
#define FOR_EACH_HASH_LIST_NODE(h, k, v) \
__pos = __thelist->head;\
while(__pos != ALG_SHARE_LIST_INVALID_POS){\
__node = &__node_base[__pos]; \
if(k) memcpy(k, __kv_base+__kv_size*__node->curr, h->k_size); \
if(v) memcpy(v, __kv_base+__kv_size*__node->curr + h->k_size, h->v_size);
#define FOR_EACH_HASH_LIST_NODE_END() __pos = __node->next;}
#define FOR_EACH_HASH_LIST_END() }}
static inline int CAlgHashList_Count(CAlgHashList* hlist)
{
return hlist->max_count - hlist->free_list.elem_count;
}
static inline void CAlgHashList_Print(CAlgHashList* hlist, int detail)
{
CAlgShareNode* nb = (CAlgShareNode*)(&((hlist)->hlist[hlist->trunk_size]));
int free_pos;
free_pos = hlist->free_list.head;
if(detail)
{
while(free_pos != ALG_SHARE_LIST_INVALID_POS)
{
printf("\t(%6d, %6d, %6d) \n", nb[free_pos].prev,
nb[free_pos].curr, nb[free_pos].next);
free_pos = nb[free_pos].next;
}
}
printf("\n");
FOR_EACH_HASH_LIST(hlist, NULL, NULL)
printf("trunk_%04d size: %4d addr: 0X%p\n", __ii, __thelist->elem_count, __thelist);
if(detail){
FOR_EACH_HASH_LIST_NODE(hlist, NULL, NULL)
printf("\t(%6d, %6d, %6d) list: 0X%p\n",
__node->prev, __node->curr, __node->next,
__node->curr_data);
assert(__node->curr_data == (void*)__thelist);
FOR_EACH_HASH_LIST_NODE_END()
printf("\n");
}
FOR_EACH_HASH_LIST_END()
printf("hlist usage: %d out of %u (%.2f%%), free nodes left: %d\n",
CAlgHashList_Count(hlist), hlist->max_count,
(float)CAlgHashList_Count(hlist)/(float)hlist->max_count * 100,
hlist->free_list.elem_count);
}
#ifdef __cplusplus
}
#endif
#endif
//endif
单元测试:
#include
#include
#include
#include
#include "CAlgHashList.h"
typedef struct {
int a;
int b;
}HashKey;
typedef struct {
int v;
int f;
}HashValue;
void TestCAlgHashList_Print(CAlgHashList* hlist, int all_list, int detail)
{
HashKey k;
HashValue v;
if(!all_list){
FOR_EACH_HASH_NODE(hlist, &k, &v)
printf("\t(%3d, %3d, %3d) k.a=%3d, k.b=%3d, v=%3d list: 0X%p\n",
__node->prev, __node->curr, __node->next,
k.a, k.b, v.v, __node->curr_data);
FOR_EACH_HASH_NODE_END()
printf("\n\n");
return ;
}
FOR_EACH_HASH_LIST(hlist, &k, &v)
printf("trunk_%04d size: %4d addr: 0X%p\n", __ii, __thelist->elem_count, __thelist);
if(detail){
FOR_EACH_HASH_LIST_NODE(hlist, &k, &v)
printf("\t(%3d, %3d, %3d) k.a=%3d, k.b=%3d, v=%3d list: 0X%p\n",
__node->prev, __node->curr, __node->next,
k.a, k.b, v.v, __node->curr_data);
assert(__node->curr_data == (void*)__thelist);
FOR_EACH_HASH_LIST_NODE_END()
printf("\n");
}
FOR_EACH_HASH_LIST_END()
}
void TestCAlgHashList_AddRemoveFind(unsigned int trunk_size, unsigned int node_size,
int print, int chkfind)
{
HashKey k;
HashValue v;
unsigned int mem_size = CAlgHashList_MemSize(sizeof(HashKey), sizeof(HashValue), trunk_size, node_size);
CAlgHashList* hlist;
unsigned int i;
HashListRC rc;
hlist = (CAlgHashList*)malloc(mem_size);
CAlgHashList_Create(hlist, sizeof(HashKey), sizeof(HashValue), trunk_size, node_size);
if(print){
CAlgHashList_Print(hlist, 0);
printf("//////////////////////////////////////////////////////////////////////////\n");
printf("//add full\n");
}
//hash table empty, find fail
for(i=0; i<2; i++)
{
k.a = 100 + i;
k.b = 200 + i;
v.v = i+1;
v.f = 0xee;
if(chkfind){
assert(CAlgHashList_Find(hlist, &k, &rc, NULL, NULL) == -1);
assert(rc.kv_base != NULL);
assert(rc.node_base != NULL);
assert(rc.kv == NULL);
assert(rc.pos == -1);
assert(rc.list != NULL);
}
}
//add full
for(i=0; i
{
k.a = 100 + i;
k.b = 200 + i;
v.v = i+1;
v.f = 0xee;
assert(CAlgHashList_Set(hlist, &k, &v, NULL, NULL) == 0);
}
//add full so add fail
k.a = k.b = 0;
assert(CAlgHashList_Set(hlist, &k, &v, NULL, NULL) == -1);
if(print){
TestCAlgHashList_Print(hlist, 1, 1);
}
if(chkfind){
//find return ok
for(i=0; i
{
k.a = 100 + i;
k.b = 200 + i;
v.v = i+1;
v.f = 0xee;
assert(CAlgHashList_Find(hlist, &k, &rc, NULL, NULL) == 0);
assert(rc.kv_base != NULL);
assert(rc.node_base != NULL);
assert(rc.kv != NULL);
assert(rc.pos > -1);
assert(rc.list != NULL);
}
}
if(print){
printf("//////////////////////////////////////////////////////////////////////////\n");
printf("//remove odd num\n");
}
//remove odd num
for(i=0; i2; i++)
{
k.a = 100 + i*2 + 1;
k.b = 200 + i*2 + 1;
//remove
assert(CAlgHashList_Set(hlist, &k, NULL, NULL, NULL) == 0);
if(chkfind){
//find fail
assert(CAlgHashList_Find(hlist, &k, &rc, NULL, NULL) == -1);
assert(rc.kv_base != NULL);
assert(rc.node_base != NULL);
assert(rc.kv == NULL);
assert(rc.pos == -1);
assert(rc.list != NULL);
}
}
if(print){
TestCAlgHashList_Print(hlist, 1, 1);
CAlgHashList_Print(hlist, 1);
printf("//////////////////////////////////////////////////////////////////////////\n");
printf("//update event num\n");
}
//update
for(i=0; i2; i++)
{
k.a = 100 + i*2;
k.b = 200 + i*2;
v.v = 300 + i;
v.f = 0xdd;
//update
assert(CAlgHashList_Set(hlist, &k, &v, NULL, NULL) == 0);
}
if(print){
TestCAlgHashList_Print(hlist, 1, 1);
printf("//////////////////////////////////////////////////////////////////////////\n");
printf("//remove even num\n");
}
//remove even num
for(i=0; i2; i++)
{
k.a = 100 + i*2;
k.b = 200 + i*2;
//remove
assert(CAlgHashList_Set(hlist, &k, NULL, NULL, NULL) == 0);
if(chkfind){
//find fail
assert(CAlgHashList_Find(hlist, &k, &rc, NULL, NULL) == -1);
assert(rc.kv_base != NULL);
assert(rc.node_base != NULL);
assert(rc.kv == NULL);
assert(rc.pos == -1);
assert(rc.list != NULL);
}
}
if(print){
CAlgHashList_Print(hlist, 1);
}
free(hlist);
}
#include
void TestCAlgHashList_Run()
{
SYSTEMTIME st1, st2;
FILETIME ft1, ft2; //100ns
printf("CAlgHashList_MemSize(4,8,8192,100000) = %u\n",
CAlgHashList_MemSize(4,8,8192,100000));
printf("CAlgHashList_MemSize(4,8,32768,100000) = %u\n",
CAlgHashList_MemSize(4,8,32768,100000));
printf("CAlgHashList_MemSize(4,8,32768,1000000) = %u\n",
CAlgHashList_MemSize(4,8,32768,1000000));
printf("CAlgHashList_MemSize(40,40,32768,1000000) = %u\n",
CAlgHashList_MemSize(40,0,32768,1000000));
TestCAlgHashList_AddRemoveFind(8, 100, 1, 1);
GetLocalTime(&st1);
TestCAlgHashList_AddRemoveFind(4096, 1000000, 0, 0);
GetLocalTime(&st2);
SystemTimeToFileTime(&st1, &ft1);
SystemTimeToFileTime(&st2, &ft2);
printf("%s using %u.%03ums to add/find/remove/update %d nodes trunk %d %02u:%02u:%02u.%03u - %02u:%02u:%02u.%03u\n",
__FILE__,
(ft2.dwLowDateTime-ft1.dwLowDateTime)/10000,
((ft2.dwLowDateTime-ft1.dwLowDateTime)%10000)/10, 1000000, 4096,
st1.wHour,st1.wMinute,st1.wSecond,st1.wMilliseconds,
st2.wHour,st2.wMinute,st2.wSecond,st2.wMilliseconds);
GetLocalTime(&st1);
TestCAlgHashList_AddRemoveFind(8192, 1000000, 0, 0);
GetLocalTime(&st2);
SystemTimeToFileTime(&st1, &ft1);
SystemTimeToFileTime(&st2, &ft2);
printf("%s using %u.%03ums to add/find/remove/update %d nodes trunk %d %02u:%02u:%02u.%03u - %02u:%02u:%02u.%03u\n",
__FILE__,
(ft2.dwLowDateTime-ft1.dwLowDateTime)/10000,
((ft2.dwLowDateTime-ft1.dwLowDateTime)%10000)/10, 1000000, 8192,
st1.wHour,st1.wMinute,st1.wSecond,st1.wMilliseconds,
st2.wHour,st2.wMinute,st2.wSecond,st2.wMilliseconds);
GetLocalTime(&st1);
TestCAlgHashList_AddRemoveFind(8192*2, 1000000, 0, 0);
GetLocalTime(&st2);
SystemTimeToFileTime(&st1, &ft1);
SystemTimeToFileTime(&st2, &ft2);
printf("%s using %u.%03ums to add/find/remove/update %d nodes trunk %d %02u:%02u:%02u.%03u - %02u:%02u:%02u.%03u\n",
__FILE__,
(ft2.dwLowDateTime-ft1.dwLowDateTime)/10000,
((ft2.dwLowDateTime-ft1.dwLowDateTime)%10000)/10, 1000000, 8192*2,
st1.wHour,st1.wMinute,st1.wSecond,st1.wMilliseconds,
st2.wHour,st2.wMinute,st2.wSecond,st2.wMilliseconds);
GetLocalTime(&st1);
TestCAlgHashList_AddRemoveFind(8192*4, 1000000, 0, 0);
GetLocalTime(&st2);
SystemTimeToFileTime(&st1, &ft1);
SystemTimeToFileTime(&st2, &ft2);
printf("%s using %u.%03ums to add/find/remove/update %d nodes trunk %d %02u:%02u:%02u.%03u - %02u:%02u:%02u.%03u\n",
__FILE__,
(ft2.dwLowDateTime-ft1.dwLowDateTime)/10000,
((ft2.dwLowDateTime-ft1.dwLowDateTime)%10000)/10, 1000000, 8192*4,
st1.wHour,st1.wMinute,st1.wSecond,st1.wMilliseconds,
st2.wHour,st2.wMinute,st2.wSecond,st2.wMilliseconds);
}
//end of file