#include "swapi.h" #include "swmem.h" #include "swhash.h" ////////////////////////////////////////////////////////////////////////////////////////// // base struct /* hash表操作结构 */ struct hash_ops { int32_t (*insert)(hash_t, const void *, int32_t, const void *); // 插入 int32_t (*delete)(hash_t, const void *, int32_t, bool); // 删除 int32_t (*replace)(hash_t, const void *, int32_t, const void *, void **); // 替换 void *(*lookup)(hash_t, const void *, int32_t); // 查找 void (*visit)(hash_t, hash_visit_cb); // 遍历hash表 void (*destroy)(hash_t, bool); // 销毁 }; /* hash表结构 */ struct shash_t { uint32_t tsize; // 表长 uint32_t ksize; // key值长度,默认0为变长 uint32_t nelems; // hash表中已存在元素的数目 uint32_t nElemCollisionCnts; // hash表中元素发生碰撞的次数(mixed-value) struct hash_ops *h_ops; // hash操作结构 hash_ex_func_hash hash_fn; // hash函数 hash_ex_func_cmp cmp_fn; // hash比较key函数 hash_data_free_cb free_cb; // hash释放data函数 union { struct elem **chtable; // 链表类型 } dm; // hash表数据管理者(无论哪种类型,都必须解决碰撞冲突的问题) }; ////////////////////////////////////////////////////////////////////////////////////////// // "chain hash" struct struct elem { void *key; // 关键字 int32_t keylen; // 关键字长度 void *data; // 数据指针 struct elem *next; // 指向下一个元素 }; static int32_t ch_insert(hash_t, const void *, int32_t, const void *); static int32_t ch_delete(hash_t, const void *, int32_t, bool); static int32_t ch_replace(hash_t, const void *, int32_t, const void *, void **); static void *ch_lookup(hash_t, const void *, int32_t); static void ch_visit(hash_t, hash_visit_cb); static void ch_destroy(hash_t, bool); static struct hash_ops ch_ops = { ch_insert, ch_delete, ch_replace, ch_lookup, ch_visit, ch_destroy, }; ////////////////////////////////////////////////////////////////////////////////////////// // hash functions /* bob hash(http://burtleburtle.net/bob/index.html) */ #define mix(a, b, c) \ { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } static uint32_t bob_hash(const unsigned char *k, uint32_t length) { uint32_t a, b, c, len; /* set up the internal state */ len = length; a = b = c = 0x9e3779b9; // the golden ratio; an arbitrary data /* handle most of the key */ while (len >= 12) { a += (k[0] +((uint32_t)k[1] << 8) +((uint32_t)k[2] << 16) +((uint32_t)k[3] << 24)); b += (k[4] +((uint32_t)k[5] << 8) +((uint32_t)k[6] << 16) +((uint32_t)k[7] << 24)); c += (k[8] +((uint32_t)k[9] << 8) +((uint32_t)k[10]<< 16)+((uint32_t)k[11] << 24)); mix(a, b, c); k += 12; len -= 12; } /* handle the last 11 bytes */ c += length; switch(len) { /* all the case statements fall through */ case 11: c += ((uint32_t)k[10] << 24); case 10: c += ((uint32_t)k[9] << 16); case 9 : c += ((uint32_t)k[8] << 8); /* the first byte of c is reserved for the length */ case 8 : b += ((uint32_t)k[7] << 24); case 7 : b += ((uint32_t)k[6] << 16); case 6 : b += ((uint32_t)k[5] << 8); case 5 : b += k[4]; case 4 : a += ((uint32_t)k[3] << 24); case 3 : a += ((uint32_t)k[2] << 16); case 2 : a += ((uint32_t)k[1] << 8); case 1 : a += k[0]; /* case 0: nothing left to add */ } mix(a, b, c); return c; } /* one-at-a-time hash */ static uint32_t oneatatime_hash(const unsigned char *k, uint32_t length) { uint32_t hash = 0; uint32_t i; for(i = 0; i < length; ++i) { hash += k[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } /* time33 hash */ static uint32_t time33_hash(const unsigned char *k, uint32_t length) { uint32_t hash = 5381; uint32_t i = 0; for(i = 0; i < length; i++) { hash = (hash<<5) + hash + k[i]; // hash = hash*31 + k[i]; } return hash; } /* mixed computing imaging to my hash table */ static uint32_t getHash(hash_t htable, const void *key, int32_t keylen) { int32_t len; if(htable->ksize == 0) len = keylen; else len = htable->ksize; return ((htable->hash_fn(key, len)) & (htable->tsize-1)); // hash the key and get the meaningful bits for our table } ////////////////////////////////////////////////////////////////////////////////////////// // default hash key compare functions static int32_t default_hash_key_cmp(const void *p1, int32_t len1,const void *p2, int32_t len2) { if(len1 != len2) return -1; else return memcmp(p1, p2, len1); // return strncmp((const char * )p1, (const char * )p2, len1); } ////////////////////////////////////////////////////////////////////////////////////////// // main functions hash_t sw_hash_create(uint32_t sizehint, uint32_t keysize, enum hash_alg alg, hash_ex_func_hash hfunc, hash_ex_func_cmp cfunc, hash_data_free_cb fcb, uint32_t flag) { hash_t htable; uint32_t size = 0; // htable size uint32_t i = 1; /* 1, take the size hint and round it to the next higher power of two */ while(size < sizehint) { size = 1<tsize = size; htable->ksize = keysize; htable->nelems = 0; htable->nElemCollisionCnts = 0; switch(alg) { case BOB_HASH: htable->hash_fn = bob_hash; break; case ONEATATIME_HASH: htable->hash_fn = oneatatime_hash; break; case TIME33_HASH: htable->hash_fn = time33_hash; break; default: if(hfunc) htable->hash_fn = hfunc; else { errno = EINVAL; goto ErrorP; } } if(cfunc) htable->cmp_fn = cfunc; else htable->cmp_fn = default_hash_key_cmp; htable->free_cb = fcb; /* 4, create elements for each hash htable type */ switch(flag) { case CHAIN_H: htable->h_ops = &ch_ops; htable->dm.chtable = (struct elem **)sw_heap_malloc(size*sizeof(struct elem *)); if(!htable->dm.chtable) { errno = ENOMEM; goto ErrorP; } else memset(htable->dm.chtable, 0, size*sizeof(struct elem *)); break; default: errno = EINVAL; goto ErrorP; } return htable; ErrorP: if(htable && flag == CHAIN_H && htable->dm.chtable) sw_heap_free((void *)htable->dm.chtable); if(htable) sw_heap_free((void *)htable); return NULL; } int32_t sw_hash_insert(hash_t htable, const void *key, int32_t keylen, const void *data) { if(!htable || !key || keylen <= 0 || !data) return -1; else return htable->h_ops->insert(htable, key, keylen, data); } int32_t sw_hash_delete(hash_t htable, const void *key, int32_t keylen, bool bAutoFreeData) { if(!htable || !key || keylen <= 0) return -1; else return htable->h_ops->delete(htable, key, keylen, bAutoFreeData); } int32_t sw_hash_replace(hash_t htable, const void *key, int32_t keylen, const void *newdata, void **olddata) { if(!htable || !key || keylen <= 0 || !newdata || !olddata) return -1; else return htable->h_ops->replace(htable, key, keylen, newdata, olddata); } void *sw_hash_lookup(hash_t htable, const void *key, int32_t keylen) { if(!htable || !key || keylen <= 0) return NULL; else return htable->h_ops->lookup(htable, key, keylen); } uint32_t sw_hash_getTableSize(hash_t htable) { assert(htable); return htable->tsize; } uint32_t sw_hash_getElemsCnt(hash_t htable) { assert(htable); return htable->nelems; } uint32_t sw_hash_getElemCollisionCnts(hash_t htable) { assert(htable); return htable->nElemCollisionCnts; } void sw_hash_visit(hash_t htable, hash_visit_cb vcb) { if(!htable) return; htable->h_ops->visit(htable, vcb); } void sw_hash_destroy(hash_t htable, bool bAutoFreeData) { if(!htable) return; htable->h_ops->destroy(htable, bAutoFreeData); sw_heap_free((void *)htable); } ////////////////////////////////////////////////////////////////////////////////////////// // "chain hash" functions static int32_t ch_insert(hash_t htable, const void *key, int32_t keylen, const void *data) { uint32_t idx; struct elem *cursor = NULL; struct elem *newelem = NULL; void *keycopy = NULL; newelem = (struct elem *)sw_heap_malloc(sizeof(struct elem)); if(!newelem) { errno = ENOMEM; goto ErrorP; } keycopy = (struct elem *)sw_heap_malloc(keylen*sizeof(char)); if(!keycopy) { errno = ENOMEM; goto ErrorP; } else memcpy(keycopy, key, keylen); newelem->key = keycopy; newelem->keylen = keylen; newelem->data = (void *)data; newelem->next = NULL; idx = getHash(htable, key, keylen); if(htable->dm.chtable[idx] == NULL) { // no collision, first element in this bucket htable->dm.chtable[idx] = newelem; } else { // collision, insert at the end of the chain cursor = htable->dm.chtable[idx]; while(1) { if(htable->cmp_fn(cursor->key, cursor->keylen, newelem->key, keylen) == 0) { // key is already inserted in the table, insert fails errno = EINVAL; goto ErrorP; } if(cursor->next == NULL) break; else cursor = cursor->next; } cursor->next = newelem; // insert element at the end of the chain htable->nElemCollisionCnts++; } htable->nelems++; return 0; ErrorP: if(newelem && newelem->key) sw_heap_free((void *)newelem->key); if(newelem) sw_heap_free((void *)newelem); return -1; } static int32_t ch_delete(hash_t htable, const void *key, int32_t keylen, bool bAutoFreeData) { uint32_t idx; struct elem *cursor; struct elem *delelem; bool bElemCollisionDecrement; idx = getHash(htable, key, keylen); if(!htable->dm.chtable[idx]) return 0; delelem = NULL, bElemCollisionDecrement = false; if(htable->cmp_fn(htable->dm.chtable[idx]->key, htable->dm.chtable[idx]->keylen, key, keylen) == 0) { // element to delete is the first in the chain delelem = htable->dm.chtable[idx]; htable->dm.chtable[idx] = delelem->next; if(htable->dm.chtable[idx]) bElemCollisionDecrement = true; } else { // search thru the chain for the element cursor = htable->dm.chtable[idx]; while(cursor->next) { if(htable->cmp_fn(cursor->next->key, cursor->next->keylen, key, keylen) == 0) { delelem = cursor->next; cursor->next = delelem->next; bElemCollisionDecrement = true; break; } cursor = cursor->next; } } if(delelem) { sw_heap_free((void *)delelem->key); if(bAutoFreeData && htable->free_cb) htable->free_cb((void *)delelem->data); sw_heap_free((void *)delelem); htable->nelems--; if(bElemCollisionDecrement) htable->nElemCollisionCnts--; } return 0; } static int32_t ch_replace(hash_t htable, const void *key, int32_t keylen, const void *newdata, void **olddata) { uint32_t idx; struct elem *cursor; idx = getHash(htable, key, keylen); cursor = htable->dm.chtable[idx]; while(cursor) { if(htable->cmp_fn(cursor->key, cursor->keylen, key, keylen) == 0) { *olddata = cursor->data; cursor->data = (void *)newdata; return 0; } cursor = cursor->next; } return -1; } static void *ch_lookup(hash_t htable, const void *key, int32_t keylen) { uint32_t idx; struct elem *cursor; idx = getHash(htable, key, keylen); cursor = htable->dm.chtable[idx]; while(cursor) { if(htable->cmp_fn(cursor->key, cursor->keylen, key, keylen) == 0) return cursor->data; cursor = cursor->next; } return NULL; } static void ch_visit(hash_t htable, hash_visit_cb vcb) { uint32_t idx; struct elem *cursor; for(idx = 0; idx < htable->tsize; idx++) { cursor = htable->dm.chtable[idx]; while(cursor) { if(vcb) vcb(cursor->key, cursor->keylen, cursor->data); cursor = cursor->next; } } } static void ch_destroy(hash_t htable, bool bAutoFreeData) { uint32_t idx; struct elem *cursor, *delelem; for(idx = 0; idx < htable->tsize; idx++) { cursor = htable->dm.chtable[idx]; while(cursor) { delelem = cursor; cursor = cursor->next; sw_heap_free((void *)delelem->key); if(bAutoFreeData && htable->free_cb) htable->free_cb((void *)delelem->data); sw_heap_free((void *)delelem); } } sw_heap_free((void *)htable->dm.chtable); }