We know that in the data structure, the biggest advantage of the list is the efficient implementation of dynamic increase, delete, change, the disadvantage is that the traversal is slow, so in Redis, a lot of the underlying implementation of functionality is based on the list, because Redis is based on the C language to write, so you can only implement their own linked list structure. For a regular doubly linked list node, we usually define it in the following way:
typedef struct Node{
void *value;
struct Node *prev;
struct Node *next;
}Node;
Redis, which is also defined in Adlist.h
Typedef struct listNode {
// Front node
Struct listNode *prev;
// post node
Struct listNode *next;
// the value of the node
Void *value;
} listNode;
The node definition above is the list of each element. In Redis, a doubly linked list is defined, and three operation functions (methods) are defined for the list:
Typedef struct list {
// header node
listNode *head;
// footer 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);
// The number of nodes included in the linked list
Unsigned long len;
} list;
Also, an iterator that defines a doubly linked list
Typedef struct listIter {
// the node to which the current iteration is
listNode *next;
// The direction of the iteration, from start to finish, from tail to head
Int direction;
} listIter;
Seeing the two-way node above, we might ask in Redis whether he uses a linked list with a ring or a chain-free list? Our later function, because to implement a linked list of rings only need to use the list of the head of the prev is not NULL, tail next is not NULL to implement.
In Adlist.h, a bunch of macros are defined to implement simple function functions.
// returns the number of nodes contained in the given list
#define listLength(l) ((l)->len)
/ / Returns the header node of the given list
#define listFirst(l) ((l)->head)
// returns the footer node of the given list
#define listLast(l) ((l)->tail)
// return the predecessor of the given node
#define listPrevNode(n) ((n)->prev)
// return the back node of the given node
#define listNextNode(n) ((n)->next)
// return the value of the given node
#define listNodeValue(n) ((n)->value)
// Set the value copy function of the linked list l to m
#define listSetDupMethod(l,m) ((l)->dup = (m))
/ / Set the value release function of the linked list l to m
#define listSetFreeMethod(l,m) ((l)->free = (m))
// Set the comparison function of the linked list to m
#define listSetMatchMethod(l,m) ((l)->match = (m))
// returns the value copy function for the given list
#define listGetDupMethod(l) ((l)->dup)
// returns the value release function for the given list
#define listGetFree(l) ((l)->free)
/ / Returns the value comparison function of the given list
#define listGetMatchMethod(l) ((l)->match)
The above defines a basic function (macro) that gets the general properties of a linked list, based on how we typically manipulate a data structure, which should be a method (function) for defining creation, initialization, addition, deletion, and so on.
/ / Create a new list
List *listCreate(void)
{
Struct list *list;
// Allocate memory
If ((list = zmalloc(sizeof(*list))) == NULL)
Return NULL;
// Initialize the property
List->head = list->tail = NULL;
List->len = 0;
List->dup = NULL;
List->free = NULL;
List->match = NULL;
Return list;
}
/ / Release the entire linked list and the nodes in the linked list
Void listRelease(list *list)
{
Unsigned long len;
listNode *current, *next;
// Point to the head pointer. I always feel that there is a problem with the writing here. Add list==NULL?
Current = list->head;
// traverse the entire list
Len = list->len;
While(len--) {
Next = current->next;
// call it if there is a set value release function
If (list->free) list->free(current->value);
/ / Release the node structure
Zfree(current);
Current = next;
}
/ / Release the linked list structure
Zfree(list);
}
/ / Create a node with a specified value to join the head of the list
List *listAddNodeHead(list *list, void *value)
{
listNode *node;
// allocate memory for the node
If ((node = zmalloc(sizeof(*node))) == NULL)
Return NULL;
// save the value pointer
Node->value = value;
// Add node to empty list
If (list->len == 0) {
List->head = list->tail = node;
Node->prev = node->next = NULL;
// Add a node to a non-empty list
} else {
Node->prev = NULL;
Node->next = list->head;
List->head->prev = node;
List->head = node;
}
/ / Update the number of linked list nodes
List->len++;
Return list;
}
/ / Create a node with a specified value, and add to the end of the list
List *listAddNodeTail(list *list, void *value)
{
listNode *node;
// allocate memory for the new node
If ((node = zmalloc(sizeof(*node))) == NULL)
Return NULL;
// save the value pointer
Node->value = value;
// The target list is empty
If (list->len == 0) {
List->head = list->tail = node;
Node->prev = node->next = NULL;
// The target list is not empty
} else {
Node->prev = list->tail;
Node->next = NULL;
List->tail->next = node;
List->tail = node;
}
/ / Update the number of linked list nodes
List->len++;
Return list;
}
/ / Create a node with the specified value, and according to the passed after parameter to determine whether to add to the specified node before or after
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;
// save the value
Node->value = value;
// Add a new node to the given node
If (after) {
Node->prev = old_node;
Node->next = old_node->next;
// The given node is the original footer node
If (list->tail == old_node) {
List->tail = node;
}
// Add a new node before the given node
} else {
Node->next = old_node;
Node->prev = old_node->prev;
// The given node is the original header node
If (list->head == old_node) {
List->head = node;
}
}
// Update the front pointer of the new node
If (node->prev != NULL) {
Node->prev->next = node;
}
// Update the post pointer of the new node
If (node->next != NULL) {
Node->next->prev = node;
}
/ / Update the number of linked list nodes
List->len++;
Return list;
}
/ / Remove the specified node from the list
Void listDelNode(list *list, listNode *node)
{
/ / Adjust the pointer of the front node, I am particularly afraid of this writing when writing programs with java, because when you encounter a null pointer, here will be an error, so the following node should first determine whether it is air
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 the node
Zfree(node);
// Reduce the number of linked lists by one
List->len--;
}
/ / Create an iterator for a specific list
listIter *listGetIterator(list *list, int direction)
{
// allocate memory for the iterator
listIter *iter;
If ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
/ / Set the start node of the iterator according to the iteration direction
If (direction == AL_START_HEAD) / / here AL_START_HEAD==0
Iter->next = list->head;
Else
Iter->next = list->tail;
/ / Record the iteration direction
Iter->direction = direction;
Return iter;
}
/ / Get the current node of the iterator, because the returned are pointers, so it can be changed
listNode *listNext(listIter *iter)
{
listNode *current = iter->next;
If (current != NULL) {
/ / Select the next node according to the direction
If (iter->direction == AL_START_HEAD)
// Save the next node to prevent the current node from being deleted and causing the pointer to be lost
Iter->next = current->next;
Else
// Save the next node to prevent the current node from being deleted and causing the pointer to be lost
Iter->next = current->prev;
}
Return current;
}
/ / Release the iterator
Void listReleaseIterator(listIter *iter) {
Zfree(iter);
}
/ / Copy the entire list, it should be noted that if the dup attribute of the original list is empty, then the two lists will share the pointer of the node, so be careful
List *listDup(list *orig)
{
List *copy;
listIter *iter;
listNode *node;
// Create a new linked list
If ((copy = listCreate()) == NULL)
Return NULL;
/ / Set the node value handler
Copy->dup = orig->dup;
Copy->free = orig->free;
Copy->match = orig->match;
/ / Iterate the entire input list
Iter = listGetIterator(orig, AL_START_HEAD);
While((node = listNext(iter)) != NULL) {
Void *value;
/ / Copy the node value to the new node
If (copy->dup) {
Value = copy->dup(node->value);
If (value == NULL) {
listRelease(copy);
listReleaseIterator(iter);
Return NULL;
}
} else
Value = node->value;
// will Node added to the linked list
If (listAddNodeTail(copy, value) == NULL) {
listRelease(copy);
listReleaseIterator(iter);
Return NULL;
}
}
// release the iterator
listReleaseIterator(iter);
// return a copy
Return copy;
}
/ / Look for the first occurrence of the specified key node in the list, it should be noted that if the match function of the node in the list is empty, then directly compare the value of the pointer, find the first one to return, find no Return to NULL
listNode *listSearchKey(list *list, void *key)
{
listIter *iter;
listNode *node;
/ / Iterate the entire linked list
Iter = listGetIterator(list, AL_START_HEAD);
While((node = listNext(iter)) != NULL) {
// Compared
If (list->match) {
If (list->match(node->value, key)) {
listReleaseIterator(iter);
// turn up
Return node;
}
} else {
If (key == node->value) {
listReleaseIterator(iter);
// turn up
Return node;
}
}
}
listReleaseIterator(iter);
// Not found
Return NULL;
}
/ / Find the node of the specified index in the list, the node can be a positive number, or a negative number, when it is a negative number, -1 stands for the tail of the list, and so on, if not negative, then the index starts from 0
listNode *listIndex(list *list, long index) {
listNode *n;
// If the index is negative, look up from the end of the table
If (index < 0) {
Index = (-index)-1;
n = list->tail;
While(index-- && n) n = n->prev;
// If the index is positive, look up from the header
} else {
n = list->head;
While(index-- && n) n = n->next;
}
Return n;
}
The next two functions, according to the name, can be seen, back, what do you mean? Is the iterator of list
/ / Set to head->tail
Void listRewind(list *list, listIter *li) {
Li->next = list->head;
Li->direction = AL_START_HEAD;
}
/ / Set to tail->head
Void listRewindTail(list *list, listIter *li) {
Li->next = list->tail;
Li->direction = AL_START_TAIL;
}
The next function, which always finds that the name does not match the content of his implementation, is to make the tail of the linked list into the head of the list.
Void listRotate(list *list) {
listNode *tail = list->tail;
If (listLength(list) <= 1) return;
/* Detach current tail */
/ / Remove the tail node
List->tail = tail->prev;
List->tail->next = NULL;
/* Move it as head */
// insert into the header
List->head->prev = tail;
Tail->prev = NULL;
Tail->next = list->head;
List->head = tail;
}
Okay, so the Redis list is basically like this.
Redis Research-linked list of 3.1 data structures