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会被释放,后面就是天知道会指向那里了!