Redis源码分析(二)_dict.c_2

#include "fmacros.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <ctype.h>

#include "dict.h"
#include "zmalloc.h"
#include "redisassert.h"

// 决定是否可以将哈希表的大小设置为恰好可以放下所有节点
static int dict_can_resize = 1;
static unsigned int dict_force_resize_ratio = 5;

// 返回值是static void 表明该函数只在该文件中被调用
static void _dictReset(dictHt *ht) {
    // 在初始化哈希表的时候,因为没有任何节点,哈希表数组指针为空
    ht->table = NULL;
    ht->used = 0;
    ht->size = 0;
    ht->sizemask = 0;
}

int _dictInit(dict *d, void *type, void *privDataPtr) {
    _dictReset(&d->ht[0]);
    _dictReset(&d->ht[1]);

    d->type = type;
    d->privdata = privDataPtr;
    d->reharseidx = -1;
    d->iterators = 0;

    return DICT_OK; // 认为返回值没有必要
}

dict *dictCreate(dictType *type, void *privDataPtr) {
    dict *d = zmalloc(sizeof(dict *));

    /*
    if (d != NULL) {
        _dictInit(d, type, privDataPtr);
    }*/
    _dictInit(d, type, privDataPtr);

    return d; // 如果d为空,相当于返回NULL
}

int dictResize(dict *d) {
    int minimal = 0;

    /* 如果dict_can_resize = 0,直接出错返回
     * 如果dict_can_resize = 1,但是d->rehashidx != -1,出错返回
     */
    if (!dict_can_resize || dictIsHashing(d)) {
        return DICT_ERR;
    }

    /* 1.函数执行到这一步,d->rehashidx一定等于-1,
     *   此时要么ht[0]、ht[1]都没有初始化,要么ht[0]被初始化
     * 2.ht[0]、ht[1]都没有被初始化,调用resize函数,大小被设置为最小值
     *   ht[0]被初始化,但是哈希表中的节点数小于最小值,大小被设置为最小值
     */
    minimal = d->ht[0].used;
    // 
    if (minimal < DICT_HT_INITAL_SIZE) {
        minimal = DICT_HT_INITAL_SIZE;
    }

    // 此时调用dictExpand函数,ht[1]一定是为空,不会出现dictExpand函数注释所说的内存泄漏
    return dictExpand(d, minimal);
}

/* 1.根据指定的大小创建一个哈希表,不是创建一个字典,字典通过函数dictCreate创建
 * 2.对同一个d而言,第一次调用该函数是创建哈希表ht[0],非第一次调用是创建/扩展哈希表ht[1]。
 */
int dictExpand(dict *d, unsigned long size) {
    dictHt n;
    // 根据指定大小获得哈希表的实际大小
    // 相当于对用户行为的矫正:)
    unsigned long realsize = _dictNextPower(size);

    /* 1.当dictIsRehashing(d)为假时需要判断第二个条件
     * 2.是否只有该函数被调用两次之后,条件一不成立?
     * 3.扩展是为了使得现在使用的结点的个数作为哈希表的大小进行申请指针数组的大小,
     *   因为在哈希表中使用的结点数是可肯比哈希表的大小大的。
     *   size其实表明的是哈希表中可以放的单链表的个数,used表明所有单链表中节点的个数
     */
    if (dictIsRehashing(d) || dict->ht[0].used > size) {
        return DICT_ERR
    }

    // 为什么只和ht[0]的大小进行比较,而对ht[1]不管不问?
    if (realsize == d->ht[0].size) {
        return DICT_ERR;
    }

    // table是一个存放指针的数组,所以数组元素的大小是sizeof(dictEntry*),而不是sizeof(dictEntry)
    n->table = zcalloc(realsize * sizeof(dictEntry *));
    n->used = 0;
    n->size = realsize;
    n->sizemask = realsize - 1;

    if (d->ht[0].table == NULL) {
        d->ht[0].table = n;
        return DICT_OK;
    }

    /* 如果57行条件dictIsRehashing(d)不成立,是不是d->ht[0].table就不为空?
     * 如果是的话,当条件dict->ht[0].used > size也不成立的时候,如果哈希表ht[1].table不为空,
     * 然后直接被n覆盖,是不是会造成内存泄漏?
     */
    d->ht[1].table = n;
    d->rehashidx = 0;
    return DICT_OK;
}

