redis6.0.5之dict阅读笔记2-dict之初始化操作和新增元素

redis6.0.5之dict阅读笔记2-dict之初始化操作和新增元素
******************************************************************
/* Create a new hash table *
创建一个新的hash table
dict *dictCreate(dictType *type, void *privDataPtr)
{    
    dict *d = zmalloc(sizeof(*d)); 
    //分配dict类型大小的空间,如果觉得不好理解这种方式,可以转化为如下模式
    //dict *d ;
    // d = zmalloc(sizeof(*d)); 或者  d = zmalloc(sizeof(dict *));
    //因为现有*d的声明,已经知道了*d的类型,再去定义
    
    _dictInit(d,type,privDataPtr); //  初始化新定义的hash table
    return d;
}
******************************************************************
/* Initialize the hash table */
初始化hash table
int _dictInit(dict *d, dictType *type, void *privDataPtr)
{
    _dictReset(&d->ht[0]);  //初始化hash table中数组0的值
    _dictReset(&d->ht[1]);  //初始化hash table中数组1的值
    d->type = type;         //初始化hash table关联的处理函数
    d->privdata = privDataPtr;  //初始化hash table自由的数据
    d->rehashidx = -1;          //初始化rehashidx的值,没有做rehashing
    d->iterators = 0;           //初始化iterators的值,非安全模式
    return DICT_OK;             //初始化成功
}
******************************************************************
/* Reset a hash table already initialized with ht_init().
 * NOTE: This function should only be called by ht_destroy(). */
//在已经到调用ht_init()后重置hash table,这个函数应该被函数ht_destroy()调用
static void _dictReset(dictht *ht)
{
    ht->table = NULL;      //将hash表清空
    ht->size = 0;        //将size置为0
    ht->sizemask = 0;    //将将size置为0
    ht->used = 0;        //将used置为0
}
******************************************************************
因为新增元素需要使用到具体的元素类,我们使用了sds字符串元素,所以需要把sds.h包括了进来
并且在TestDict.c中新增如下元素

static uint8_t dict_hash_function_seed[16];

uint64_t dictSdsHash(const void *key) {
    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
}

int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2)
{
    int l1,l2;
    DICT_NOTUSED(privdata);

    l1 = sdslen((sds)key1);
    l2 = sdslen((sds)key2);
    if (l1 != l2) return 0;
    return memcmp(key1, key2, l1) == 0;
}

dictType keyptrDictType = {
    dictSdsHash,                /* hash function */
    NULL,                       /* key dup */
    NULL,                       /* val dup */
    dictSdsKeyCompare,          /* key compare */
    NULL,                       /* key destructor */
    NULL                        /* val destructor */
};


******************************************************************
/* Add an element to the target hash table */
在目标hash表中添加一个元素
int dictAdd(dict *d, void *key, void *val)
{
    dictEntry *entry = dictAddRaw(d,key,NULL);  //开辟内存空间,添加key

    if (!entry) return DICT_ERR;
    dictSetVal(d, entry, val);  //添加值
    return DICT_OK;
}

******************************************************************
/* Low level add or find: 
底层的元素添加和删除
 * This function adds the entry but instead of setting a value returns the
 * dictEntry structure to the user, that will make sure to fill the value
 * field as he wishes.
这个函数添加了一个实体(hash表元素对象),代替设置一个值而返回了实体的结构给用户,
这个可以让用户随心所欲的设置对象的值
 * This function is also directly exposed to the user API to be called
 * mainly in order to store non-pointers inside the hash value, example:
这个函数也被直接暴露给用户API调用,主要是为了存储在hash对象中的非指针值,示例如下:
 * entry = dictAddRaw(dict,mykey,NULL); //创建一个hash表中的对象
 * if (entry != NULL) dictSetSignedIntegerVal(entry,1000); //设置一个有符号的整型值
 *
 * Return values: 返回值
 *
 * If key already exists NULL is returned, and "*existing" is populated
 * with the existing entry if existing is not NULL.
如果键值已经存在,那么返回空,其中"*existing"将被用已经存在的非空实体填充
 * If key was added, the hash entry is returned to be manipulated by the caller.
 */
如果键值被添加了,那么调用者就可以操作返回的hash实体
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
{
    long index;
    dictEntry *entry;
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);
//如果正在做Rehashing操作,那么就需要做一点以前工作(将迁移工作分布在每个小的操作点,这样更会平滑一点,具体细节后面再讨论)

    /* Get the index of the new element, or -1 if
     * the element already exists. */
     判断键值是否存在,存在就返回-1,不存在就返回新元素的索引位置
    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)
        return NULL;

    /* Allocate the memory and store the new entry.
     * Insert the element in top, with the assumption that in a database
     * system it is more likely that recently added entries are accessed
     * more frequently. */
分配内存空间并且保存这个新的实体元素,基于在一个数据库系统中,
最近添加的元素被访问的更加频繁的假设,将新元素插入到最上面
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; 
    //判断是否在做Rehashing,在的话使用table1,不在就使用table0
    entry = zmalloc(sizeof(*entry));//分配一个实体元素的空间
    entry->next = ht->table[index]; //将原来的第一个位置放在新元素的后面
    ht->table[index] = entry; //将最新添加的元素放在第一个位置
    ht->used++;//使用个数+1

    /* Set the hash entry fields. */
    dictSetKey(d, entry, key); //设置键值
    return entry;      
}

******************************************************************
/* Returns the index of a free slot that can be populated with
 * a hash entry for the given 'key'.
 * If the key already exists, -1 is returned
 * and the optional output parameter may be filled.
返回一个空的槽,能够用来填充给定键值的新实体,如果键值存在,返回-1,
可选参数(*existing)将会被填充
 * Note that if we are in the process of rehashing the hash table, the
 * index is always returned in the context of the second (new) hash table. */
注意到如果我们处于对hash表的rehashing的进程中,那么返回的位置通常位于第二个hash表中(新表)
static long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing)
{
    unsigned long idx, table;
    dictEntry *he;
    if (existing) *existing = NULL; //如果需要设置返回值,那么先清空

    /* Expand the hash table if needed */
    if (_dictExpandIfNeeded(d) == DICT_ERR) //如果需要扩展字典空间失败(后续再看这个函数),则返回失败
        return -1;
    for (table = 0; table <= 1; table++) {
        idx = hash & d->ht[table].sizemask; //查找具体的存放位置(槽)
        /* Search if this slot does not already contain the given key */
        he = d->ht[table].table[idx]; //找到第一个元素
        while(he) {
            if (key==he->key || dictCompareKeys(d, key, he->key)) {
                if (existing) *existing = he; //找到了,需要赋值
                return -1;
            }
            he = he->next; //没有找到,通过链表继续找
        }
        if (!dictIsRehashing(d)) break; //如果不在做Rehashing,那么只查找table0就可以了,table1不需要查找
    }
    return idx;
}
******************************************************************
自己写代码调试过程遇到了一个空指针问题,如下:

    int ret;
    sds key ;
    sds val;
    dict *dd = dictCreate(&keyptrDictType, NULL
    
    for (int i = 0; i < 10 ; ++i) {
        ret = dictAdd(dd, sdscatprintf(key, "%d", i), sdscatprintf(val, "%d", i));
        printf("Add ret  %d is :%d  \n", i, ret);
    }
sdscatprintf(key, "%d", i),使用的时候key会被释放,后面就是天知道会指向那里了!

 

posted on 2020-08-10 17:28  子虚乌有  阅读(258)  评论(0)    收藏  举报