Analysis of Redis source code -- implementation of adlist

Keywords: Redis

adlist

Adlist is one of the basic data structures of Redis. It is a two-way linked list and records the length of the linked list. The iterator of adlist records the iteration node and direction. I think the implementation of list is better than STL

Several important structures

The implementation of adlist is relatively simple. Basically, you can write all the implementation functions quickly after writing the code related to the linked list

/*
 * Double end linked list node
 */
typedef struct listNode {
    // Front node
    struct listNode *prev;
    // Post node
    struct listNode *next;
    // Node value
    void *value;
} listNode;

/*
 * Double ended list iterator
 */
typedef struct listIter {
    // Current iterated node
    listNode *next;
    // Direction of iteration
    int direction;
} listIter;

// Iteration from header to tail
#define AL_START_HEAD 0
// Iterating from tail to header
#define AL_START_TAIL 1

/*
 * Double ended list structure
 */
typedef struct list {
    // header node
    listNode *head;
    // Tail node
    listNode *tail;
    // Node value copy function
    void *(*dup)(void *ptr);
    // Node value release function
    void (*free)(void *ptr);
    // Node value comparison function
    int (*match)(void *ptr, void *key);
    // Number of nodes in the list
    unsigned long len;
} list;

Overview of basic functions

function function Complexity
listCreate Create linked list O(1)
listRelease Free, reclaim space O(N)
listInsertNode Insertion node O(1)
listDelNode Delete node O(1)
listSearchKey Search node O(N)
listGetIterator Get iterator O(1)
... ... ...

Other macro / function implementations are simple. No introduction

listCreate

// Create an empty list without nodes
list *listCreate(void) {
    struct list *list;

    // Allocated memory
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;

    // Initialize properties
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;

    return list;
}

listRelease

// Release all nodes and then release List
void listRelease(list *list) {
    unsigned long len;
    listNode *current, *next;

    // Pointer to head
    current = list->head;
    // Traverse the entire list
    len = list->len;
    while(len--) {
        next = current->next;

        // If there is a set value release function, call it
        if (list->free) list->free(current->value);

        // Release node structure
        zfree(current);

        current = next;
    }

    // Release list structure
    zfree(list);
}

listInsertNode

// Insert a new node before / after the given node
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;

    // Create a new node
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // Saved value
    node->value = value;

    // After adding a new node to the given node
    if (after) {
        node->prev = old_node;
        node->next = old_node->next;
        // Given node is the original table tail node
        if (list->tail == old_node) {
            list->tail = node;
        }
    // Before adding a new node to a given node
    } else {
        node->next = old_node;
        node->prev = old_node->prev;
        // Given node is the original header node
        if (list->head == old_node) {
            list->head = node;
        }
    }

    // Update new node's leading pointer
    if (node->prev != NULL) {
        node->prev->next = node;
    }
    // Update post pointer for new node
    if (node->next != NULL) {
        node->next->prev = node;
    }

    // Update the number of linked list nodes
    list->len++;

    return list;
}

listDelNode

// Delete node
void listDelNode(list *list, listNode *node) {
    // Adjust the pointer of the front node
    if (node->prev)
        node->prev->next = node->next;
    else
        list->head = node->next;

    // Adjust the pointer of the post node
    if (node->next)
        node->next->prev = node->prev;
    else
        list->tail = node->prev;

    // Release value
    if (list->free) list->free(node->value);

    // Release node
    zfree(node);

    // Number of linked lists minus one
    list->len--;
}

listSearchKey

// Find the node according to the given Key value, and return the first
listNode *listSearchKey(list *list, void *key) {
    listIter *iter;
    listNode *node;

    // Iterate the entire list
    iter = listGetIterator(list, AL_START_HEAD);
    while((node = listNext(iter)) != NULL) {

        // Contrast
        if (list->match) {
            if (list->match(node->value, key)) {
                listReleaseIterator(iter);
                // find
                return node;
            }
        } else {
            if (key == node->value) {
                listReleaseIterator(iter);
                // find
                return node;
            }
        }
    }

    listReleaseIterator(iter);

    // not found
    return NULL;
}

listGetIterator

// Get iterator by direction
listIter *listGetIterator(list *list, int direction) {
    // Allocate memory for iterators
    listIter *iter;
    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;

    // Set the starting node of the iterator according to the iteration direction
    if (direction == AL_START_HEAD)
        iter->next = list->head;
    else
        iter->next = list->tail;

    // Record iteration direction
    iter->direction = direction;

    return iter;
}

Summary

The implementation of adlist is relatively simple. Compared with STL's list, the complexity is much smaller, but the basic functions are all available. STL provides functions such as merging and sorting. Adlist records the length of the linked list, which can be O(1) to get the length

Posted by regexpert on Sat, 04 Apr 2020 09:06:09 -0700