redis中動態字元串sds相關的文件為:adlist.h與adlist.c 一、數據結構 redis里定義的雙向鏈表,與普通雙向鏈表大致相同 單個節點: 1 typedef struct listNode { 2 struct listNode *prev; 3 struct listNode * ...
redis中雙向鏈表相關的文件為:adlist.h與adlist.c
一、數據結構
redis里定義的雙向鏈表,與普通雙向鏈表大致相同
單個節點:
1 typedef struct listNode { 2 struct listNode *prev; 3 struct listNode *next; 4 void *value; 5 } listNode;
鏈表:
1 typedef struct list { 2 listNode *head; 3 listNode *tail; 4 void *(*dup)(void *ptr); 5 void (*free)(void *ptr); 6 int (*match)(void *ptr, void *key); 7 unsigned long len; 8 } list;
鏈表以函數指針的方式,實現了複製、銷毀與比較的方法的多態。
迭代器:
1 typedef struct listIter { 2 listNode *next; 3 int direction; 4 } listIter;
迭代器中有個成員變數direction,用於表示當前遍歷的方向。
大致結構:
1 /* 2 +-------------------+ +----------------> +--------------+ <-------+ 3 |listNode *head |--------+ |listNode *prev|-->NULL | 4 +-------------------+ +--------------+ | 5 |listNode *tail |--------+ |listNode *next|----+ | 6 +-------------------+ | +--------------+ | | 7 |void *(*dup)(...) | | |void *value | | | 8 +-------------------+ | +--------------+ | | 9 |void (*free)(...) | | | | 10 +-------------------+ | | | 11 |int (*match)(...) | | | | 12 +-------------------+ +----------------> +--------------+ <--+ | 13 |unsigned long len | |listNode *prev|---------+ 14 +-------------------+ +--------------+ 15 |listNode *next|-->NULL 16 +--------------+ 17 |void *value | 18 +--------------+ 19 */
二、創建
redis中創建一個初始雙向鏈表比較簡單,只要分配好記憶體,並給成員變數賦初值就可以了
1 list *listCreate(void) 2 { 3 struct list *list; 4 5 if ((list = zmalloc(sizeof(*list))) == NULL) 6 return NULL; 7 list->head = list->tail = NULL; 8 list->len = 0; 9 list->dup = NULL; 10 list->free = NULL; 11 list->match = NULL; 12 return list; 13 }
redis中提供了頭插法、尾插法以及指定位置插入節點三種方式向鏈表中添加節點,與普通雙向鏈表無異,此處不做詳細敘述。
三、銷毀
因鏈表中每個節點的value可能指向堆空間,故不能直接把list結構體free,這樣會造成記憶體泄露。需要先將每個節點的value釋放,才可以free結構體
清空所有節點:
1 void listEmpty(list *list) 2 { 3 unsigned long len; 4 listNode *current, *next; 5 6 current = list->head; 7 len = list->len; 8 while(len--) { 9 next = current->next; 10 //若指定了銷毀的函數,則使用指定的函數進行銷毀value 11 if (list->free) list->free(current->value); 12 zfree(current); 13 current = next; 14 } 15 list->head = list->tail = NULL; 16 list->len = 0; 17 }
銷毀鏈表:
1 void listRelease(list *list) 2 { 3 listEmpty(list); 4 zfree(list); 5 }
同樣,redis的鏈表提供了與普通鏈表相同的刪除單個節點的操作,此處也不做敘述。
四、迭代器操作
redis中提供了獲取迭代器的介面
1 listIter *listGetIterator(list *list, int direction) 2 { 3 listIter *iter; 4 5 if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL; 6 if (direction == AL_START_HEAD) 7 iter->next = list->head; 8 else 9 iter->next = list->tail; 10 iter->direction = direction; 11 return iter; 12 }
以AL_START_HEAD為例,生成好的迭代器結構如下:
1 /* 2 +-------------------+ +---> +--------------+ <-------+----+ 3 |listNode *head |----+ |listNode *prev|-->NULL | | 4 +-------------------+ +--------------+ | | +--------------+ 5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next| 6 +-------------------+ | +--------------+ | | +--------------+ 7 |void *(*dup)(...) | | |void *value | | | |int direction | 8 +-------------------+ | +--------------+ | | +--------------+ 9 |void (*free)(...) | | | | 10 +-------------------+ | | | 11 |int (*match)(...) | | | | 12 +-------------------+ +---> +--------------+ <--+ | 13 |unsigned long len | |listNode *prev|---------+ 14 +-------------------+ +--------------+ 15 |listNode *next|-->NULL 16 +--------------+ 17 |void *value | 18 +--------------+ 19 */
迭代器的next方法:
1 listNode *listNext(listIter *iter) 2 { 3 listNode *current = iter->next; 4 5 if (current != NULL) { 6 if (iter->direction == AL_START_HEAD) 7 iter->next = current->next; 8 else 9 iter->next = current->prev; 10 } 11 return current; 12 }
調用一次之後的結構:
1 /* 2 +-------------------+ +---> +--------------+ <-------+ 3 |listNode *head |----+ |listNode *prev|-->NULL | 4 +-------------------+ +--------------+ | +--------------+ 5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next| 6 +-------------------+ | +--------------+ | | | +--------------+ 7 |void *(*dup)(...) | | |void *value | | | | |int direction | 8 +-------------------+ | +--------------+ | | | +--------------+ 9 |void (*free)(...) | | | | | 10 +-------------------+ | | | | 11 |int (*match)(...) | | | | | 12 +-------------------+ +---> +--------------+ <--+----|----+ 13 |unsigned long len | |listNode *prev|---------+ 14 +-------------------+ +--------------+ 15 |listNode *next|-->NULL 16 +--------------+ 17 |void *value | 18 +--------------+ 19 */
再次調用:
1 /* 2 +-------------------+ +---> +--------------+ <-------+ 3 |listNode *head |----+ |listNode *prev|-->NULL | 4 +-------------------+ +--------------+ | +--------------+ 5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next| 6 +-------------------+ | +--------------+ | | | +--------------+ 7 |void *(*dup)(...) | | |void *value | | | | |int direction | 8 +-------------------+ | +--------------+ | | | +--------------+ 9 |void (*free)(...) | | | | | 10 +-------------------+ | | | | 11 |int (*match)(...) | | | | | 12 +-------------------+ +---> +--------------+ <--+ | +-->NULL 13 |unsigned long len | |listNode *prev|---------+ 14 +-------------------+ +--------------+ 15 |listNode *next|-->NULL 16 +--------------+ 17 |void *value | 18 +--------------+ 19 */
調用next函數的返回值為調用之前的listNode首地址
五、其它操作
redis的雙向鏈表還提供了其它操作。其中,查找指定的key與複製整個list依賴於迭代器的使用,並使用到自定義的比較/複製方法。
除此之外,還提供了類似隨機讀取的方式,其內部實現為遍歷,且“越界”時返回NULL。同時,它支持index為負數,表示從尾開始。類似旋轉的操作,把尾節點移至原頭節點之前,成為新的頭節點。當然,還有拼接兩個鏈表的操作。
redis 5.0.7 下載鏈接
http://download.redis.io/releases/redis-5.0.7.tar.gz
源碼閱讀順序參考:
https://github.com/huangz1990/blog/blob/master/diary/2014/how-to-read-redis-source-code.rst