HashMap/kmaplib/XipHashMap.c

782 lines
22 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**************************************************
* 程序名称 : XipHashMap.c
* 程序作者 : icesky
* 程序版本 : 1.0.0
* 创建日期 : 2016.07.28
* 程序功能 :
* 参考java的hashmap实现机制,实现了C基本的
* hashmap实现支持几种功能:
* 1.新建
* 2.销毁
* 3.加入/设置
* 4.获取
* 5.删除
* 6.判断是否存在
* 7.打印
* 注意事项:
*
* 修改记录 :
*************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#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);
}