redis 5.0.7 源码阅读——双向链表
redis中双向链表相关的文件为:adlist.h与adlist.c
一、数据结构
redis里定义的双向链表,与普通双向链表大致相同
单个节点:
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
链表:
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
链表以函数指针的方式,实现了复制、销毁与比较的方法的多态。
迭代器:
typedef struct listIter {
listNode *next;
int direction;
} listIter;
迭代器中有个成员变量direction,用于表示当前遍历的方向。
大致结构:
/*
+-------------------+ +----------------> +--------------+ <-------+
|listNode *head |--------+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ |
|listNode *tail |--------+ |listNode *next|----+ |
+-------------------+ | +--------------+ | |
|void *(*dup)(...) | | |void *value | | |
+-------------------+ | +--------------+ | |
|void (*free)(...) | | | |
+-------------------+ | | |
|int (*match)(...) | | | |
+-------------------+ +----------------> +--------------+ <--+ |
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
二、创建
redis中创建一个初始双向链表比较简单,只要分配好内存,并给成员变量赋初值就可以了
list *listCreate(void)
{
struct list *list; if ((list = zmalloc(sizeof(*list))) == NULL)
return NULL;
list->head = list->tail = NULL;
list->len = ;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
redis中提供了头插法、尾插法以及指定位置插入节点三种方式向链表中添加节点,与普通双向链表无异,此处不做详细叙述。
三、销毁
因链表中每个节点的value可能指向堆空间,故不能直接把list结构体free,这样会造成内存泄露。需要先将每个节点的value释放,才可以free结构体
清空所有节点:
void listEmpty(list *list)
{
unsigned long len;
listNode *current, *next; current = list->head;
len = list->len;
while(len--) {
next = current->next;
//若指定了销毁的函数,则使用指定的函数进行销毁value
if (list->free) list->free(current->value);
zfree(current);
current = next;
}
list->head = list->tail = NULL;
list->len = ;
}
销毁链表:
void listRelease(list *list)
{
listEmpty(list);
zfree(list);
}
同样,redis的链表提供了与普通链表相同的删除单个节点的操作,此处也不做叙述。
四、迭代器操作
redis中提供了获取迭代器的接口
listIter *listGetIterator(list *list, int direction)
{
listIter *iter; if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
if (direction == AL_START_HEAD)
iter->next = list->head;
else
iter->next = list->tail;
iter->direction = direction;
return iter;
}
以AL_START_HEAD为例,生成好的迭代器结构如下:
/*
+-------------------+ +---> +--------------+ <-------+----+
|listNode *head |----+ |listNode *prev|-->NULL | |
+-------------------+ +--------------+ | | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | +--------------+
|void *(*dup)(...) | | |void *value | | | |int direction |
+-------------------+ | +--------------+ | | +--------------+
|void (*free)(...) | | | |
+-------------------+ | | |
|int (*match)(...) | | | |
+-------------------+ +---> +--------------+ <--+ |
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
迭代器的next方法:
listNode *listNext(listIter *iter)
{
listNode *current = iter->next; if (current != NULL) {
if (iter->direction == AL_START_HEAD)
iter->next = current->next;
else
iter->next = current->prev;
}
return current;
}
调用一次之后的结构:
/*
+-------------------+ +---> +--------------+ <-------+
|listNode *head |----+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | | +--------------+
|void *(*dup)(...) | | |void *value | | | | |int direction |
+-------------------+ | +--------------+ | | | +--------------+
|void (*free)(...) | | | | |
+-------------------+ | | | |
|int (*match)(...) | | | | |
+-------------------+ +---> +--------------+ <--+----|----+
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
再次调用:
/*
+-------------------+ +---> +--------------+ <-------+
|listNode *head |----+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | | +--------------+
|void *(*dup)(...) | | |void *value | | | | |int direction |
+-------------------+ | +--------------+ | | | +--------------+
|void (*free)(...) | | | | |
+-------------------+ | | | |
|int (*match)(...) | | | | |
+-------------------+ +---> +--------------+ <--+ | +-->NULL
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
调用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
redis 5.0.7 源码阅读——双向链表的更多相关文章
- redis 5.0.7 源码阅读——整数集合intset
redis中整数集合intset相关的文件为:intset.h与intset.c intset的所有操作与操作一个排序整形数组 int a[N]类似,只是根据类型做了内存上的优化. 一.数据结构 ty ...
- redis 5.0.7 源码阅读——跳跃表skiplist
redis中并没有专门给跳跃表两个文件.在5.0.7的版本中,结构体的声明与定义.接口的声明在server.h中,接口的定义在t_zset.c中,所有开头为zsl的函数. 一.数据结构 单个节点: t ...
- redis 5.0.7 源码阅读——字典dict
redis中字典相关的文件为:dict.h与dict.c 与其说是一个字典,道不如说是一个哈希表. 一.数据结构 dictEntry typedef struct dictEntry { void * ...
- redis 5.0.7 源码阅读——动态字符串sds
redis中动态字符串sds相关的文件为:sds.h与sds.c 一.数据结构 redis中定义了自己的数据类型"sds",用于描述 char*,与一些数据结构 typedef c ...
- redis 5.0.7 源码阅读——压缩列表ziplist
redis中压缩列表ziplist相关的文件为:ziplist.h与ziplist.c 压缩列表是redis专门开发出来为了节约内存的内存编码数据结构.源码中关于压缩列表介绍的注释也写得比较详细. 一 ...
- Linux 0.11源码阅读笔记-文件管理
Linux 0.11源码阅读笔记-文件管理 文件系统 生磁盘 未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件. 磁盘分区 生磁盘可以被分区,分区中可以安装文件系统, ...
- Linux 0.11源码阅读笔记-中断过程
Linux 0.11源码阅读笔记-中断过程 是什么中断 中断发生时,计算机会停止当前运行的程序,转而执行中断处理程序,然后再返回原被中断的程序继续运行.中断包括硬件中断和软件中断,硬中断是由外设自动产 ...
- Linux 0.11源码阅读笔记-总览
Linux 0.11源码阅读笔记-总览 阅读源码的目的 加深对Linux操作系统的了解,了解Linux操作系统基本架构,熟悉进程管理.内存管理等主要模块知识. 通过阅读教复杂的代码,锻炼自己复杂项目代 ...
- redis 4.0.8 源码包安装集群
系统:centos 6.9软件版本:redis-4.0.8,rubygems-2.7.7,gcc version 4.4.7 20120313,openssl-1.1.0h,zlib-1.2.11 y ...
随机推荐
- CSS动效集锦,视觉魔法的碰撞与融合(三)
本文讲述的原理和相关demo 扇形DIV的使用——实现雷达扫描图 DIV环形布局—实现loading圈 动画的向量合成—实现抛物线动画 无限滚动动画—实现跑马灯效果 perspective和trans ...
- python3小脚本-监控服务器性能并插入mysql数据库
操作系统: centos版本 7.4 防火墙 关闭 selinux 关闭 python版本 3.6 mysql版本 5.7 #操作系统性能脚本 [root@localhost sql]# cat cp ...
- 传递额外的值 Passing Extra Values |在视图中生成输出URL | 高级路由特性 | 精通ASP-NET-MVC-5-弗瑞曼
结果呢 <a href="/App/DoCustomVariable?id=Hello">This is an outgoing URL</a> 理解片段变 ...
- NOI2.4 2011
描述 已知长度最大为200位的正整数n,请求出2011^n的后四位. 输入 第一行为一个正整数k,代表有k组数据,k<=200接下来的k行, 每行都有一个正整数n,n的位数<=200 输出 ...
- JavaScript学习之路1
1. 变量类型只有var var money=5; var house="big house"; 示例如下 <!DOCTYPE html> <html> & ...
- linux系统iot平台编程阶段总结
1.inline内联函数 在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗. 为了解决这个问题,特别的引入了inline修饰符,表示为内联函数. 在使用循 ...
- [SDOI2010]魔法猪学院(A*,最短路)
[SDOI2010]魔法猪学院(luogu) Description 题目描述 iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig ...
- DOCKER 学习笔记8 Docker Swarm 集群搭建
前言 在前面的文章中,已经介绍如何在本地通过Docker Machine 创建虚拟Docker 主机,以及也可以在本地Windows 创建虚拟主机,也是可以使用的.这一节,我们将继续学习 Docker ...
- [win]更改win终端编码
更改cmd的编码格式 chcp: 显示当前的编码格式 chcp 65001: 更改当前编码格式为UTF-8 字体选择`Lucida Console` 更改PowerShell编码格式(from zhi ...
- Go语言实现:【剑指offer】数据流中的中位数
该题目来源于牛客网<剑指offer>专题. 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位 ...