/************************************************** * 程序名称 : XipHashMap.c * 程序作者 : icesky * 程序版本 : 1.0.0 * 创建日期 : 2016.07.28 * 程序功能 : * 参考java的hashmap实现机制,实现了C基本的 * hashmap实现,支持几种功能: * 1.新建 * 2.销毁 * 3.加入/设置 * 4.获取 * 5.删除 * 6.判断是否存在 * 7.打印 * 注意事项: * * 修改记录 : *************************************************/ #include #include #include #include #include "xiphashmap.h" #define hmap_malloc(size) malloc(size) #define HMAPFREE(x) free(x);x=NULL; #define MAXIMUM_CAPACITY 1 << 30 /*1 073 741 824 */ #define DEFAULT_CAPACITY 1 << 4 /*16*/ #define DEFAULT_FACTOR 0.75f #define INTER_MAX_VALUE 1<<31 /*int的最大值*/ #define MALLOC_FLAG_NO 1 /*不分配内存*/ /*hashmap node结构 */ typedef struct st_xip_hashmap_node { char * key; struct { void *ptr; /*注意ptr本身,只是指针*/ int size; /*内存大小*/ }value; struct st_xip_hashmap_node *next; unsigned int hash; } TxipHashmapNode; /*hashmap 结构*/ typedef struct { TxipHashmapNode ** table; /*T表*/ unsigned int length; /*桶大小*/ unsigned int size; /*实际个数*/ unsigned int threshold; /*加载临界值*/ int malloc_flag; /*是否为value分配内存*/ float factor; /*加载因子,默认为0.75(0-1)*/ } TxipHashmap; #define HMAPLOG(...) AppLog(__FILE__,__LINE__,__VA_ARGS__) static void AppLog(char *file, long line, char *level, char *fmtstr, ...) { va_list ap; char tmpstr[501]; memset(tmpstr, 0x0, sizeof(tmpstr)); va_start(ap, fmtstr); vsprintf(tmpstr, fmtstr, ap); va_end(ap); printf("[%s][%s][%03ld]", level, file, line); printf("[%s]\n", tmpstr); return ; } /*** static 声明 ***/ /*新建T表*/ static TxipHashmapNode ** create_T_table( int opacity); /*新建hashmap节点*/ static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next, int malloc_flag, int size); /*重建hashmap*/ static int rebuild_hash( TxipHashmap * hashmap); /*hash算法*/ static unsigned int XipHash(char * key); /******************************************************************** * 函数名称: XipHashmapPrint * 函数功能: 打印hashmap中所有的元素,主要用于调试和检查散列分布 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapPrint( void * hashmap, void (*ref_func)(int,char *, void *)) { TxipHashmap * map = ( TxipHashmap *)hashmap; int idx = 0; TxipHashmapNode * e = NULL; TxipHashmapNode * next = NULL; /** 打印日志 **/ if( map != NULL) { HMAPLOG("I", "HashMap:length(%d),size(%d),threshold(%d),malloc_flag(%d)", map->length, map->size, map->threshold, map->malloc_flag); for ( idx = 0; idx < map->length; idx++) { for( e = map->table[idx]; e != NULL; ) { next = e->next; if( ref_func == NULL) { HMAPLOG("I", "Node[%d]:hash[%d],key[%s],value[%d][%x][%s]", idx, e->hash, e->key, e->value.size, e->value.ptr, (char *)e->value.ptr); } else { ref_func(idx, e->key, e->value.ptr); } e = next; } } } /** 打印分布情况图 **/ int i = 0; if( map != NULL) { for( idx = 0; idx < map->length; idx++) { i = 0; for( e= map->table[idx] ; e!= NULL; ) { next = e->next; i++; e = next; } HMAPLOG("I","%08ld:%*d", idx, i, i); } } return 0; } /******************************************************************** * 函数名称: XipHashmapGetNum * 函数功能: 输出hashmap中的key:value对的个数 * 函数作者: icesky * 创建日期: 2016.08.04 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapNum( void * hashmap) { TxipHashmap * map = ( TxipHashmap *)hashmap; if( map == NULL) return 0; return map->size; } /******************************************************************** * 函数名称: XipHashmapGetSize * 函数功能: 打印hashmap中所有的元素,主要用于调试和检查散列分布 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapSize( void * hashmap, int * mapsize, int * tablesize, int * nodesize, int * keysize, int * valuesize) { TxipHashmap * map = ( TxipHashmap *)hashmap; if( map == NULL) return 0; int total_size = 0; /*总内存大小*/ int map_size = 0; /*map结构体大小*/ int table_size = 0; /*散列表大小*/ int node_size = 0; /*总节点大小*/ int key_size = 0; /*总key大小--当不分配内存为0*/ int value_size = 0; /*总值大小--当不分配内存为0*/ int idx = 0; map_size = sizeof(TxipHashmap); table_size = sizeof(TxipHashmapNode*) * map->length; TxipHashmapNode * e = NULL; TxipHashmapNode * next = NULL; if( map != NULL) { for( idx = 0; idx < map->length; idx++) { for( e= map->table[idx] ; e!= NULL; ) { next = e->next; /*计算大小*/ node_size += sizeof(TxipHashmapNode); if ( map->malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ { ; } else { key_size += strlen(e->key)+1; value_size += e->value.size; } e = next; } } } total_size = map_size + table_size + node_size + key_size + value_size; HMAPLOG("D","total_size[%d] = map_size[%d]+table_size[%d]+node_size[%d]+key_size[%d]+value_size[%d]", total_size, map_size, table_size, node_size, key_size, value_size); if( mapsize != NULL) *mapsize = map_size; if( tablesize != NULL) *tablesize = table_size; if( nodesize != NULL) *nodesize = node_size; if( keysize != NULL) *keysize = key_size; if( valuesize != NULL) *valuesize = value_size; return total_size; } /******************************************************************** * 函数名称: XipHashmapNew * 函数功能: 以所有的默认参数,创建一个hashmap * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapNew() { return XipHashmapInit( 0, 0.00, 0); /*全默认选项*/ } /******************************************************************** * 函数名称: XipHashmapInit * 函数功能: 指定初始容量,加载因子和value存储方式,创建一个hashmap * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapInit( int opacity, float factor, int malloc_flag) { TxipHashmap * map = ( TxipHashmap *)hmap_malloc(sizeof( TxipHashmap)); if( map == NULL) { HMAPLOG("E","初始化hashmap内存失败!!!"); return NULL; } memset( map, 0x00, sizeof(TxipHashmap)); if ( opacity < DEFAULT_CAPACITY ) opacity = DEFAULT_CAPACITY; if( opacity > MAXIMUM_CAPACITY) opacity = MAXIMUM_CAPACITY; if ( factor >= -0.005 && factor <=0.005 ) factor = DEFAULT_FACTOR; if( opacity <= 1) { HMAPLOG("E","初始容量必须大于1!!!"); HMAPFREE(map); map = NULL; return NULL; } if( factor < 0.00 || factor > 1.00) { HMAPLOG("E","加载因子取值区间为[0.00-1.00],但本次加载因子为[%.2f]", factor); HMAPFREE(map); map = NULL; return NULL; } map->table = create_T_table( opacity); if( map->table == NULL) { HMAPLOG("E","创建hash值的T表失败!!!"); HMAPFREE(map->table); map->table = NULL; HMAPFREE(map); map = NULL; return NULL; } map->length = opacity; map->size = 0; map->factor = factor; map->threshold = (unsigned int)( factor * opacity); map->malloc_flag = malloc_flag; return (void *)map; } /******************************************************************** * 函数名称: XipHashmapDestory * 函数功能: 释放hashmap的内存资源 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapDestory( void * hashmap) { TxipHashmap * map = (TxipHashmap *) hashmap; TxipHashmapNode * e = NULL; TxipHashmapNode * next = NULL; register unsigned int idx; /** 释放hashmap node 内存 **/ if( map != NULL) { /** 释放hashmap的node空间 **/ for ( idx = 0; idx < map->length; idx++) { for( e = map->table[idx]; e != NULL; ) { next = e->next; /* 释放node空间*/ if( map->malloc_flag == MALLOC_FLAG_NO) { ; } else { /*释放node的key和value空间*/ HMAPFREE(e->key); e->key = NULL; HMAPFREE(e->value.ptr); e->value.ptr = NULL; } /*释放node本身*/ HMAPFREE(e); e=NULL; e = next; } } /** 释放hashmap的T表 **/ HMAPFREE( map->table); map->table = NULL; /** 释放hashmap本身的空间 **/ HMAPFREE( map); map = NULL; } return 0; } /******************************************************************** * 函数名称: XipHashmapPut * 函数功能: 将key和value放入hashmap中,如果key已存在,相当于Set命令 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapPut( void * hashmap, char * key, void * value, int size) { TxipHashmap * map = (TxipHashmap *)hashmap; if( map == NULL) { HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); return NULL; } if ( key == NULL || strlen(key) == 0) { HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); return NULL; } if ( value == NULL) { HMAPLOG("E","value不可以为空(NULL)"); return NULL; } if ( size <= 0) { HMAPLOG("E","值的大小size不可以为0"); return NULL; } TxipHashmapNode *e = NULL; unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; /*如果找到了key,则相当于set*/ for ( e = map->table[idx]; e != NULL ; e = e->next) { /*参考java的hashmap实现,先比较hash,再比较strcmp*/ if ( hcode == e->hash && (key == e->key || strcmp( key, e->key) == 0)) { if( map->malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ { e->value.ptr = value; e->value.size = 0; } else { HMAPFREE(e->value.ptr); e->value.size = size; e->value.ptr = NULL; e->value.ptr = hmap_malloc(size); if( e->value.ptr == NULL) { HMAPLOG("E","为value分配内存时异常!!!"); return NULL; } memset( e->value.ptr, 0x00, size); memcpy( e->value.ptr, value, size); return e->value.ptr; } return e->value.ptr; /*将原value返回给调用者*/ } } /*新建node*/ map->table[idx] = create_hashmap_node( key, value, map->table[idx], map->malloc_flag, size); if( map->table[idx] == NULL) { HMAPLOG("E","新建hashnode节点异常malloc_flag[%d],size[%d]!!!", map->malloc_flag, size); return NULL; /*返回固定值*/ } /*修改hashmap*/ e = map->table[idx]; e->hash = hcode; map->size++; /*如果触发临界值,则重建hash表*/ if( map->size > map->threshold) { /* HMAPLOG("D","达到临界值[%d] > [%d]", map->size, map->threshold); */ if( rebuild_hash(map) != 0) { HMAPLOG("E","重建hash表错误!!!"); if( map->malloc_flag == MALLOC_FLAG_NO) { ; } else { HMAPFREE(map->table[idx]->key); map->table[idx]->key = NULL; HMAPFREE(map->table[idx]->value.ptr); map->table[idx]->value.ptr = NULL; } HMAPFREE(map->table[idx]); map->table[idx] = NULL; return NULL; /*返回固定值*/ } } return map->table[idx]->value.ptr; /*返回内存地址*/ } /******************************************************************** * 函数名称: XipHashmapGet * 函数功能: 根据Key值获取对应的value值 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ void * XipHashmapGet( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; if( map == NULL) { HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); return NULL; } if ( key == NULL || strlen(key) == 0) { HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); return NULL; } unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; TxipHashmapNode * e = NULL; for ( e = map->table[idx]; e != NULL ; e = e->next) { /*参考java的hashmap实现,先比较hash,再比较strcmp, key == e->key是用来给ihashmap用的,待扩展*/ if ( hcode == e->hash && (key == e->key || strcmp( key, e->key) == 0)) { return e->value.ptr; } } return NULL; } /******************************************************************** * 函数名称: XipHashmapExists * 函数功能: 判断Key是否在hashmap中存在 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapExists( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; if( map == NULL) { HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); return -5; } if ( key == NULL || strlen(key) == 0) { HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); return -10; } unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; TxipHashmapNode * e = NULL; for ( e = map->table[idx]; e != NULL ; e = e->next) { /*参考java的hashmap实现,先比较hash,再比较strcmp*/ if ( hcode == e->hash && (key == e->key || strcmp( key, e->key) == 0)) { return 1; } } return 0; } /******************************************************************** * 函数名称: XipHashmapRemove * 函数功能: 删除map中对应的Key和VALUE的节点 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 外部调用函数,参见xiphashmap.h中描述 *********************************************************************/ int XipHashmapRemove( void * hashmap, char *key) { TxipHashmap * map = (TxipHashmap *) hashmap; if( map == NULL) { HMAPLOG("E","hashmap指针为空,请检查是否已经调用XipHashmapNew/XipHashmapInit,或调用返回异常"); return -5; } if ( key == NULL || strlen(key) == 0) { HMAPLOG("E","Key值必须为字符串,且Key值不能为空或者NULL!!!"); return -10; } unsigned int hcode = XipHash(key); unsigned int idx = hcode % map->length; TxipHashmapNode * e = NULL; TxipHashmapNode * prev = NULL; for ( e = map->table[idx]; e != NULL ; prev = e, e = e->next) { /*参考java的hashmap实现,先比较hash,再比较strcmp*/ if ( hcode == e->hash && (key == e->key || strcmp( key, e->key) == 0)) { if( prev == NULL) map->table[idx] = e->next; else prev->next = e->next; if( map->malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ { ; } else { HMAPFREE(e->key); e->key = NULL; HMAPFREE(e->value.ptr); e->value.ptr = NULL; } HMAPFREE(e); map->size--; return 0; } } return 0; } /******************************************************************** * 函数名称: create_T_table * 函数功能: 申请指向node的连续内存指针,在《算法导论》关于散列表中,所处位置为T表 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 内部调用函数.注意返回值是指向TxipHashmapNode的指针的指针 *********************************************************************/ static TxipHashmapNode ** create_T_table( int opacity) { register unsigned int i=0; TxipHashmapNode ** table = (TxipHashmapNode **)hmap_malloc( sizeof(TxipHashmapNode*) *opacity); if( table == NULL) { return NULL; } memset( table, 0x00, sizeof(TxipHashmapNode*) * opacity); /*初始化*/ for ( i = 0; i < opacity; i++) table[i] = NULL; return table; } /******************************************************************** * 函数名称: create_hashmap_node * 函数功能: 创建具体的key,value的node节点 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 内部调用函数. 如果malloc_flag是1的话,则size无效,因为不需要 为value申请内存。 *********************************************************************/ static TxipHashmapNode * create_hashmap_node( char * key, void * value, TxipHashmapNode * next, int malloc_flag, int size) { TxipHashmapNode * node = ( TxipHashmapNode *) hmap_malloc( sizeof(TxipHashmapNode)); if ( node == NULL) { return NULL; } memset( node, 0x00, sizeof( TxipHashmapNode)); if( malloc_flag == MALLOC_FLAG_NO) /*不分配内存*/ { node->key = key; node->value.ptr = value; } else { /*限定KEY必须是字符串类型*/ node->key = hmap_malloc(strlen(key)+1); if( node->key == NULL) { HMAPFREE(node); node = NULL; return NULL; } memset(node->key, 0x00, strlen(key)+1); memcpy( node->key, key, strlen(key)+1); node->value.size = size; node->value.ptr = hmap_malloc(size); if( node->value.ptr == NULL) { /*防止异常时内存泄漏*/ HMAPFREE(node); node = NULL; HMAPFREE(node->key); node->key = NULL; return NULL; } memset(node->value.ptr, 0x00, size); memcpy( node->value.ptr, value, size); } node->next = next; return node; } /******************************************************************** * 函数名称: rebuild_hash * 函数功能: 当前hashmap中的size达到了临界值的时候,需要重新创建更大的hashmap 来进行存储。 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 内部调用函数. 注意重建失败了要判断返回值 *********************************************************************/ static int rebuild_hash( TxipHashmap * map) { register unsigned int i = 0; unsigned int idx = 0; /*如果达到最大了,则不在rebuild*/ if( map->length == MAXIMUM_CAPACITY) { map->threshold = INTER_MAX_VALUE; return 0; } unsigned int length = map->length*2; if( length > MAXIMUM_CAPACITY) length = MAXIMUM_CAPACITY; TxipHashmapNode *e=NULL; TxipHashmapNode *next=NULL; TxipHashmapNode ** newtable = create_T_table( length); if ( newtable == NULL) { return -5; } for( i = 0; i < map->length; i++) { /*遍历*/ e = *(map->table + i); if( e != NULL) { do { next = e->next; idx = e->hash % length; e->next = newtable[idx]; /*如果newtable[idx]的值已经存在了,该行则会将该值变为newtable[idx]->idx*/ newtable[idx] = e; e = next; }while( e != NULL); } } /*释放oldtable*/ HMAPFREE(map->table); map->table = newtable; map->length = length; map->threshold = (unsigned int)(map->factor * length); return 0; } /******************************************************** * 函数名称: XipHash * 函数功能: 通过key来计算hash值的算法,算法越好,散列的一致分布性越好 * 函数作者: icesky * 创建日期: 2016.07.28 * 调用说明: 内部调用函数. * hash算法,此处使用的是 simple BKDR hash algorithm * 为JAVA的JDK中使用的算法 * 也可以使用PHP等使用的time33算法(DJP hash algorithm) * 还可以使用暴雪公司的的One-Way-Hash(号称最快的hash算法) * * 之所以没有使用我推荐的算法,是因为我把机会留给了你.如果 * 你看到了这个地方,并且试图优化算法的话,可以使用我建议的 * 算法进行尝试. * 不过我想等到系统瓶颈追溯到本代码的时候,或许早就有了更 * 完美的算法来保证趋近一致散列了.o(∩_∩)o * icesky.2016.07.08 *********************************************************/ static unsigned int XipHash(char * key) { register unsigned int h = 0; unsigned int seed = 131; //31 131 1313 13131 while ( *key != '\0' ) { h = h * seed + ( *key++ ); } return (h & 0x7FFFFFFF); }