Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis的好多方案都是可以拿出来应用到具体的工程中.
Hash表就是很不错的实现.主要在Dict.h Dict.c这个文件中.
其中函数:
/* API */
static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
static dict *dictCreate(dictType *type, void *privDataPtr);
static int dictExpand(dict *ht, unsigned long size);
static int dictAdd(dict *ht, void *key, void *val);
static int dictReplace(dict *ht, void *key, void *val);
static int dictDelete(dict *ht, const void *key);
static void dictRelease(dict *ht);
static dictEntry * dictFind(dict *ht, const void *key);
static dictIterator *dictGetIterator(dict *ht);
static dictEntry *dictNext(dictIterator *iter);
static void dictReleaseIterator(dictIterator *iter);
使用步骤大概如下,根据Redis中的使用进行举例:
ac->sub.channels = dictCreate(&callbackDict,NULL);//创建hash表.
dict *callbacks = ac->sub.channels;
de = dictFind(callbacks,sname);//根据关键字查找value
if (de != NULL) {
memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));//获取value
/* If this is an unsubscribe message, remove it. */
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
dictDelete(callbacks,sname); //删除该key-value键值对
/* If this was the last unsubscribe message, revert to
* non-subscribe mode. */
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
if (reply->element[2]->integer == 0)
c->flags &= ~REDIS_SUBSCRIBED;
}
}
ret = dictReplace(ac->sub.channels,sname,&cb);//添加一个新的key-value键值对,如果已经存在,释放旧的,添加新的.
接下来关注一下hash表的一些结构体定义.
typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
typedef struct dict {
dictEntry **table;
dictType *type;
unsigned long size;
unsigned long sizemask;
unsigned long used;
void *privdata;
} dict;
然后是create函数:
static void _dictReset(dict *ht) {
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
}
/* Create a new hash table */
static dict *dictCreate(dictType *type, void *privDataPtr) {
dict *ht = malloc(sizeof(*ht));
_dictInit(ht,type,privDataPtr);
return ht;
}
/* Initialize the hash table */
static int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
_dictReset(ht);
ht->type = type;
ht->privdata = privDataPtr;
return DICT_OK;
}
hash表就创建成功了.回掉函数就可以根据实际情况来写回掉函数.redis的hash函数采用的是常见的hash算法,即:
static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
unsigned int hash = 5381;
while (len--)
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
return hash;
}
然后看一下addhash函数:
/* Add an element to the target hash table */
static int dictAdd(dict *ht, void *key, void *val) {
int index;
dictEntry *entry;//节点
/* Get the index of the new element, or -1 if
* the element already exists. */
if ((index = _dictKeyIndex(ht, key)) == -1)
return DICT_ERR;
/* Allocates the memory and stores key */
entry = malloc(sizeof(*entry));//节点分配内存
entry->next = ht->table[index];//哈希表新加一个节点.
ht->table[index] = entry;
/* Set the hash entry fields. */
dictSetHashKey(ht, entry, key);//生成节点里的key值
dictSetHashVal(ht, entry, val);//生成节点里的value值
ht->used++;//哈希表的数量加一
return DICT_OK;
}
首先看一下函数dictKeyIndex,
static int _dictKeyIndex(dict *ht, const void *key) {
unsigned int h;
dictEntry *he;
/* Expand the hashtable if needed */
if (_dictExpandIfNeeded(ht) == DICT_ERR)
return -1;
/* Compute the key hash value */
h = dictHashKey(ht, key) & ht->sizemask; //调用hash算法.根据key计算得到索引index.返回索引值.
/* Search if this slot does not already contain the given key */
he = ht->table[h];
while(he) {
if (dictCompareHashKeys(ht, key, he->key))
return -1;
he = he->next;
}
return h;
}