/* 1.n:哈希表中指针数组元素的下标的个数
 * 2.d->rehashidx:当前遍历到的哈希表中指针数组元素的下标值
 * 3.empty_visit:整个函数运行的过程中,允许访问的指针数组元素为空的个数,超出该个数,函数直接返回
 *   为了防止函数执行时间过长
 * 4.d->size, n, d->rehashidx, empty_visit都是链表级别的描述
 * 5.ht.used是描述哈希表中的节点个数,是节点级别的描述
 * 6.函数返回1,表示还有事要做,即继续哈希表的迁移
 *   函数返回0,表示哈希表的迁移已经完成,但是最终ht[1]就成为了ht[0]
 *   ht[1]更像是ht[0]为了达到重新哈希的一个跳板
 */
int dictRehash(dict *d, int n) {
    dict *de, *denext;
    unsigned int h = 0;
    int empty_visit = n * 10;

    if (!dictIsRehashing(d)) {
        return 0;
    }

    // 如何保证n不会大于size呢?使用d->ht[0].used >= 0来保证
    while (n-- && d->ht[0].used >= 0) {
        // 如何保证d->rehashidx不会大于d->ht[0].size呢?使用该assert语句保证
        assert(d->ht[0].size > d->rehashidx);

        while (d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx += 1;
            if (--empty_visit == 0) {
                return 1;
            }
        }

        de = d->ht[0].table[d->rehashidx];
        /* 循环遍历ht[0]的一条单链表,将该单链表上的所有节点依次头插入ht[1]中
         * 通过计算索引值找到节点在ht[1]中的那条单链表中,然后进行单链表的头插
         */
        while (de) {
            denext = de->next;
            h = dictHashKey(d, de->key) & d->ht[0].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used -= 1;
            d->ht[1].used += 1;
            de = denext;
        }

        // ht[0]把自己的一条单链表都给了ht[1],自己需要彻底断开连接,把该位置指针清零
        d->ht[0].table[d->rehashidx] = NULL;
        // 指针数组元素往下走一个
        d->rehashidx += 1;
    }

    // 如果哈希表中节点全部赠送完毕,哈希表1就成为了哈希表0
    if (d->ht[0].used == 0) {
        d->ht[0] = d->ht[1];
        zfree(d->ht[1]);
        d->ht[1] = 0;
        d->rehashidx = -1;
        return 0;
    }
}

/*
struct timeval  
{  
__time_t tv_sec;        // Seconds.  
__suseconds_t tv_usec;  // Microseconds.  
};
*/
// 返回当前时间,毫秒表示
long long timeInMiliseconds(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    // 不同数据类型进行运算时,都会自动类型转换为最多字节的数据类型
    return (((long long)(tv.tv_sec) * 1000) + (tv.tv_usec / 1000))
}

// 在指定时间(ms)内进行哈希表的迁移,返回迁移的单链表数
int dictRehashInMiliseconds(dict *d, int ms) {
    int rehashes = 0;
    long long start = timeInMiliseconds();
    while (dictRehash(d, 100)) { // dictRehash函数的返回值能帮助进行该循环
        rehash += 100;
        if (timeInmilliseconds() - start > ms) {
            break;
        }
    }

    return rehashes;
}

// 在字典中没有使用迭代器的情况下,哈希表迁移一条单链表
static void _dictRehashStep(dict *d) {
    if (d->iterators == 0) {
        return dictRehash(d, 1);
    }
}

