redis源码阅读理解,及相关语言细节---adlist.c

redis现在在分布式上的应用十分的普遍,作为一个高效的DB,其并发吞吐数据的能力十分的优秀,所以通过源码,可以让我们详细的了解相关的知识和实现细节,我希望能坚持下去。在六月份之前弄清楚redis的相关框架知识,能够提升自己的代码能力。这也是我写的第一篇csdn,希望自己能坚持下去。

首先redis 的list声明了list的相关结构,代码如下:

typedef struct listNode {

    // 前驱节点
    struct listNode *prev;

    // 后继节点
    struct listNode *next;

    // 值
    void *value;

} listNode;
listnode结构声明了前向节点和后向节点,即实现的是双向链表,同时用void*来声明变量,这里要特别说明一下void*,void*可以指向任意的数据类型,也就是说你可以这样写:
void *ptr;  
int temp;  //int 可以替换为其他的数据类型 如float,double等等。
ptr = &temp;

也就是说这样的链表节点是可以接收任何数据类型的。

接下来.h文件声明了数据迭代器:

typedef struct listIter {

    // 下一节点
    listNode *next;

    // 迭代方向
    int direction;

} listIter;

在迭代器中声明了一个节点指针,指针名称为next,同时比较轻巧的设计出现了,在声明迭代器的同时,还会有一个direction数据传入, AL_START_HEAD 并且默认值值为0,AL_START_TAIL并且默认值值为1,这个值声明在cdlist.h中,当direction等于0的时候迭代器的指向默认为下个节点,当direction等于1的时候迭代器将指向上一个节点,代码实现如下:

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;
}

接下来头文件声明了list结构体:

typedef struct list {

    // 表头指针
    listNode *head;

    // 表尾指针
    listNode *tail;

    // 节点数量
    unsigned long len;

    // 复制函数
    void *(*dup)(void *ptr);
    // 释放函数
    void (*free)(void *ptr);
    // 比对函数
    int (*match)(void *ptr, void *key);
} list;
这里面运用了c的一个小技巧,即在结构体中声明了函数,这样的声明相当于c++中的class,不同的是这样声明之后的函数为public的,而c++则是private的,但是在源码中并未对函数实现,而是提供了其他的函数来实现对应的功能,这样的目的应该是便于用户有特殊需求是自己重写对应的函数,(并且在.h文件中提供了相应的宏定义,可以通过调用函数,来完成对match等函数的写入)在实际使用中可以看到,当有自己的定义的时候会优先调用自定义的方法,这里我们通过match函数来举例看一下:
listNode *listSearchKey(list *list, void *key)
{
    listIter *iter;
    listNode *node;

    // 使用迭代器查找
    iter = listGetIterator(list, AL_START_HEAD);
    while((node = listNext(iter)) != NULL) {
        if (list->match) {
            // 使用列表自带的匹配器进行比对
            if (list->match(node->value, key)) {
                listReleaseIterator(iter);
                return node;
            }
        } else {
            // 直接用列表的值来比对
            if (key == node->value) {
                listReleaseIterator(iter);
                return node;
            }
        }
    }

    // 没找到
    listReleaseIterator(iter);
    return NULL;
}

//if (list->match(node->value, key))

这一句说明如果我们自己定义了相关的函数,将会优先执行自定义的函数,这点在实际上十分的优秀,免去了在特殊要求时候的代码重构,十分的值得学习。

list结构的其他部分声明了两个头尾节点指针,以及节点数量。

接下来就是具体的函数实现代码,大部分的代码是十分简单的,我选取两个最有代表性的来详细的解读一下,

list *listDup(list *orig)
{
    list *copy;
    listIter *iter;
    listNode *node;

    if ((copy = listCreate()) == NULL)
        return NULL;

    // 复制属性
    copy->dup = orig->dup;
    copy->free = orig->free;
    copy->match = orig->match;

    // 复制节点
    iter = listGetIterator(orig, AL_START_HEAD);
    while((node = listNext(iter)) != NULL) {

        // 复制节点值
        void *value;
        
        if (copy->dup) {
            value = copy->dup(node->value);
            if (value == NULL) {
                listRelease(copy);
                listReleaseIterator(iter);
                return NULL;
            }
        } else
            value = node->value;
        
        // 将新节点添加到新列表末尾
        if (listAddNodeTail(copy, value) == NULL) {
            listRelease(copy);
            listReleaseIterator(iter);
            return NULL;
        }
    }

    listReleaseIterator(iter);

    return copy;
}
这个函数为链表复制函数,首先生命了链表迭代器,空链表以及单一的一个节点,首先会将原有链表结构中的match,dup,等一一赋值,这里面有个细节需要注意一下,在调用结构体中的对应变量时,有两种形式(. 和->)当结构体中的变量声明为指针变量的时候,需要用->来完成获取,而当结构中的值为变量的时候,那么用的应该是.  。
接下来我们首先通过函数将迭代器指向对应的头结点,然后判断是否已经到达列表的末端,通过调用函数

if (listAddNodeTail(copy, value) == NULL)

这句话比较的简练,首先在if语句之中执行了对应的添加节点函数,如果执行不成功那么说明整个内存已经满了,直接返回null,在最后将迭代器释放,然后将新建的list返回,复制函数就结束了。

总结,只是简单的整理了list的相关代码理解,list的整体代码并不难,大体上对于学过数据结构的人来说,理解并不困难,但是有一些精巧的设计十分的值得我们学习。

猜你喜欢

转载自blog.csdn.net/zhuangtongy/article/details/80205260