Redis源码阅读笔记—adlist

Redis系统使用C语言自己实现了一个链表,主要相关代码为adlist.h跟adlist.c文件当中。

链表的实现很简单,且跟传统数据结构中的链表实现基本一致,只不过多了一些辅助的使用方法。接下来总结从结构体开始分析记录,再分析相关函数。

listNode:

typedef struct listNode {
    struct listNode *prev;//指向前一个元素的指针
    struct listNode *next;//指向下一个元素的指针
    void *value;//元素内容的指针
} listNode;
跟传统链表相同,有着前后元素指针,只不过元素的实际内容使用void指针存储,意味着存储内容的格式不再有限制,不需要因为存储值的类型变更而声明多个链表。

listIter:

typedef struct listIter {
    listNode *next;//指向下一个元素的指针
    int direction;//遍历方向
} listIter;
#define AL_START_HEAD 0
#define AL_START_TAIL 1

这里是实现了链表的迭代器(iterator),用于方便的遍历、访问整个链表。其中next为指向下一个元素的指针,direction则是遍历的方便。direction的值取为AL_START_HEAD或者AL_START_TAIL,分别代表从头部开始遍历,及从尾部遍历。比如说当direction为AL_START_HEAD时,next跟listNode中的next值相同,当direction为AL_START_TAIL时,next跟listNode中的prev值相同。

list:

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;

这里是真正访问、管理链表的入口。其中,head、tail、len这些属性跟其他链表相同,代表链表的开始、结尾与长度。有意思的是dup、free、match函数,为用户使用链表提供了更加灵活的服务。
dup函数用于链表的元素内容复制,当链表需要复制、移植等操作的时候,变回使用dup函数来复制元素的值,因为元素的值可能是一个自定义的结构体或者类,存在着指针等成员,一般直接只用'='符号进行赋值会造成隐患,除非重构'='函数。当然,你也可以选择不使用dup函数,给dup赋值NULL就可以使用默认的'='操作符来复制元素内容。
free函数用于链表的元素内容释放,也是为了一些指针成员变量而设计,一般指正成员都需要手动分配一块内存来使用,若是不相应的调用free来释放内存,将会导致内存泄露。同dup一样,free在赋值NULL之后,便会调用redis自带的zfree函数来释放。
match函数用于判断链表的元素内容是否匹配,在通过一个值在链表查找是否拥有该元素时,如果match函数没有被赋值为NULL,则通过match函数来比较两个值是否相等。

listCreate函数:

list *listCreate(void)
{
    struct list *list;
    if ((list = zmalloc(sizeof(*list))) == NULL)//申请内存空间
        return NULL;
    list->head = list->tail = NULL;//初始化流程
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match - NULL;
    return list;
}
listCreate函数用于创建并初始化一个链表,其中zmalloc为redis自带的函数,zmalloc对malloc进行了一定的封装。

listEmpty函数:

void listEmpty(list *list)
{
    unsigned long len;
    unsigned *current, *next;

    current = list->head;
    len = list->len;
    //迭代释放链表中的元素
    while(len--)
    {
        next = current->next;
        //判断是否有自定义free函数,并进行相应处理
        if (list->free) list->free(current->value);
        //释放指定节点
        zfree(current);
        //指向下一个节点
        current = next;
    }
    //初始化
    list->head = list->tail = NULL;
    list->len = 0;
}
listEmpty函数用于清空整个链表,从head开始,通过迭代访问所有节点来释放所有节点,释放内存。

listRelease函数:

void listRelease(list *list)
{
    listEmpty(list);//清空整个链表
    zfree(lit);//释放链表本身
}
listRelease函数用于释放整个链表,包括链表头,zfree同zmalloc一样,都是redis自己封装的函数。

listAddNodeHead函数:

list *listAddNodeHead(list *list, void *value)
{
    listNode *node;
    //创建一个节点并申请内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {//链表长度为0的情景
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->len++;//增加链表长度
    return list;
}
listAddNodeHead函数为一个元素申请一个节点,并添加到链表最前端。实现逻辑不复杂,只要是有基础数据结构知识的都是可以一眼了解的。

listAddNodeTail:

list *listAddNodeTail(list *list, void *value)
{
        listNode *node;

        //创建一个节点并申请内存
        if ((node = zmalloc(sizeof(*node)) == NULL)
            return NULL;
        node->value = value;
        if (list->len == 0) {//链表长度为0的情景
            list->head = list->tail = node;
            node->prev = node->next = NULL;
        } else {
            node->prev = list->tail;
            node->next = NULL;
            list->tail->next = node;
            list->tail = node;
        }
        list->len++;//增加链表长度
        return list;
}
与上述的函数列斯,listAddNodeTail函数为一个元素申请一个节点,并添加到链表最后端。

listInsertNode:

list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;

    //创建一个节点并申请内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (after) {
        //插入一个新节点在指定节点后
        node->prev = old_node;
        node->next = old_node->next;
        if (list->tail == old_node) {
            //指定节点为链表最后节点
            list->tail = node;
        }
    } else {
        //插入一个新节点在指定节点前
        node->next = old_node;
        node->prev = old_node->prev;
        if (list->head == old_node )
        {
            //指定节点为链表第一个节点
            list->head = node;
        }
    }
    //更新链表节点关系
    if (node->prev != NULL) {
        node->prev->next = node;
    }
    if (node->next != NULL) {
        node->next->prev = node;
    }
    list->len++;
    return list;
}
listInsertNode函数为一个元素创建节点,并插入在指定节点前或者指定节点后。after参数来控制节点插入的位置,after为0则插入在指定节点前,after为非0则插入在指定节点后。