/* 在字典里添加一个指定键值对的节点
 * 调用一次该函数,会尝试进行两次哈希表的迁移,每次迁移一条单链表
 *
 * 返回DICT_OK: 新添加一个结点,并设置结点的val
 * 返回DICT_ERR:key在哈希表中已经存在
 */
int dictAdd(dict *d, void *key, void *val) {
    // 趁火打劫:在哈希表中插入一个节点的时候,迁移哈希表中的一条单链表
    if (dictIsRehasing(d)) {
        _dictRehashStep(d);
    }

    /* 1.key在dict中已经存在,返回NULL,不存在,返回新节点的指针
     * 2.添加的新的节点里仅指定了节点中的键,没有指定值。值由用户决定是否指定
     * 3.函数名为添加一个生的,光的节点
     */
    dictEntry *entry = dictAddRaw(d, key);
    if (!entry) {
        return DICT_ERR;
    }

    // 设置字典中节点的键对应的值
    dictSetVal(d, entry, val);
    return DICT_OK;
}

dictEntry *dictAddRaw(dict *d, void *key) {
    unsigned int h;
    dictHt *ht;
    dictEntry *entry;
    // 趁火打劫:在哈希表中插入一个节点的时候,迁移哈希表中的一条单链表
    if (dictIsRehashing(d)) {
        _dictRehashStep(d)
    }

    if ((h = _dictKeyIndex(d, key)) == -1) {
        return NULL;
    }
    // 为什么如果rehashidx != -1时,就一定是ht[1]呢?
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];

    // 申请一个节点,然后将该节点头插入单链表中
    entry = zmalloc(sizeof(dictHt));
    entry.next = ht->table[h];
    ht->table[h] = entry;
    ht->used += 1;

    // 设置节点的键
    dictSetKey(d, entry, key);
    return entry;
}

// 找到key值,返回key值所在的结点指针,未找到返回NULL
dictEntry *dictFind(dict *d, void *key) {
    unsigned int h, idx, table;
    dictEntry *entry;
    // 如果哈希表为空,无法进行查找
    if (d->ht[0].size = 0) {
        return NULL;
    }

    // 趁着查找操作的执行,迁移哈希表中的一条单链表
    if (dictIsRehashing(d)) {
        _dictRehashStep(d);
    }

    /* 1.找到Key所在哈希表中的哪一条单链表
    * 2.遍历找到的单链表,比较key值
    * 3.找到key值,返回key值所在的结点指针,未找到返回NULL
    */
    h = dictHashKey(d, key);
    for (table = 0; table < 2; table++) {
        idx = h & d->ht[table].sizemask;
        entry = d->ht[table].table[idx];
        while (entry) {
            if (dictCompareKeys(d, entry->key, key)) {
                return entry;
            }
            entry = entry->next;
        }

        // 如果只有哈希表0,就不进行哈希表1的查找
        if (!dictIsRehashing(d)) {
            break;
        }
    }

    return NULL;
}


/* 返回1:新增加了一个结点
 * 返回0:修改既存结点的val值
 * 1.只要调用了这个函数,哈希表中就一定有该键值对结点
 * 2.返回值旨在告诉调用者到底值新增的结点还是修改的既存结点
 */
int dictReplace(dict *d, void *key, void *val) {
    dictEntry *entry, auxentry;
    // 如果返回DICT_OK,说明新添加一个结点,键值对是key-val
    if (dictAdd(d, key, val) == DICT_OK) {
        return 1;
    }

    // 函数执行到这里,说明哈希表中已经有包含key的结点
    entry = dictFind(d, key);

    // 认为既然已经拷贝了entry,所以设置值和释放值的顺序没有关系TBD
    auxentry = *entry;
    dictSetVal(d, entry, val);
    dictFreeVal(d, &auxentry);
    return 0
}

/* 返回添加结点的指针
 * 1.调用该函数一定会在哈希表中添加一个结点
 * 2.如果包含键的结点,已经存在,直接返回结点指针
 *   如果不存在,添加一个没有指定val的结点
 */
