redis 源碼分析--鏈表

簡介

       鏈表在Redis中的應用非常廣泛,比如列表鍵的底層實現之一就是鏈表。當一個列表鍵包含了數量表較多的元素,又或列表中包含的元素都是比較長的字符串時,Redis就會用鏈表作爲列表鍵的底層實現。

redis鏈表實現所需的結構

/*
 * 雙端鏈表節點
 */
typedef struct listNode {

    // 前置節點
    struct listNode *prev;

    // 後置節點
    struct listNode *next;

    // 節點的值
    void *value;

} listNode;

/* Directions for iterators 
 *
 * 迭代器進行迭代的方向
 */
// 從表頭向表尾進行迭代
#define AL_START_HEAD 0
// 從表尾到表頭進行迭代
#define AL_START_TAIL 1


/*
 * 雙端鏈表迭代器
 */
typedef struct listIter {

    // 當前迭代到的節點
    listNode *next;

    // 迭代的方向
    int direction;

} listIter;

/*
 * 雙端鏈表結構
 */
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;

 

 

Redis鏈表特性

Redis鏈表操作主要API實現

/*
 * 創建一個新的鏈表
 *
 * 創建成功返回鏈表,失敗返回 NULL 。
 *
 * T = O(1)
 */
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;
}

/*
 * 釋放整個鏈表,以及鏈表中所有節點
 *
 * T = O(N)
 */
void listRelease(list *list)
{
    unsigned long len;
    listNode *current, *next;

    // 指向頭指針
    current = list->head;
    // 遍歷整個鏈表
    len = list->len;
    while(len--) {
        next = current->next;

        // 如果有設置值釋放函數,那麼調用它
        if (list->free) list->free(current->value);

        // 釋放節點結構
        zfree(current);

        current = next;
    }

    // 釋放鏈表結構
    zfree(list);
}

/*
 * 將一個包含有給定值指針 value 的新節點添加到鏈表的表頭
 *
 * 如果爲新節點分配內存出錯,那麼不執行任何動作,僅返回 NULL
 *
 * 如果執行成功,返回傳入的鏈表指針
 *
 * T = O(1)
 */
list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    // 爲節點分配內存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指針
    node->value = value;

    // 添加節點到空鏈表
    if (list->len == 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;
}

/*
 * 將一個包含有給定值指針 value 的新節點添加到鏈表的表尾
 *
 * 如果爲新節點分配內存出錯,那麼不執行任何動作,僅返回 NULL
 *
 * 如果執行成功,返回傳入的鏈表指針
 *
 * T = O(1)
 */
list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    // 爲新節點分配內存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指針
    node->value = value;

    // 目標鏈表爲空
    if (list->len == 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;
}

/*
 * 創建一個包含值 value 的新節點,並將它插入到 old_node 的之前或之後
 *
 * 如果 after 爲 0 ,將新節點插入到 old_node 之前。
 * 如果 after 爲 1 ,將新節點插入到 old_node 之後。
 *
 * T = O(1)
 */
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;
}

/*
 * 從鏈表 list 中刪除給定節點 node 
 * 
 * 對節點私有值(private value of the node)的釋放工作由調用者進行。
 *
 * T = O(1)
 */
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;

    // 釋放值
    if (list->free) list->free(node->value);

    // 釋放節點
    zfree(node);

    // 鏈表數減一
    list->len--;
}

/*
 * 爲給定鏈表創建一個迭代器,
 * 之後每次對這個迭代器調用 listNext 都返回被迭代到的鏈表節點
 *
 * direction 參數決定了迭代器的迭代方向:
 *  AL_START_HEAD :從表頭向表尾迭代
 *  AL_START_TAIL :從表尾想表頭迭代
 *
 * T = O(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;
}

/* Release the iterator memory */
/*
 * 釋放迭代器
 *
 * T = O(1)
 */
void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

/*
 * 返回迭代器當前所指向的節點。
 *
 * 刪除當前節點是允許的,但不能修改鏈表裏的其他節點。
 *
 * 函數要麼返回一個節點,要麼返回 NULL ,常見的用法是:
 *
 * iter = listGetIterator(list,<direction>);
 * while ((node = listNext(iter)) != NULL) {
 *     doSomethingWith(listNodeValue(node));
 * }
 *
 * T = O(1)
 */
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;
}

/* 
 * 查找鏈表 list 中值和 key 匹配的節點。
 * 
 * 對比操作由鏈表的 match 函數負責進行,
 * 如果沒有設置 match 函數,
 * 那麼直接通過對比值的指針來決定是否匹配。
 *
 * 如果匹配成功,那麼第一個匹配的節點會被返回。
 * 如果沒有匹配任何節點,那麼返回 NULL 。
 *
 * T = O(N)
 */
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;
}

/*
 * 返回鏈表在給定索引上的值。
 *
 * 索引以 0 爲起始,也可以是負數, -1 表示鏈表最後一個節點,諸如此類。
 *
 * 如果索引超出範圍(out of range),返回 NULL 。
 *
 * T = O(N)
 */
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;
}

還有一些宏定義

// 返回給定鏈表所包含的節點數量
// T = O(1)
#define listLength(l) ((l)->len)
// 返回給定鏈表的表頭節點
// T = O(1)
#define listFirst(l) ((l)->head)
// 返回給定鏈表的表尾節點
// T = O(1)
#define listLast(l) ((l)->tail)
// 返回給定節點的前置節點
// T = O(1)
#define listPrevNode(n) ((n)->prev)
// 返回給定節點的後置節點
// T = O(1)
#define listNextNode(n) ((n)->next)
// 返回給定節點的值
// T = O(1)
#define listNodeValue(n) ((n)->value)

// 將鏈表 l 的值複製函數設置爲 m
// T = O(1)
#define listSetDupMethod(l,m) ((l)->dup = (m))
// 將鏈表 l 的值釋放函數設置爲 m
// T = O(1)
#define listSetFreeMethod(l,m) ((l)->free = (m))
// 將鏈表的對比函數設置爲 m
// T = O(1)
#define listSetMatchMethod(l,m) ((l)->match = (m))

// 返回給定鏈表的值複製函數
// T = O(1)
#define listGetDupMethod(l) ((l)->dup)
// 返回給定鏈表的值釋放函數
// T = O(1)
#define listGetFree(l) ((l)->free)
// 返回給定鏈表的值對比函數
// T = O(1)
#define listGetMatchMethod(l) ((l)->match)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章