Hashdict
宏定义
#define DICT_OK 1
#define DICT_ERR 0
#define dictHashKey(d, key) (d)->type->hashFunction(key)
#define dictSetKey(d, entry, _key_)
#define dictSetVal(d, entry, _val_)
#define dictIsRehashing(d) ((d)->rehashidx != -1)
#define DICT_HT_INITIAL_SIZE 4
#define dictCompareKeys(d, key1, key2) \
(((d)->type->keyCompare) ? \
(d)->type->keyCompare((d)->privdata, key1, key2) : \
(key1) == (key2))
#define dictFreeVal(d, entry) \
if ((d)->type->valDestructor) \
(d)->type->valDestructor((d)->privdata, (entry)->v.val)
#define dictFreeKey(d, entry) \
if ((d)->type->keyDestructor) \
(d)->type->keyDestructor((d)->privdata, (entry)->key)
#define dictGetVal(he) ((he)->v.val)
static int dict_can_resize = 1;
static unsigned int dict_force_resize_ratio = 5;
//哈希表节点结构
typedef struct dictEntry
{
void* key;
union {
void* val;
uint64_t u64;
int64_t s64;
}v;
struct dictEntry* next;
}dictEntry;
//哈希表结构
typedef struct dictht {
dictEntry** table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
}dictht;
//字典结构
typedef struct dict {
dictType* type;
void* privdata;
dictht ht[2];
long rehashidx;
unsigned long iterators;
}dict;
// dictType 用于操作字典类型函数
typedef struct dictType {
uint64_t(*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;
字典迭代器:dictlterator
//重置哈希表各属性
static void dictReset(dictht* ht)
{
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
}
//创建字典结构
dict* dictCreate(dictType* type, void* privDataPtr)
{
dict* d = (dict*)malloc(sizeof(*d));
dictInit(d, type, privDataPtr);
return d;
}
//初始化字典结构
int dictInit(dict* d, dictType* type, void* privDataPtr)
{
dictReset(&d->ht[0]);
dictReset(&d->ht[1]);
d->type = type;
d->privdata = privDataPtr;//需要传给特定类型函数的可选参数
d->rehashidx = -1;//记录rehash的进度
d->iterators = 0;//Redis字典自己实现的迭代器,用于遍历,该变量用于记录迭代器的运行数量
return 1;
}
1)Rehash(渐进式扩展函数)
//渐进式扩展函数
int dictRehash(dict* d, int n)
{
if (!dictIsRehashing(d))return 0;
while (n--&&d->ht[0].used!=0)
{ // n代表需要迁移的索引结点数量
dictEntry* de, * nextde;
//确保rehashidx没有越界
assert(d->ht[0].size > (unsigned)d->rehashidx);
while (d->ht[0].table[d->rehashidx] == NULL)
{
d->rehashidx++;
}
// 指向第一个非空索引的链表表头节点
de = d->ht[0].table[d->rehashidx];
// 将链表中的所有节点迁移到新哈希表(重新计算位置,在新表上可能就不是在一条链上了)
while (de)
{
unsigned int h;
// 保存下个节点的指针
nextde = de->next;
//计算结点插入的位置索引
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
// 将刚迁移完的哈希表索引的指针设为空
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++; // 更新 rehash 索引
}
free(d->ht[0].table);
d->ht[0] = d->ht[1];
dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
}
//单步Rehash
static void dictRehashStep(dict* d)
{
//要求该字典上不存在安全迭代器
if (d->iterators == 0) dictRehash(d, 1);
}
2)键值对插入函数
//将键插入到字典中
dictEntry* dictAddRaw(dict* d, void* key)
{
long index;
dictEntry* entry;
dictht* ht;
if (dictIsRehashing(d)) dictRehashStep(d);
if ((index = _dictKeyIndex(d, key, dictHashKey(d, key))) == -1)
// 获取当前key的hash值,如果已经存在直接返回NULL
return NULL;
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
entry = (dictEntry *)malloc(sizeof(*entry));// 分配内存空间
entry->next = ht->table[index]; // 插入到index头部
ht->table[index] = entry;//此处不是指向,相当于直接赋值,让新插入的节点成为新的头部
ht->used++; // 已有节点数加一
dictSetKey(d, entry, key);
return entry;
}
//将给定值对添加到字典中
int dictAdd(dict* d, void* key, void* val) // 添加一个键值对进入到dict中
{
// 尝试添加键到字典,并返回包含了这个键的新哈希节点 T = O(N)
dictEntry* entry = dictAddRaw(d, key);
// 键已存在,添加失败
if (!entry) return DICT_ERR;
// 键不存在,设置节点的值
// T = O(1)
dictSetVal(d, entry, val);
// 添加成功
return DICT_OK;
}
3)索引获取函数
//获取hash index;返回可以将Key插入到哈希表的索引位置
static int dictKeyIndex(dict* d, const void* key)
{
unsigned int h, idx, table;
dictEntry* he;//哈希节点
//如果需要的话,扩展哈希表
if (dictExpandIfNeeded(d) == DICT_ERR)
{
return -1;
}
//获取哈希值
h = dictHashKey(d, key);
for (table = 0; table <= 1; table++)
{
idx = h & d->ht[table].sizemask; //获取索引值
he = d->ht[table].table[idx];// 获得哈希表索引上的结点(存在哈希冲突的话,改结点就是链表结点)
while (he)
{
if (dictCompareKeys(d, key, he->key))//比较当前节点的值与给定值是否相等
return -1;
he = he->next;//继续找下一个节点
}
//程序执行到此处,说明0号哈希表中不存在该节点
//此时若正在进行Rehash,则在1号哈希表中继续找,否则直接退出循环
if (!dictIsRehashing(d))
break;
}
return idx;//程序执行到此处,说明在哈希表中不存在该节点,可以放心插入,直接返回新获得的索引值
}
4)字典哈希表扩展系列函数(非渐进式)
//计算第一个大于等于 size 的 2 的 N 次方,用作新哈希表大小的值
//根据ht[0]的大小,确定rehash操作需要的ht[1]的大小
static unsigned long dictNextPower(unsigned long size)
{
unsigned long i = DICT_HT_INITIAL_SIZE;
if (size >= LONG_MAX) return LONG_MAX;
while (1) {
if (i >= size)
return i;
i *= 2;
}
}
//字典哈希表的扩展
int dictExpand(dict* d, unsigned long size)
{ // 不能在字典正在 rehash 时进行扩展表操作,size 的值也不能小于 0 号哈希表的当前已使用节点
if (dictIsRehashing(d) || d->ht[0].used > size)
return DICT_ERR;
dictht n;//新哈希表
unsigned long realsize = dictNextPower(size);
//确定新哈希表大小
n.size = realsize;
n.sizemask = realsize - 1;
//为1号哈希表分配内存空间
n.table = (dictEntry**)calloc(realsize ,sizeof(dictEntry*));
n.used = 0;
//0号哈希表为空,那么这是一次初始化
if (d->ht[0].table == NULL)
{
d->ht[0] = n;
} // 0号哈希表不空, 那么这是一次rehash
else
{
d->ht[1] = n;
d->rehashidx = 0;
}
return DICT_OK;
}
//根据需要,初始化字典(的哈希表),或者对字典(的现有哈希表)进行扩展
static int dictExpandIfNeeded(dict* d)
{
// 渐进式 rehash 已经在进行了,不能扩展和初始化 直接返回
if (dictIsRehashing(d))
return DICT_OK;//返回ok
// 如果字典(的 0 号哈希表)为空,那么创建并返回初始化大小的 0 号哈希表
if (d->ht[0].size == 0)
return dictExpand(d, DICT_HT_INITIAL_SIZE);
// 一下两个条件之一为真时,对字典进行扩展
//1)字典已使用节点数和字典大小之间的比率接近 1:1,并且 dict_can_resize 为真
//2)已使用节点数和字典大小之间的比率超过 dict_force_resize_ratio
if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used / d->ht[0].size > dict_force_resize_ratio))
{
// 新哈希表的大小至少是目前已使用节点数的两倍
return dictExpand(d, d->ht[0].used * 2);
}
//返回ok标志
return DICT_OK;
}
5)字典哈希表缩减函数
//缩小字典的哈希表
int dictResize(dict* d)
{
//新表所需结点的最小数量
int minimal;
// 不能在关闭 rehash 或者正在 rehash 的时候调用
if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
// 计算让比率接近 1:1 所需要的最少节点数量
minimal = d->ht[0].used;
if (minimal < DICT_HT_INITIAL_SIZE)
minimal = DICT_HT_INITIAL_SIZE;
// 调整字典的大小
return dictExpand(d, minimal);
}
6)哈希表键和值的查找函数(注释与上相同)
//查找键(注释与上类似)
dictEntry* dictFind(dict* d, const void* key)
{
dictEntry* he;
unsigned int h, idx, table;
if (d->ht[0].used == 0)
{
return NULL;
}
if (dictIsRehashing(d))dictRehashStep(d);
h = dictHashKey(d, key);//计算哈希值
for (table = 0; table < 1; table++)
{
idx = h & d->ht[table].sizemask;//计算索引值
he = d->ht[table].table[idx];//获取哈希表索引上的节点
while (he)
{
if (dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
if (!dictIsRehashing(d))return NULL;
}
return NULL;
}
//查找值
void *dictFetchVslue(dict* d, const void* key)
{
dictEntry* he;
//根据键查找在字典中是否存在
he = dictFind(d, key);
//若存在返回该节点的值,反之返回NULL
return he ? dictGetVal(he) : NULL;//dictGetVal为宏定义
}
7)查找并删除给定键的函数(注意:头部节点特殊处理)
//查找并删除给定键的节点
//参数 nofree 决定是否调用键和值的释放函数
//0 表示调用,1 表示不调用
static int dictGenericDelete(dict* d, const void* key, int nofree)
{
unsigned int h, idx;
dictEntry* he, * prevHe;
int table;
if (d->ht[0].size == 0)
{
return DICT_ERR;
}
if (dictIsRehashing(d))dictRehashStep(d);
h = dictHashKey(d, key);
for (table = 0; table < 1; table++)
{
idx = h & d->ht[0].sizemask;
he = d->ht[table].table[idx];
prevHe = NULL;
while (he)
{
if (dictCompareKeys(d, key, he->key))
{
if (prevHe)
{//删除头部之后的节点
prevHe->next = he->next;
}
else
{//删除头部节点
d->ht[table].table[idx] = he->next;
}
//如果需要释放,则调用键和值的释放函数
if (!nofree)
{
dictFreeKey(d, he);
dictFreeVal(d, he);
}
free(he);
d->ht[table].used--;
return DICT_OK;
}
prevHe = he;
he = he->next;
}
//程序只想到此处,说明在0号哈希表中没有该节点,
//此时判断是否正在rehash,若没有进行Rehash,则不用再继续找了,否则在1号哈希表中继续找
if (!dictIsRehashing(d))
break;
}
return DICT_ERR;
}
//删除键
int dictDelete(dict* ht, const void* key)
{
return dictGenericDelete(ht, key, 0);
}
8)清空哈希表
//删除哈希表上的所有节点
int dictClear(dict* d, dictht* ht)
{
unsigned long i;
for (i = 0; i < ht->size && ht->used>0; i++)
{
dictEntry* he, * nextHe;
if ((he = ht->table[i]) == NULL)continue;
while (he != NULL)
{
nextHe = he->next;
dictFreeKey(d, he);//删除键
dictFreeVal(d, he);//删除值
free(he);
ht->used--;
he = nextHe;
}
}
free(ht->table);//释放哈希表结构
dictReset(ht);//重置哈希表属性
return DICT_OK;
}
9)清空并释放整个字典
//删除并释放整个字典
void dictRelease(dict* d)
{//删除并释放两个哈希表结构
dictClear(d, &d->ht[0]);
dictClear(d, &d->ht[1]);
//释放字典结构
free(d);
}