dictEntry *dictReplaceRaw(dict *d, void *key) {
    dictEntry *entry = dictFind(d, key);

    return entry ? entry : dictAddRaw(d, key);
}

/* 返回DICT_OK:删除成功
 * 返回DICT_ERR: 哈希表为空,或者哈希表不为空,但是没有找到含有key的结点
 * 删除指定key值的结点,nofree=0:析构key和val,nofree=1,不析构。
 */
static int dictGenericDelete(dict *d, void *key, int nofree) {
    dictEntry *prev, entry;
    unsigned int h, idx, table;

    if (d->ht[0].size == 0) {
        return DICT_ERR;
    }

    // 趁着删除操作,对哈希表迁移一个单链表
    if (dictIsRehashing(d)) {
        _dictRehashStep(d);
    }

    h = dictHashKey(d, key);
    for (table = 0; table < 2; table++) {
        idx = h & d->ht[table].sizemask;
        entry = d->ht[table].table[idx];
        prev = NULL;
        // 单链表的删除结点操作
        while (entry) {
            if (dictCompareKeys(d, entry->key, key)) {
                // 如果prev=NULL,说明entry是第一个结点
                if (prev) {
                    d->ht[table].table[idx] = entry->next;
                }
                else {
                    prev->next = entry->next;
                }

                if (!nofree) {
                    dictFreeKey(d, entry);
                    dictFreeVal(d, entry);
                }

                // 释放操作和used--容易忘记
                zfree(entry);
                d->ht[table].used -= 1;
                return DICT_OK; // 找到结点并释放之后,直接返回成功
            }

            prev = entry;
            entry = entry->next;
        }

        if (!dictIsRehashing(d)) {
            break;
        }
    }

    return DICT_ERR;
}

int dictDelete(dict *d, void *key) {
    return dictGenericDelete(d, key, 0);
}

int dictDeleteNoFree(dict *d, void *key) {
    return dictGenericDelete(d, key, 0);
}

/* 删除一张哈希表 
 * 返回:DICT_OK,不存在失败情况
 */
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
    unsigned long i;
    dictEntry *he, *henext;

    for (i = 0; i < ht->size && ht->used > 0; i++) {

        // 什么操作?if条件只可能成立一次还是多次?
        if (callback && (i & 65535) == 0) {
            callback(d->private);
        }
        // 当前单链表为空
        if ((he = ht->table[i]) == NULL) {
            continue;
        }
        // 遍历一条单链表执行删除结点操作
        while (he) {
            henext = he->next;
            dictFreeKey(he->key);
            dictFreeVal(he->val);
            zfree(he);
            ht->used -= 1;
            he = henext;
        }
    }

    zfree(ht->table);  // 释放指针数组空间
    _dictReset(ht);    // 重置哈希表

    return DICT_OK;
}

void dictRelease(dict *d) {
    _dictClear(d, &d->ht[0], NULL); // callback = NULL, privdata没有执行释放操作
    _dictClear(d, &d->ht[1], NULL);
    zfree(d); // 释放d->privdata操作是在释放哈希表的时候释放的
}

/* 获得指定键的结点里的值
 * 返回值val:找到含有该键的结点
 * 返回NULL:没有包含该键的结点
 */
void *dictFetchVal(dict *d, void *key) {
    dictEntry *he;
    he = dictFind(d, key);
    return he ? dictGetVal(he) : NULL;
}

/* 申请一个迭代器,并对其进行初始化,该迭代器和字典绑定
 * 返回生成的迭代器
 */
dictIterator *dictGetIterator(dict *d) {
    dictIterator *iter = zmalloc(sizeof(dictIterator));
    iter->d = d;
    iter->index = -1;
    iter->table = 0;
    iter->safe = 0;  // safe = 0:表示迭代的过程中,只能执行dictNext操作
    iter->entry = NULL;
    iter->nextEntry = NULL;

    return iter;
}

