//hash.h
/*
* hashmap.h
*
*/
#ifndef HASHMAP_H
#define HASHMAP_H
#define HMAP_PRESET_SIZE 2 <<23 // use a power of 2 for faster array access
#define HMAP_GROWTH_RATE 2
#define HMAP_MAKE_HASHFN // build a few hash functions
#define HMAP_THREAD_SAFE // add "-lrt" to your GCC compile flags
#define HMAP_DESTRUCTORS // require destructors for value clean-up
#define NEED 1 //需要做成hashCode
#define NOTNEED 0 //不需要做成hashCode
#ifdef HMAP_MAKE_HASHFN
#include <string.h>
#endif
#ifdef HMAP_THREAD_SAFE
#include <semaphore.h>
#endif
#include <stdint.h>
#include <stdbool.h>
// it may be a good idea to redefine key and val if the size of either is less
// than sizeof(void*). keep in mind that the default hash functions assume that
// key and val are void pointers, and will not work properly if they are changed
// to other (non-pointer) types.
typedef void* key;
typedef void* val;
typedef int int32;
typedef struct key_val_pair key_val_pair;
typedef struct hashmap hashmap;
// create a hashmap
hashmap* mk_hmap(uint32_t (*hash_fn)(key),
bool (*eq_fn)(key, key)
#ifdef HMAP_DESTRUCTORS
, void (*del_fn)(val)
#endif
,uint32_t isNeedCalcHashCode
);
// create a hashmap ,Specify memory Size Settings
hashmap* mk_hmap2(uint32_t (*hash_fn)(key),
bool (*eq_fn)(key, key)
#ifdef HMAP_DESTRUCTORS
, void (*del_fn)(val)
#endif
,uint32_t isNeedCalcHashCode
,uint32_t set_size
);
// delete the hashmap (and if destructors are enabled, destroy all values)
void free_hmap(hashmap*);
// add a value (with a given key) to the hashmap
// returns true on success and false on failure
bool __hmap_add(hashmap* hmap, key in, val out);
#define hmap_add(hmap, in, out) __hmap_add(hmap, (key) in, (val) out)
// retrieve a value using a given key from the hashmap
// returns your value if successful and NULL if not
val __hmap_get(hashmap* hmap, key in);
#define hmap_get(hmap, obj) __hmap_get(hmap, (key) obj)
#ifdef HMAP_MAKE_HASHFN
// integer-as-key hash functions
uint32_t int_hash_fn(key);
bool int_eq_fn(key, key);
void int_del_fn(val);
// char*-as-key hash functions
uint32_t str_hash_fn(key);
bool str_eq_fn(key, key);
void str_del_fn(val);
#endif
#endif
//hash.c
/*
* hashmap.c
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <semaphore.h>
#include "hash.h"
struct key_val_pair {
key k;
val v;
};
struct hashmap {
key_val_pair* map;
uint32_t size;
uint32_t capacity;
uint32_t (*hash_fn)(key);
bool (*eq_fn)(key, key);
#ifdef HMAP_DESTRUCTORS
void (*del_fn)(val);
#endif
#ifdef HMAP_THREAD_SAFE
sem_t lock;
uint32_t isNeedCalcHashCode;
#endif
};
// hashmaps need a hash function, an equality function, and a destructor
hashmap* mk_hmap(uint32_t (*hash_fn)(key),
bool (*eq_fn)(key, key)
#ifdef HMAP_DESTRUCTORS
, void (*del_fn)(val)
#endif
, uint32_t isNeedCalcHashCode
) {
hashmap* hmap = (hashmap*) calloc(sizeof(hashmap),1);
//memset(hmap,0,sizeof(hashmap));
hmap->map = (key_val_pair*) calloc(sizeof(key_val_pair) , HMAP_PRESET_SIZE);
//memset(hmap->map,0,sizeof(key_val_pair) * HMAP_PRESET_SIZE);
hmap->size = 0;
hmap->capacity = HMAP_PRESET_SIZE;
hmap->hash_fn = hash_fn;
hmap->eq_fn = eq_fn;
#ifdef HMAP_DESTRUCTORS
hmap->del_fn = del_fn;
#endif
#ifdef HMAP_THREAD_SAFE
sem_init(&hmap->lock, 0, 1);
#endif
hmap->isNeedCalcHashCode = isNeedCalcHashCode;
return hmap;
}
hashmap* mk_hmap2(uint32_t (*hash_fn)(key),
bool (*eq_fn)(key, key)
#ifdef HMAP_DESTRUCTORS
, void (*del_fn)(val)
#endif
, uint32_t isNeedCalcHashCode
, uint32_t set_size
) {
hashmap* hmap = (hashmap*) calloc(sizeof(hashmap),1);
hmap->map = (key_val_pair*) calloc(sizeof(key_val_pair) , set_size);
hmap->size = 0;
hmap->capacity = set_size;
hmap->hash_fn = hash_fn;
hmap->eq_fn = eq_fn;
#ifdef HMAP_DESTRUCTORS
hmap->del_fn = del_fn;
#endif
#ifdef HMAP_THREAD_SAFE
sem_init(&hmap->lock, 0, 1);
#endif
hmap->isNeedCalcHashCode = isNeedCalcHashCode;
return hmap;
}
void free_hmap(hashmap* hmap) {
#ifdef HMAP_THREAD_SAFE
sem_wait(&hmap->lock);
#endif
#ifdef HMAP_DESTRUCTORS
static uint32_t it;
for (it=0; it < hmap->size; ++it) {
if (hmap->map[it].v != NULL) {
hmap->del_fn(hmap->map[it].v);
}
}
#endif
free(hmap->map);
#ifdef HMAP_THREAD_SAFE
sem_post(&hmap->lock);
#endif
free(hmap);
}
// open addressed hashmap insertion function
static void __oa_hmap_add(key_val_pair* map, uint32_t size,
uint32_t (*hash_fn)(key),
key in, val out,int32 flag) {
static uint32_t hash;
//PRINT_INFO("add in=[%s]\n",(char *)in);
//PRINT_INFO("flag=%d\n",flag);
if(flag){
hash = hash_fn(in) % size;
}else{
hash = *((uint32_t *)in) % size;
}
while (map[hash].v != NULL) {
hash = (hash + 1) % size;
}
map[hash].k = in;
//PRINT_INFO("add k in=[%s]\n",(char *)map[hash].k);
map[hash].v = out;
}
bool __hmap_add(hashmap* hmap, key in, val out) {
#ifdef HMAP_THREAD_SAFE
sem_wait(&hmap->lock);
#endif
// performace degrades after a certain load
if (((float) hmap->size) / hmap->capacity > 0.70) {
key_val_pair* temp = (key_val_pair*) malloc(hmap->capacity * HMAP_GROWTH_RATE);
//PRINT_INFO("hmap->capacity=%d",hmap->capacity);
if (temp != NULL) {
hmap->capacity *= HMAP_GROWTH_RATE;
} else {
#ifdef HMAP_THREAD_SAFE
sem_post(&hmap->lock);
#endif
// we're out of memory
return false;
}
// re-posn all elements
static uint32_t it;
for (it=0; it < hmap->capacity; ++it) {
if (hmap->map[it].v != NULL) {
__oa_hmap_add(temp, hmap->capacity, hmap->hash_fn, in, out ,hmap->isNeedCalcHashCode);
}
}
// swap out the old map with the new one
free(hmap->map);
hmap->map = temp;
//PRINT_INFO("hmap->capacity=%d",hmap->capacity);
}
__oa_hmap_add(hmap->map, hmap->capacity, hmap->hash_fn, in, out , hmap->isNeedCalcHashCode);
hmap->size += 1;
//PRINT_INFO("hmap->capacity=%d",hmap->capacity);
#ifdef HMAP_THREAD_SAFE
sem_post(&hmap->lock);
#endif
return true;
}
val __hmap_get(hashmap* hmap, key in) {
#ifdef HMAP_THREAD_SAFE
sem_wait(&hmap->lock);
#endif
static uint32_t hash;
//PRINT_INFO("get in=[%s] aaaa hmap->capacity=%d",(char *)in,hmap->capacity);
if(hmap->isNeedCalcHashCode){
hash = hmap->hash_fn(in) % hmap->capacity;
}else{
//PRINT_INFO("%d %d",*((uint32_t *)in) , hmap->capacity);
hash = *((uint32_t *)in) % hmap->capacity;
}
//PRINT_INFO("hash=%d",hash);
while (hmap->map[hash].v != NULL) {
if (hmap->eq_fn(in, hmap->map[hash].k)) {
#ifdef HMAP_THREAD_SAFE
//PRINT_INFO("HMAP_THREAD_SAFE");
sem_post(&hmap->lock);
#endif
//PRINT_INFO("get in=[%s] bbbb",(char *)in);
return hmap->map[hash].v;
}
hash = (hash + 1) % hmap->capacity;
}
#ifdef HMAP_THREAD_SAFE
sem_post(&hmap->lock);
#endif
return NULL;
}
#ifdef HMAP_MAKE_HASHFN
// Robert Jenkins' 32 bit integer hash function
uint32_t int_hash_fn(key in) {
static uint32_t a;
a = *((uint32_t*) in);
a = (a+0x7ed55d16) + (a << 12);
a = (a^0xc761c23c) ^ (a >> 19);
a = (a+0x165667b1) + (a << 5);
a = (a+0xd3a2646c) ^ (a << 9);
a = (a+0xfd7046c5) + (a << 3);
a = (a^0xb55a4f09) ^ (a >> 16);
return a;
}
bool int_eq_fn(key a, key b) {
return *((int*) a) == *((int*) b) ? true : false;
}
void int_del_fn(val q) {};
// Dan Bernstein's string hash function (djb2)
uint32_t str_hash_fn(key in) {
static uint32_t hash;
//static unsigned char c;
int c;
PRINT_INFO("c:%c\n",c);
hash = 5381;
//c = *(unsigned char*) in++;
//while (c != '\0') {
while((c = *(unsigned char*)in++)){
// //PRINT_INFO("while\n");
// c = *(unsigned char*) in++;
hash = ((hash << 5) + hash) + c;
}
PRINT_INFO("hash:%d\n",hash);
return hash;
}
bool str_eq_fn(key a, key b) {
return (strcmp((char*) a, (char*) b) == 0) ? true : false;
}
void str_del_fn(val q) {
free(q);
};
#endif
main.c
#include <stdio.h>
#include "hash.h"
void main()
{
hashmap *pfilterMap;
pfilterMap = mk_hmap(str_hash_fn, str_eq_fn, str_del_fn,NEED);
if(!hmap_add(pfilterMap, (void*)"key", (void*)"value"))
return;
printf( "%s\n", (char*)hmap_get(pfilterMap, (void*)"key") );
free_hmap(pfilterMap);
pfilterMap = NULL;
}
以上程序解决了:
1、hash冲突
2、hash表个数的限制
3、线程安全
注意问题:
1、key和value全是存储的值地址,所以key和value在hashmap释放之前不能释放。
2、如果2次add的都是同一个key,get出的值是第一次add的时候的值。以后add的找不到了,因为为了解决冲突,把hashcode一样的map,hashcode+1了。