redis原始碼閱讀-資料結構篇-字典dict
阿新 • • 發佈:2021-06-10
@
目錄4. 字典實現 dict.h 和 dict.c
資料結構定義
-
狀態碼
#define DICT_OK 0 // 操作成功 #define DICT_ERR 1 // 操作失敗(或出錯)
-
雜湊表kv節點
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; // 雜湊表大小掩碼,用於計算索引值,總是等於 size - 1 unsigned long used; // 該雜湊表已有節點的數量 } dictht;
-
kv操作函式
typedef struct dictType { 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;
-
dict主角
typedef struct dict { dictType *type; // 型別特定函式 void *privdata; // 私有資料 dictht ht[2]; // 雜湊表, 存在一個時佔第一個,存在兩個時正在rehash int rehashidx; // rehash 索引, 當 rehash 不在進行時,值為 -1 int iterators; // 目前正在執行的安全迭代器的數量 } dict;
-
dict 迭代器
typedef struct dictIterator { dict *d; // 被迭代的字典 // table :正在被迭代的雜湊表號碼,值可以是 0 或 1 。 // index :迭代器當前所指向的雜湊表索引位置。 // safe :標識這個迭代器是否安全 int table, index, safe; // entry :當前迭代到的節點的指標 // nextEntry :當前迭代節點的下一個節點 // 因為在安全迭代器運作時, entry 所指向的節點可能會被修改, // 所以需要一個額外的指標來儲存下一節點的位置, // 從而防止指標丟失 dictEntry *entry, *nextEntry; long long fingerprint; /* unsafe iterator fingerprint for misuse detection */ } dictIterator;
部分巨集定義
#define DICT_HT_INITIAL_SIZE 4 // 雜湊表初始大小
// 釋放給定字典節點的值
#define dictFreeVal(d, entry) \
if ((d)->type->valDestructor) \
(d)->type->valDestructor((d)->privdata, (entry)->v.val)
// 設定給定字典節點的值
#define dictSetVal(d, entry, _val_) do { \
if ((d)->type->valDup) \
entry->v.val = (d)->type->valDup((d)->privdata, _val_); \
else \
entry->v.val = (_val_); \
} while(0)
...
// 比對兩個鍵
#define dictCompareKeys(d, key1, key2) \
(((d)->type->keyCompare) ? \
(d)->type->keyCompare((d)->privdata, key1, key2) : \
(key1) == (key2))
#define dictHashKey(d, key) (d)->type->hashFunction(key) // 計算給定鍵的雜湊值
#define dictGetKey(he) ((he)->key) // 返回獲取給定節點的鍵
...
雜湊函式(本部分可選跳過)
-
Thomas Wang's 32 bit Mix Function
unsigned int dictIntHashFunction(unsigned int key) { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); key ^= (key >> 6); key += ~(key << 11); key ^= (key >> 16); return key; }
-
Identity hash function for integer keys
unsigned int dictIdentityHashFunction(unsigned int key) { return key; }
-
MurmurHash2, by Austin Appleby
unsigned int dictGenHashFunction(const void *key, int len) { /* 'm' and 'r' are mixing constants generated offline. They're not really 'magic', they just happen to work well. */ uint32_t seed = dict_hash_function_seed; const uint32_t m = 0x5bd1e995; const int r = 24; /* Initialize the hash to a 'random' value */ uint32_t h = seed ^ len; /* Mix 4 bytes at a time into the hash */ const unsigned char *data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(uint32_t*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } /* Handle the last few bytes of the input array */ switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; /* Do a few final mixes of the hash to ensure the last few * bytes are well-incorporated. */ h ^= h >> 13; h *= m; h ^= h >> 15; return (unsigned int)h; }
-
a case insensitive hash function (based on djb hash)
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) { unsigned int hash = (unsigned int)dict_hash_function_seed; while (len--) hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */ return hash; }
關於hash函式,留坑待填,有時間會整理常見hash函式
Helper函式(可跳過,需要時閱讀)
-
時間函式 O(1)
long long timeInMilliseconds(void) { struct timeval tv; gettimeofday(&tv,NULL); return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); }
-
Fingerprint函式 O(1)
long long dictFingerprint(dict *d) { long long integers[6], hash = 0; int j; integers[0] = (long) d->ht[0].table; integers[1] = d->ht[0].size; integers[2] = d->ht[0].used; integers[3] = (long) d->ht[1].table; integers[4] = d->ht[1].size; integers[5] = d->ht[1].used; for (j = 0; j < 6; j++) { hash += integers[j]; hash = (~hash) + (hash << 21); hash = hash ^ (hash >> 24); hash = (hash + (hash << 3)) + (hash << 8); hash = hash ^ (hash >> 14); hash = (hash + (hash << 2)) + (hash << 4); hash = hash ^ (hash >> 28); hash = hash + (hash << 31); } return hash; }
-
重置/初始化 hashtable,不涉及記憶體 O(1)
static void _dictReset(dictht *ht) { ht->table = NULL; ht->size = 0; ht->sizemask = 0; ht->used = 0; }
-
重置/初始化dict,不涉及記憶體 O(1)
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; // 設定字典的安全迭代器數量 return DICT_OK; }
-
dict進行一步rehash O(1)
static void _dictRehashStep(dict *d) { if (d->iterators == 0) dictRehash(d,1); }
-
遍歷釋放某個hashtable,使用者自定義釋放記憶體,並重置 O(n)
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) { unsigned long i; for (i = 0; i < ht->size && ht->used > 0; i++) { // 遍歷整個雜湊表 T = O(N) dictEntry *he, *nextHe; if (callback && (i & 65535) == 0) callback(d->privdata); if ((he = ht->table[i]) == NULL) continue; // 跳過空索引 while(he) { // 遍歷整個連結串列 T = O(1) nextHe = he->next; dictFreeKey(d, he); // 刪除鍵 dictFreeVal(d, he); // 刪除值 zfree(he); // 釋放節點 ht->used--; // 更新已使用節點計數 he = nextHe; // 處理下個節點 } } zfree(ht->table); // 釋放雜湊表結構 _dictReset(ht); // 重置雜湊表屬性 return DICT_OK; /* never fails */ }
-
嘗試expand,符合條件呼叫expand函式 O(1) / O(n)
static int _dictExpandIfNeeded(dict *d) { // 漸進式 rehash 已經在進行了,直接返回 if (dictIsRehashing(d)) return DICT_OK; // 如果字典(的 0 號雜湊表)為空,那麼建立並返回初始化大小的 0 號雜湊表 T = O(1) if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); // 一下兩個條件同時為真時,對字典進行擴充套件 // 1)字典已使用節點數和字典大小之間的比率大於 1 // 2)dict_can_resize 為真 或者 已使用節點數和字典大小之間的比率超過 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)) { // 新雜湊表的大小至少是目前已使用節點數的兩倍 T = O(N) return dictExpand(d, d->ht[0].used * 2); } return DICT_OK; }
-
返回合適的hashtable size O(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; } }
-
返回合適的新key對應的index O(1)
static int _dictKeyIndex(dict *d, const void *key) { unsigned int h, idx, table; dictEntry *he; if (_dictExpandIfNeeded(d) == DICT_ERR) // rehash O(1) / O(N) return -1; h = dictHashKey(d, key); // 計算 key 的雜湊值 for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; // 計算索引值 he = d->ht[table].table[idx]; // 查詢 key 是否存在 T = O(1) while(he) { if (dictCompareKeys(d, key, he->key)) return -1; // 存在返回 -1 he = he->next; } if (!dictIsRehashing(d)) break; // 如果這時 rehash 正在進行,那麼繼續對 1 號雜湊表進行 rehash } return idx; // 返回索引值 }
-
返回合適的hashtable size
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; } }
dict 建構函式 O(1)
dict *dictCreate(dictType *type, void *privDataPtr) {
dict *d = zmalloc(sizeof(*d));
_dictInit(d, type, privDataPtr); //不涉及 hashtable 記憶體
return d;
}
dict 解構函式 O(N)
void dictRelease(dict *d) {
// 刪除並清空兩個雜湊表
_dictClear(d,&d->ht[0],NULL);
_dictClear(d,&d->ht[1],NULL);
// 釋放節點結構
zfree(d);
}
void dictEmpty(dict *d, void(callback)(void*)) {
// 刪除並清空兩個雜湊表
_dictClear(d,&d->ht[0],callback);
_dictClear(d,&d->ht[1],callback);
// 重置屬性
d->rehashidx = -1;
d->iterators = 0;
}
hashtable 建構函式 O(1)
int dictExpand(dict *d, unsigned long size) {
dictht n; // 新雜湊表
unsigned long realsize = _dictNextPower(size); // 根據 size 引數,計算雜湊表的大小
// 不能在字典正在 rehash 時進行
// 新 size 的值也不能小於 0 號雜湊表的當前已使用節點
if (dictIsRehashing(d) || d->ht[0].used > size)
return DICT_ERR;
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));// 為雜湊表分配空間,並將所有指標指向 NULL
n.used = 0;
if (d->ht[0].table == NULL) { // 如果 0 號雜湊表為空,那麼這是一次初始化:
d->ht[0] = n; //程式將新雜湊表賦給 0 號雜湊表的指標,然後字典就可以開始處理鍵值對了。
return DICT_OK;
}
// 否則這是一次 rehash :
d->ht[1] = n; // 程式將新雜湊表設定為 1 號雜湊表,
d->rehashidx = 0; // 並將字典的 rehash 標識開啟,讓程式可以開始對字典進行 rehash
return DICT_OK;
// 此函式並沒初始化kv 或者 rehash kv,因此 O(1)
}
dict resize O(1)
用於初始化hashtable 或者 rehash
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); // 調整字典的大小 O(1)
}
rehash O(m)
int dictRehash(dict *d, int n) {
if (!dictIsRehashing(d)) return 0; // 只可以在 rehash 進行中時執行
while(n--) { // 進行 N 步遷移,N 為函式引數,N 為槽的個數
dictEntry *de, *nextde;
if (d->ht[0].used == 0) { // 如果舊 0 號雜湊表為空,那麼表示 rehash 執行完畢
zfree(d->ht[0].table); // 釋放舊 0 號雜湊表
d->ht[0] = d->ht[1]; // 0 號雜湊表 指向 rehash 完畢的 1 號雜湊表
_dictReset(&d->ht[1]); // 重置 1 號雜湊表
d->rehashidx = -1; // 關閉 rehash 標識
return 0; // 返回 0 ,向呼叫者表示 rehash 已經完成
}
// 略過陣列中為空的索引,找到下一個非空索引
while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
de = d->ht[0].table[d->rehashidx]; // 指向該索引的連結串列表頭節點
while(de) {
// 將連結串列中的所有節點遷移到新雜湊表 T = O(1)
unsigned int h;
nextde = de->next;
// 儲存下個節點的指標
h = dictHashKey(d, de->key) & d->ht[1].sizemask;// 計算新雜湊表的雜湊值,以及節點插入的索引位置
de->next = d->ht[1].table[h];
// 插入節點到新雜湊表
d->ht[1].table[h] = de;
d->ht[0].used--;
// 更新計數器
d->ht[1].used++;
de = nextde;
// 繼續處理下個節點
}
d->ht[0].table[d->rehashidx] = NULL; // 將剛遷移完的雜湊表索引的指標設為空
d->rehashidx++;
// 更新 rehash 索引
}
return 1;
}
限時rehash
int dictRehashMilliseconds(dict *d, int ms) {
long long start = timeInMilliseconds();// 記錄開始時間
int rehashes = 0;
while(dictRehash(d,100)) {// 100 為步長
rehashes += 100;
if (timeInMilliseconds()-start > ms) break; // 如果時間已過,跳出
}
return rehashes; // 返回rehash數量
}
set k O(1)
dictEntry *dictAddRaw(dict *d, void *key) {
int index;
dictEntry *entry;
dictht *ht;
// 如果條件允許的話,進行單步 rehash T = O(1)
if (dictIsRehashing(d)) _dictRehashStep(d);
if ((index = _dictKeyIndex(d, key)) == -1) // 計算鍵在雜湊表中的索引值,存在返回null
return NULL;
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];// rehash 新增到 1 號雜湊表,否則 0 號
entry = zmalloc(sizeof(*entry)); // 為新節點分配空間
entry->next = ht->table[index]; // 將新節點插入到連結串列表頭
ht->table[index] = entry;
ht->used++; // 更新雜湊表已使用節點數量
dictSetKey(d, entry, key); // 設定新節點的鍵
return entry;
}
set kv O(1)
int dictAdd(dict *d, void *key, void *val) {
dictEntry *entry = dictAddRaw(d,key); // 嘗試新增鍵到字典,並返回包含了這個鍵的新雜湊節點
if (!entry) return DICT_ERR; // 鍵已存在,新增失敗
dictSetVal(d, entry, val); // 鍵不存在,設定節點的值
return DICT_OK;
}
get k O(1)
dictEntry *dictFind(dict *d, const void *key) {
dictEntry *he;
unsigned int h, idx, table;
if (d->ht[0].size == 0) return NULL; // 空
// 如果條件允許的話,進行單步 rehash
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]; // 遍歷給定索引上的連結串列的所有節點,查詢 key
while(he) {
if (dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
if (!dictIsRehashing(d)) return NULL; // 沒找到, rehash時 繼續查詢 1 號雜湊表
}
return NULL; // 進行到這裡時,說明兩個雜湊表都沒找到
}
void *dictFetchValue(dict *d, const void *key) {
dictEntry *he;
he = dictFind(d,key);
return he ? dictGetVal(he) : NULL;
}
update k O(1)
dictEntry *dictReplaceRaw(dict *d, void *key) {
dictEntry *entry = dictFind(d,key);
return entry ? entry : dictAddRaw(d,key); //存在直接返回節點,不存在新建節點
}
update kv O(1)
int dictReplace(dict *d, void *key, void *val) {
dictEntry *entry, auxentry;
// 嘗試set
if (dictAdd(d, key, val) == DICT_OK)
return 1;
// key 已經存在
entry = dictFind(d, key);
auxentry = *entry; // 先儲存原有的值的指標
dictSetVal(d, entry, val); // 然後設定新的值
dictFreeVal(d, &auxentry); // 然後釋放舊值
return 0;
}
delete kv O(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; // 空
// 進行單步 rehash ,T = O(1)
if (dictIsRehashing(d)) _dictRehashStep(d);
h = dictHashKey(d, key); // 計算雜湊值
for (table = 0; table <= 1; table++) { // 選擇雜湊表 T = O(1)
idx = h & d->ht[table].sizemask; // 計算索引值
he = d->ht[table].table[idx]; // 指向該索引上的連結串列
prevHe = NULL;
while(he) { // 遍歷連結串列上的所有節點 T = O(1)
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);
}
zfree(he); // 釋放節點本身
d->ht[table].used--; // 更新已使用節點數量
return DICT_OK; // 返回已找到訊號
}
prevHe = he;
he = he->next;
}
if (!dictIsRehashing(d)) break; // 沒找到, rehash時 繼續查詢 1 號雜湊表
}
return DICT_ERR; // 沒找到
}
int dictDelete(dict *ht, const void *key) {
return dictGenericDelete(ht,key,0);
}
int dictDeleteNoFree(dict *ht, const void *key) {
return dictGenericDelete(ht,key,1);
}
get iter O(1)
dictIterator *dictGetIterator(dict *d) {
dictIterator *iter = zmalloc(sizeof(*iter));
iter->d = d;
iter->table = 0;
iter->index = -1;
iter->safe = 0;
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
dictIterator *dictGetSafeIterator(dict *d) {
dictIterator *i = dictGetIterator(d);
i->safe = 1;
return i;
}
delete iter O(1)
void dictReleaseIterator(dictIterator *iter) {
if (!(iter->index == -1 && iter->table == 0)) {
if (iter->safe) // 釋放安全迭代器時,安全迭代器計數器減一
iter->d->iterators--;
else // 釋放不安全迭代器時,驗證指紋是否有變化
assert(iter->fingerprint == dictFingerprint(iter->d));
}
zfree(iter);
}
迭代 O(1)
dictEntry *dictNext(dictIterator *iter) {
while (1) {
// 指向下個候選節點,待下面查
if (iter->entry == NULL) { // 1) 第一次迭代 2) 連結串列迭代完成,尋找下個連結串列
dictht *ht = &iter->d->ht[iter->table]; // 被迭代的雜湊表
if (iter->index == -1 && iter->table == 0) {// 第一次迭代
if (iter->safe) // 迭代時更新計數
iter->d->iterators++;
else // 否則計算指紋
iter->fingerprint = dictFingerprint(iter->d);
}
iter->index++; // 迭代時增加index
if (iter->index >= (signed) ht->size) { // 當索引大於大小時,迭代完畢
if (dictIsRehashing(iter->d) && iter->table == 0) {// 如果還有1號hashtable
iter->table++;
iter->index = 0;
ht = &iter->d->ht[1];
} else { // 迭代徹底完成
break;
}
}
iter->entry = ht->table[iter->index]; // 指向 index 的連結串列頭,交給下面判斷
} else {
iter->entry = iter->nextEntry; // 正在迭代某個連結串列,指向連結串列的下個節點
}
if (iter->entry) { // 如果不為空,記錄連結串列下個節點,返回
iter->nextEntry = iter->entry->next;
return iter->entry;
}
}
return NULL; // 迭代完畢
}
dict 隨機key O(1)
dictEntry *dictGetRandomKey(dict *d) {
dictEntry *he, *orighe;
unsigned int h;
int listlen, listele;
// 字典為空
if (dictSize(d) == 0) return NULL;
// 進行單步 rehash
if (dictIsRehashing(d)) _dictRehashStep(d);
if (dictIsRehashing(d)) {// rehash ,1 號 hashtable 也算在內
do {
h = random() % (d->ht[0].size+d->ht[1].size);
he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
d->ht[0].table[h];
} while(he == NULL);
} else { // 否則,只算 0 號 hashtable
do {
h = random() & d->ht[0].sizemask;
he = d->ht[0].table[h];
} while(he == NULL);
}
listlen = 0;// 從這個連結串列隨機返回一個節點
orighe = he;
while(he) { // 連結串列長度
he = he->next;
listlen++;
}
listele = random() % listlen;// 連結串列內索引
he = orighe;
while(listele--) he = he->next; // 按索引查詢節點
return he;// 返回隨機節點
}
int dictGetRandomKeys(dict *d, dictEntry **des, int count) {
int j; // hashtable id
int stored = 0; // 返回數量
if (dictSize(d) < count) count = dictSize(d);// 不超過 dictsize
while(stored < count) {
for (j = 0; j < 2; j++) {// 0 1 順序遍歷
unsigned int i = random() & d->ht[j].sizemask; // 隨機 index,只取 1 次
int size = d->ht[j].size;
while(size--) {// 從 index 處的連結串列開始,一致向後迭代至 count 個
dictEntry *he = d->ht[j].table[i];
while (he) {
*des = he;
des++;
he = he->next;
stored++;
if (stored == count) return stored;
}
i = (i+1) & d->ht[j].sizemask;
}
assert(dictIsRehashing(d) != 0);
}
}
return stored;
}
dict scan O(1)
// helper 函式,留坑待填
/* Function to reverse bits. Algorithm from:
* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
static unsigned long rev(unsigned long v) {
unsigned long s = 8 * sizeof(v);
unsigned long mask = ~0;
while ((s >>= 1) > 0) {
mask ^= (mask << s);
v = ((v >> s) & mask) | ((v << s) & ~mask);
}
return v;
}
// 只迭代 v 的連結串列,並返回下次迭代的下標
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 = t0->sizemask; // 記錄 mask
de = t0->table[v & m0]; // 指向雜湊桶
while (de) { // 遍歷桶中的所有節點
fn(privdata, de);
de = de->next;
}
} else { // 迭代有兩個雜湊表的字典,指向兩個雜湊表
t0 = &d->ht[0];
t1 = &d->ht[1];
if (t0->size > t1->size) { // 確保 t0 比 t1 要小
t0 = &d->ht[1];
t1 = &d->ht[0];
}
m0 = t0->sizemask; // 記錄掩碼
m1 = t1->sizemask;
de = t0->table[v & m0]; // 指向桶,並迭代桶中的所有節點
while (de) {
fn(privdata, de);
de = de->next;
}
do { // 迭代大表中的桶,這些桶被索引的 expansion 所指向
de = t1->table[v & m1]; // 指向桶,並迭代桶中的所有節點
while (de) {
fn(privdata, de);
de = de->next;
}
v = (((v | m0) + 1) & ~m0) | (v & m0);
} while (v & (m0 ^ m1));
}
v |= ~m0;
v = rev(v);
v++;
v = rev(v);
return v;
}