// 生成一个安全的迭代器
dictIterator *dictGetSafeIterator(dict *d) {
    dictIterator *iter = dictGetIterator(d);
    iter->safe = 1;  // safe = 1:表示迭代的过程中,可以对字典进行其他操作

    return iter;
}

/* 返回迭代器中下一个结点指针
 * 返回NULL:字典中的结点全部迭代完
 */
dictEntry *dictNext(dictIterator *iter) {
    dict *d = iter->d;

    while (1) {
        if (iter->entry == NULL) {
            // 如果是第一次调用该函数,该if语句可以放在上一个if的上面
            if ((iter->index == -1) && (iter->table == 0)) {
                if (iter->safe) {
                    d->iterators += 1;
                }
                else {
                    iter->fingerprint = dictFingerprint(iter->d);
                }
            }

            iter->index += 1;
            if ((iter->index > d->ht[iter->table].size) {
                // 如果满足可以继续迭代哈希表1的条件,就继续迭代哈希表1
                if (dictIsRehashing(d) && iter->table == 0) {
                    iter->index = 0;
                    iter->table = 1;
                }
                // 否则哈希表0迭代结束,返回NULL
                else {
                    break;
                }
            }

            iter->entry = d->ht[iter->table][iter->index];
        }
        else {
            iter->entry = iter->nextEntry;
        }

        if (iter->entry) {
            iter->nextEntry = iter->entry->next;
            return iter->entry; // 下一个结点不为空时返回该结点
        }
    }

    return NULL;
}

// 释放一个迭代器
void dictReleaseIterator(dictIterator *iter) {
    // 如果迭代器进行过迭代操作,需要执行一下操作
    if (!(iter->index == -1 && iter->table == 0)) {
        if (iter->safe) {
            iter->d->iterators -= 1;
        }
        else {
            /* 不安全的迭代器,需要验证在迭代器存活期间,字典的size、used有没有发生改变
             * 如果发生改变,条件不成立,直接错误
             */
            assert(iter->fingerprint == dictFingerprint(iter->d));
        }
    }

    zfree(iter);
}

dictEntry *dictGetRandomKey(dict *d) {
    unsigned int h;
    int listlen, listele;
    dictEntry *he, orighe;

    // 哈希表0和哈希表1中都没有结点
    if (dictSize(d) == 0) {
        return NULL;
    }

    // 趁机进行哈希表一条单链表的迁移
    if (dictIsRehashing(d)) {
        _dictRehashStep(d);
    }

    // 随机获得一条单链表的头结点指针
    if (dictIsRehasing(d)) {
        do {
            h = (unsigned int)(d->rehashidx + (random() %
                (d->ht[0].size + d->ht[1].size - d->rehashidx)));
            he = (h > d->ht[0].size) > d->ht[0].table[h - d->ht[0].size] :
                 d->ht[1].table[h];
        } while (he == NULL);
    }
    else {
        do {
            //h = (unsigned int)(random() % d->ht[0].size);
            h = random() & d->ht[0].sizemask;
            he = d->ht[0].table[h];
        } while (he == NULL);
    }

    orighe = he;
    listlen = 0;
    // 计算单链表的长度
    while (he) {
        he = he->next;
        listlen += 1;
    }

    // 产生随机数,作为取得该单链表的第几个结点
    listele = ramdom() % listlen;

    he = orighe;
    while (listele--) {
        he = he->next;
    }

    return he;
}

/* 1.查找键是否在哈希表中已经存在
 * 2.如果已经存在返回-1,如果不存在返回键值所在单链表的下标
 */
static int _dictKeyIndex(dict *d, const void *key) {
    unsigned int h, idx, table;
    dictEntry *he;
    // 以在哈希表中添加节点为契机,创建哈希表或者扩展哈希表
    if (_dictExpandIfNeeded(d) == DICT_ERR) {
        return -1;
    }

    /* 1.根据key计算出该键应该在的单链表的位置
     * 2.遍历单链表查找和该key值相同的结点,找到,直接返回-1
     */
    h = dictHashKey(d, key);
    while (table = 0; table < 2; table++) {
        idx = h & dict->ht[table].sizemask;
        he = dict->ht[table].table[idx];
        while (he) {
            if (dictKeyCompare(d, he->key, key)) {
                return -1;
            }
            he = he->next;
        }

        // 如果d->rehashidx == -1,不需要遍历ht[1]
        if (!dictIsRehashing(d)) {
            break;
        }
    }

    return idx;// 返回该key所在的单链表的位置
}

/* 返回:随机获得的结点数
 * 参数:随机获得的结点指针,放在指针数组des中;count是请求获得的结点数
 * 
 */
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
    unsigned int i = 0, j = 0;
    unsigned int table = 0, stored = 0;
    unsigned int maxsteps = 0, emptylen = 0;
    unsigned int maxsizemask = 0; // 为什么定义为unsigned int,而不是unsigned long
    dictEntry *he;

    if (count > dictSize(d)) { // 返回ht[0].used + ht[1].used
        count = (unsigned int)dictSize(d); // 因为ht[0].used的类型是unsigned long,所以这里进行强制类型转换
    }
    maxsteps = count * 10;

    for (i = 0; i < count; i++) {
        if (dictIsRehashing(d)) {
            _dictRehashStep(d);
        }
        else {
            break;
        }
    }

    table = dictIsRehashing(d) ? 2 : 1;
    maxsizemask = d->ht[0].sizemask;
    if (table == 2 && maxsizemask < d->ht[1].sizemask) {
        maxsizemask = d->ht[1].sizemask;
    }

    i = random() % maxsizemask;
    while (stored < count && maxsteps--) {
        for (j = 0; j < table; j++) {

            /* 对于ht[0]已经迁移到ht[1]的单链表直接跳过,跳过之后接下来会到ht[1]中去找i位置的单链表,
             * 如果i >= d->ht[1].size,说明在两个哈希表中都没有我们要找的单链表。
             * 那都没有的话,为什么要设置i的值为d->rehashidx呢?
             * 因为这样的话,在下次进入该循环的时候,下面这个if条件一定不成立,
             * 可以直接去找ht[0]中的单链表。而且设置这个值,只在ht[0]和ht[1]中都没有要找的
             * 单链表的时候。
             * 这样做直接减少了很多无效的操作,即跳过了ht[0]比ht[1]目前迁移的单链表的条数
             * 的差个步骤。
             * 例如:ht[0]中的10条单链表迁移到了ht[1]中产生了5条单链表。
             *       此时d->rehashidx=10,i初值为5,i值为5,6,7,8,9在ht[0]和ht[1]中都是不能获得的单链表下标
             *       下面807行直接跳过了这些值。
             */
            if (table == 2 && j == 0 && i < d->rehashidx) {
                if (i >= d->ht[1].size) {
                    i = d->rehashidx;
                }
                continue;
            }

            if (i > d->ht[j].size) {
                continue;
            }

            he = d->ht[i].table[i];
            if (he == NULL) {
                emptylen++;
                // 对获得的i值进行矫正。连续产生了很多个单链表为空的下标
                if (emptylen > 5 && emptylen > count) {
                    i = random() % maxsizemask;
                    emptylen = 0;
                }
            }
            else {
                emptylen = 0;
                // 找到一条单链表,就一条道走到黑,取其全部的结点
                while (he) {
                    *des = he;
                    des++;
                    stored++;
                    he = he->next;
                    if (stored == count) {
                        return stored;
                    }
                }
            }
        }

        i = (i + 1) % maxsizemask; // 跳到下一条单链表
    }

    return stored;
}

