In the previous introduction to Redis, you can see that the list is used very often.
A linked list can act as a separate storage structure, such as a client's watch list that records all the keys that the client monitors, and the server's modal subscription list records all clients and its schema subscriptions.
A linked list can also be embedded into a dictionary as a value type in a dictionary, such as a database's monitoring dictionary that uses a linked list to store all clients that monitor a key, and the server's subscription dictionary uses a linked list to store all clients that subscribe to a channel. Linked list Structure node
The linked list in Redis is a doubly linked list, which means that each node holds its predecessor and successor nodes for increased operational efficiency. The nodes are defined as follows
Adlist.h
/* List node *
/typedef struct LISTNODE {
struct listnode *prev;/* Precursor node *
/struct ListNode *next; * Successor node */
void *value; /* value */
} ListNode;
Linked list
The linked list structure mainly records the table head node and the footer node, the number of nodes and some function pointers, defined as follows
Adlist.h/
* list */
typedef struct LIST {
listnode *head;/* Link Header node */
ListNode *tail;/* Link footer node */
void * (*dup) (void *ptr); /* Node value copy function */
void (*free) (void *ptr);/* Node value destructor */
Int (*match) (void *ptr, void *key);/* Node value matching function */
Unsi gned long Len; /* Linked list length */
} list;
function pointers are primarily operations on node values, including copying, destructors, and determining whether an equal iterator
In addition, Redis provides iterators for linked lists, primarily for the encapsulation of linked list nodes, as well as to easily move forward and backward through the predecessor and successor nodes of the linked list node.
Adlist.h/
* iterator */
typedef struct LISTITER {/
* points to the actual node */
ListNode *next;
/* Iterator direction, forward or backward */
int direction;
} Listiter;
The value of direction is two, forward and backward, as defined by a macro
Adlist.h
#define AL_START_HEAD 0/* From beginning to end (back)/
#define AL_START_TAIL 1/* from tail to head (forward) */
Linked list Operations
Create a linked list
The creation of a linked list is done by the Listcreate function, which is actually applying the list memory and initializing the member variable
ADLIST.C
/* Create an empty list *
/list *listcreate (void)
{
struct list *list;
/* Request memory for linked list *
/if (list = Zmalloc (sizeof (*list)) = = null)
return null;
/* Initialize */
List->head = List->tail = NULL;
List->len = 0;
List->dup = NULL;
List->free = NULL;
List->match = NULL;
return list;
}
Delete a linked list
Deleting a linked list is a little trickier than creating it, because the values saved in each node need to be freed, and that's exactly what the call to the free function does.
ADLIST.C
/* Release the memory space of the list *
/void Listrelease (list *list)
{
unsigned long len;
ListNode *current, *next;
Current = list->head;
Len = list->len;
/* Walk through the list and release each node */while
(len--) {/
* record the next node */
next = current->next;
/* If the node value destructor is defined, call */if
(list->free) list->free (current->value);
/* Release node Memory *
/Zfree (current);
current = next;
}
/* Because list* is also a dynamic application, you also need to release *
/Zfree (list);
}
Insert node at end
In the implementation of other modules, we often see the operation of adding a node to the tail of the linked list, and its implementation is done by Listaddnodetail. The function first requests the memory for the new node, and then adds the node to the linked list, where the list is empty according to whether the list is empty, the new node will be the head node and the tail node of the list, the precursor and subsequent pointers of the new node are empty, and the new node will be the tail node of the linked list. The successor pointer to the previous tail node points to the new node, and the new node's predecessor pointer points to the previous tail node
ADLIST.C
/* Add nodes at the end of the list *
/list *listaddnodetail (list *list, void *value)
{
ListNode *node;
/* Apply node */
if (node = zmalloc (sizeof (*node)) = = null)
return null;
/* Record node value */
node->value = value;
/* If the previously linked list is empty, insert a node with the rear end node as a new node *
/if (List->len = = 0) {
list->head = list->tail = node;
/* Set precursor successor */
Node->prev = Node->next = NULL;
} else {/
* not empty, only change tail node */
Node->prev = LIST-&G T;tail;
Node->next = NULL;
list->tail->next = node;
list->tail = node;
}
/* Number of nodes plus one */
list->len++;
return list;
}
iterator Movement
Iterators are mainly used to traverse the list, and the focus of the iterator is moving, through the direction variable, you can know the direction of the iterator movement, and through the predecessor node of the linked list node, can easily implement the mobile operation
The adlist.c
/* Moves the iterator while returning the next node *
/ListNode *listnext (listiter *iter)
{/
* Next pointer is the node pointer that the current iterator points to *
/ ListNode *current = iter->next;
if (current! = NULL) {/
* is assigned next according to direction *
/if (iter->direction = = al_start_head)
Iter->next = Current-> ; next;
else
Iter->next = current->prev;
}
/* Returns the node pointed to by the previous iterator */return current
;
}
resetting iterators
In addition, Redis provides the action to reset the iterator, which is done by the Listrewind and Listrewindtail functions, respectively
/* Reset iterator direction from start to end, so that the iterator points to the head node *
/void Listrewind (list *list, Listiter *li) {
Li->next = list->head;
Li->direction = Al_start_head;
}
/* Reset iterator direction from tail to end, make iterator point to tail node *
/void Listrewindtail (list *list, Listiter *li) {
Li->next = list->tail;
Li->direction = Al_start_tail;
}
Linked List Search
With the foundation of the iterator, you can implement the list search function, that is, to find a node in the list that matches a value, you need to iterate through the linked list
ADLIST.C
/* Lookup value key, return list node *
/ListNode *listsearchkey (list *list, void *key)
{
listiter iter;
ListNode *node;
/* Set the iterator direction to start from start to end, making it point to the list header node *
/Listrewind (list, &iter);
/* Iterate through the list *
/while ((node = Listnext (&iter)) = NULL) {/
* If a value matching function is provided, otherwise use = = Compare *
/if (List->match) {
if (List->match (Node->value, key)) {
return node;
}
} else {
if (key = = Node->value) {
return node;}}
}
return NULL;
}
macro definition Functions
In addition to the functions mentioned above, Redis also provides a number of macro-defined functions, such as returning node values, returning the predecessor nodes of a node, etc.
Adlist.h/* Returns the number of nodes in the list */#define LISTLENGTH (L)
((L)->len)/
* Return to head node */
#define LISTFIRST (L) ((L)- >head)/
* Return to tail node */#define LISTLAST (L)
((l)->tail)/
* Return to precursor node */
#define LISTPREVNODE (n) ((n)- >prev)
/* Returns the successor/*
#define LISTNEXTNODE (n) ((n)->next)/
* Returns the node value */
#define LISTNODEVALUE (N) ( (n)->value)//
Set the value of the linked list copy, value destruction, value matching function */
#define LISTSETDUPMETHOD (L,m) ((l)->dup = (m))
#define Listsetfreemethod (L,m) ((l)->free = (m))
#define Listsetmatchmethod (L,m) ((l)->match = (m))/
* Get value assignment, value destructor, value match function/#define LISTGETDUPMETHOD (L) (l)
->dup)
#define LISTGETFREE (L) ((l)->free)
#define LISTGETMATCHMETHOD (L) ((l)->match)
Summary
Because the structure of the list is simple, it is very easy to understand the implementation. Of course, there are a lot of functions related to the list in Redis, here are just a few of the common operations, interested in the deep source to see