redis5.0.7源码阅读——双向链表

database

redis中动态字符串sds相关的文件为:adlist.h与adlist.c

一、数据结构

redis里定义的双向链表,与普通双向链表大致相同

单个节点:

1 typedef struct listNode {

2struct listNode *prev;

3struct listNode *next;

4void *value;

5 } listNode;

链表:

1 typedef struct list {

2 listNode *head;

3 listNode *tail;

4void *(*dup)(void *ptr);

5void (*free)(void *ptr);

6int (*match)(void *ptr, void *key);

7 unsigned long len;

8 } list;

链表以函数指针的方式,实现了复制、销毁与比较的方法的多态。

迭代器:

1 typedef struct listIter {

2 listNode *next;

3int 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{

3struct list *list;

4

5if ((list = zmalloc(sizeof(*list))) == NULL)

6return NULL;

7 list->head = list->tail = NULL;

8 list->len = 0;

9 list->dup = NULL;

10 list->free = NULL;

11 list->match = NULL;

12return list;

13 }

 

redis中提供了头插法、尾插法以及指定位置插入节点三种方式向链表中添加节点,与普通双向链表无异,此处不做详细叙述。

三、销毁

因链表中每个节点的value可能指向堆空间,故不能直接把list结构体free,这样会造成内存泄露。需要先将每个节点的value释放,才可以free结构体

清空所有节点:

 1void listEmpty(list *list)

2{

3 unsigned long len;

4 listNode *current, *next;

5

6 current = list->head;

7 len = list->len;

8while(len--) {

9 next = current->next;

10//若指定了销毁的函数,则使用指定的函数进行销毁value

11if (list->free) list->free(current->value);

12 zfree(current);

13 current = next;

14 }

15 list->head = list->tail = NULL;

16 list->len = 0;

17 }

销毁链表:

1void 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

5if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;

6if (direction == AL_START_HEAD)

7 iter->next = list->head;

8else

9 iter->next = list->tail;

10 iter->direction = direction;

11return 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

5if (current != NULL) {

6if (iter->direction == AL_START_HEAD)

7 iter->next = current->next;

8else

9 iter->next = current->prev;

10 }

11return 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

以上是 redis5.0.7源码阅读——双向链表 的全部内容, 来源链接: utcz.com/z/532071.html

回到顶部