/* 函数功能:
 * 假设v,二进制表示的话是:a1a2a3a4a5a6a7a8
 * 经过该函数之后v的二进制表示是:a8a7a6a5a4a3a2a1
 */
static unsigned long rev(unsigned long v) {
    unsigned long s = 8 * sizeof(v); // bit size; must be power of 2
    unsigned long mask = ~0;
    while ((s >>= 1) > 0) {
        mask ^= (mask << s);
        v = ((v >> s) & mask) | ((v << s) & ~mask);
    }
    return v;
}

/* 对哈希表进行迭代,即使在迭代的过程中哈希表可能在进行迁移。
 * 没调用一次该函数,对一条单链表执行fn操作
 */
unsigned long dictScan(dict *d, unsigned long v,
                       dictScanFunction *fn, void *privdata) {
    dictht *t0, *t1;
    const dictEntry *de;
    unsigned long m0, m1;

    if (dictSize(d) == 0) return 0;

    if (!dictIsRehashing(d)) {
        t0 = &(d->ht[0]);
        m0 = (unsigned long)t0->sizemask;

        de = t0->table[v & m0];
        while (de) {
            fn(privdata, de);
            de = de->next;
        }

    }
    else {
        t0 = &d->ht[0];
        t1 = &d->ht[1];

        /* Make sure t0 is the smaller and t1 is the bigger table */
        if (t0->size > t1->size) {
            t0 = &d->ht[1];
            t1 = &d->ht[0];
        }

        m0 = (unsigned long)t0->sizemask;
        m1 = (unsigned long)t1->sizemask;

        de = t0->table[v & m0];
        while (de) {
            fn(privdata, de);
            de = de->next;
        }

        /* 假设小的哈希表的size=8,大的哈希表中size=32
        small v is: 000
        large v is: 00000
        large v is: 01000
        large v is: 10000
        large v is: 11000
        较小的哈希表中v是000的时候,大的哈希表中遍历的就是上面4个索引。
        即低三位不变,高两位一次加1
        根据该假设分析下面的循环
        */
        do {
            /* Emit entries at cursor */
            de = t1->table[v & m1];
            while (de) {
                fn(privdata, de);
                de = de->next;
            }

            /* 每次进入该代码端,经过语句v = (((v | m0) + 1) & ~m0) | (v & m0);计算之后,
            * v的值的变化如下:
            * 1. 00000--->01000
            * 2. 01000--->10000
            * 3. 10000--->11000
            * 4. 11000--->00000
            */
            v = (((v | m0) + 1) & ~m0) | (v & m0);

        } while (v & (m0 ^ m1)); // m0 ^ m1 = 11000
    }

    v |= ~m0;

    v = rev(v);
    v++;
    v = rev(v);

    return v;
}


/* 根据条件判断是否需要创建或者扩展哈希表
 */
static int _dictExpandIfNeeded(dict *d) {
    if (dictIsRehashing(d)) {
        return DICT_OK;
    }

    // 哈希表还没有创建,进行创建
    if (d->ht[0].size = 0) {
        return dictExpand(d, DICT_HT_INITIAL_SIZE);
    }

    //哈希表已经创建,根据条件判断是否扩展
    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);
    }

    return DICT_OK;
}



/* 根据指定大小设置哈希表的实际大小:是2的整数倍且大于等于指定大小的最小数 */
static unsigned long _dictNextPower(unsigned long size) {
    unsigned long i = DICT_HT_INITAL_SIZE;

    if (size >= MAX_LONG) {
        return MAX_LONG; // 返回unsigned long能保存的最大值
    }

    /*
    while (i < size) {
        i *= 2;
    }
    return i;
    */
    while (1) {
        if (i >= size) {
            return i;
        }
        i *= 2;
    }
}

其他:

1.函数dictScan函数的理解参考:https://blog.csdn.net/gqtcgq/article/details/50533336

2.涉及到哈希算法的函数在此没有列出,可参照上一篇博客:Redis源码分析(二)_dict.c_1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值