listDelNode:

void listDelNode(list *list, listNode *node)
{
    //判断删除的节点前一个是否为空
    if (node->prev)
        node->prev->next = node->next;
    else
        list->head = node->next;
    //判断删除节点的下一个节点是否为空
    if (node->next)
        node->next->prev = node->prev;
    else
        list->tail = node->prev;
    //判断自定义free函数是否为空
    if (list->free) list->free(node->value);
    //释放节点
    zfree(node);
    //自减链表长度
    list->len--;
}
listDelNode函数为从链表当中删除指定的一个节点。

listGetIterator & listReleaseIterator & listRewind & listRewindTail & listNext:

listIter *listGetIterator(list *list, int direction)
{
    listIter *iter;

    //创建一个迭代器并申请内存
    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
    if (direction == AL_START_HEAD)
        //从头部开始遍历的迭代器
        iter->next = liest->head;
    else
        //从尾部开始遍历的迭代器
        iter->next = list->tail;
    iter->direction = direction;//更新迭代器的访问顺序属性
    return iter;
}

void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

void listRewind(list *list, listIter *li) {
    li->next = list->head;
    li->direction = AL_START_HEAD;
}

void listRewindTail(list *lit, listIter *li) {
    li->next = list->tail;
    li->direction = AL_START_TAIL;
}

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;
}
以上五个函数全部跟迭代器相关,分别为创建迭代器、销毁迭代器、重置迭代器到链表头部、重置迭代器到链表尾部、获取迭代器下一个节点。

listDup:

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

    if ((copy = listCreate() == NULL)//创建一个新链表
        return NULL;
    copy->dup = orgi->dup;//复制链表相关函数
    copy->free = orig->free;
    copy->match = orig->match;
    listRewind(orig, &iter);//创建迭代器,迭代访问源链表
    while ((node = listNext(&iter)) != NULL) {
        void *value;

        if (copy->dup) {//判断是否有自定义复制函数
            value = copy->dup(node->value);
            if (value == NULL) {
                listRelease(copy);
                return NULL;
            }
        } else
            value = node->value;
        if (listAddNodeTail(copy, value) == NULL) {
            listRelease(copy);
            return NULL;
        }
    }
    return copy;
}
listDup函数为复制一个已知链表。

listSearchKey & listIndex & listRotate & listJoin:

listNode *listSearchkey(list *list, void *key)
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);
    //遍历链表
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {//是否使用自定义match函数判断命中
            if (list->match(node->value. key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

listNode *listIndex(list *list, long index) {
    listNode *n;

    if (index < 0) {//索引为负数,从尾部开始计数
        index = (-index)-1;
        n = list->tail;
        while(index-- && n) n = n->prev;
    } else {//从头部开始计数
        n = list->head;
        while(index-- && n) n = n->next;
    }
    return n;
}

void listRotate(list *list) {
    listNode *tail = list->tail;

    if (listLength(list) <= 1) return;

    /* Detach current tail */
    list->tail = tail->prev;
    list->tail->next = NULL;
    /* Move it as head */
    list->head->prev = tail;
    tail->prev = NULL;
    tail->next = list->head;
    list->head = tail;
}

void listJoin(list *l, list *o) {
    //将o连边的头接到l链表尾部
    if (o->head)
        o->head->prev = l->tail;

    //将l链表的尾部指向o链表的头部
    if (l->tail)
        l->tail->next = o->head;
    else
        l->head = o->head;

    //更新l链表属性
    l->tail = o->tail;
    l->len += o->len;

    /* Setup other as an empty list. */
    o->head = o->tail = NULL;
    o->len = 0;
}
以上四个函数为最后的list相关api了。
listSearchKey为在链表当中寻找指定元素的节点,若成功找到则返回该节点,若找不到则返回NULL。这里,若是自定义了match函数,则使用match函数来比较元素的值,否则则使用默认的'='操作符。
listIndex函数使用索引的方式访问链表当中的元素,类似数组的[index]或者向量里面的at(index),只不过listIndex多了一个从尾部计数返回的功能,跟python的数据相似,采用负数的索引,则从尾部开始计数,并返回节点。若索引超出界限则返回NULL。
listRotate函数用于将链表最后节点移到最开始节点。
listJoin函数用于链表的拼接,将o链表拼接与l链表的尾部。

以上便是redis的链表相关的所有代码,总结来说,redis的链表实现还是很精简、易懂的,只要有一定的数据结构跟C语言基础就可以很快读懂它的代码了。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值