General linked list structure in redis

Source: Internet
Author: User

Redis is an ongoing open-source key-value database project, and the author is antirez. Compared with general key-value databases, redis has the support for linked lists, sets, sortsets, and other composite structures for values in key-value pairs. Here we will look at how antirez implements the universal linked list structure.

If you are familiar with Linux, you should know that the Linux kernel also implements a general linked list structure, which uses the features in GCC that go beyond the ansi c Specification. But redis does not do this, but uses C ++ STL-like practices. Redis implements a two-way non-circular linked list, involving three data structures: node listnode, iterator listiter, and list. The Code is as follows.

 typedef struct listNode {
void *value;
struct listNode *prev;
struct listNode *next;
}listNode;

The value points to the specific data. From the next two pointers, we can see that this is a two-way linked list. We need to judge whether the loop is based on other codes.

 

 typedef struct listIter {
struct listNode *next;
int direction;
}listIter;

This is an iterator. Of course, a pointer is required to point to the node, that is, next; direction indicates the forward direction of the iterator, al_start_head indicates the start from the beginning, and al_start_tail indicates the start from the end.

Okay. It's time to create the linked list structure.

Adlist. h

 typedef struct list {
listNode *head;
listNode *tail;

void* (*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void*key);

unsigned int len;
}list;

Head, tail, and Len are easy to understand. What are the three variables in the middle? They are function variables and DUP are replication functions. You can define the replication policy by yourself. Free is the release policy, match is a method for querying content in a linked list.

The three data structures related to the linked list are shown below. For more information about the APIS, see adlist. h.

 

List * listcreate (void); no doubt this is the constructor of the linked list.

Adlist. c

 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;
}

There is nothing to say. dynamically allocate a list structure from the heap, initialize the member variables, and then return the list pointer, so that we can control the linked list in the heap. Note that zmalloc is a memory allocation function encapsulated by redis. If you are interested, you can view the zmalloc. c file.

If you have a constructor, you will naturally have the destructor, as shown below:
Void listrelease (list * List );

 void listRelease(list *list)
{
unsigned int 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);
}

Zfree is a redis-encapsulated function. Here we should note that list-> free is used to release data in nodes, note: If the value in your listnode is directed to the memory in the heap, you must define the release function for the linked list; otherwise, memory leakage will occur.

 

 

Now let's take a look at how to add nodes to the linked list. redis only provides methods to add nodes to the head or tail of the linked list. Maybe these methods are enough for redis.
List * listaddnodehead (list * List, void * value );

Adlist. c

 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;
}

We only need to provide real data values for the linked list. This function will create its own listnode structure. The declaration of this function is a bit interesting. It returns the list pointer. We usually return an int, and 0 indicates success, and-1 indicates failure. In redis, if the chain table fails to be inserted, null is returned, and the original chain table is not affected. If the table is inserted successfully, the changed chain table is returned. Similar to listaddnodetail.

 

If there is insert, there will be deletion. during insertion, We only provide the value in listnode, but the address of listnode will be provided for deletion. Like listrelease, if the value points to an internal heap, you must provide your own defined free function. Otherwise, the memory will leak.
Void listdelnode (list * List, listnode * node );

Adlist. c

Why does this function return the list pointer like inserting a function? In the source code, this function cannot fail, so there is no need to return the value. However, I think the returned list pointer is also good, and the interface consistency can be maintained. I don't know what antirez thinks. Is performance considered.

 

 

Redis accesses nodes through the iterator. Like STL, there must be a function for generating the iterator for the linked list.
Listiter * listgetiter (list * List, int direction );

Adlist. c

 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;
}

 

 

 

The iterator is not a part of the linked list, but helps the programmer to operate the linked list node. Therefore, it needs a separate destructor.
Void listreleaseiterator (listiter * ITER );

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

 

 

 

Now we need to access the linked list node through the iterator. This function may be the most important to understand the linked list. It will return the node currently referred to by the iterator and forward or backward according to the direction.
Listnode * listnext (listiter * ITER); adlist. c

For direction = al_start_head, when the iterator points to the last node, if you call this function again, it returns NULL. Therefore, the frame of our traversal table is as follows:

Iter = listgetiterator (list );
While (node = listnext (ITER ))! = NULL ){
Dosomethingwith (listnodevalue (node); // listnodevalue is a macro in adlist. H, which extracts the value from the node.
}
I think this framework is a bit awkward. It is not the same as STL. I will discuss the design of this interface in my summary.

 

 

 

 

Summary:

 

The basic API of the general linked list structure has been described. You 'd better read the redis source code for a thorough understanding. However, from listdelnode, I have sniffed a trace of discord. listdelnode requires us to pass a listnode pointer. However, in a good general linked list, there should be only three data structures in the user's concept: linked List, iterator, and type of values related to specific applications. In the user's concept, value should be the node in the linked list, and there is no need to know that value is actually a pointer in listnode. Therefore, listnode should not appear in the user interface at all. Based on this idea, we can change two interfaces:
Void listdelnode (listnode * node) should be changed to void listdelnode (listiter * ITER)
The listnode * listnext (listiter * ITER) should be changed to listiter * listnext (listiter * ITER)
In this way, the traversal framework becomes
Iter = listgetiter (list, direction );
While (! Listiternull (ITER )){
Dosomethingwith (listitervalue (ITER ));
Listnext (ITER );
}
Check whether the traversal framework is pleasing to the eye. We also need to implement listiternull and listitervalue in the framework, which are both very simple.
# Define listiternull (ITER) (ITER-> next)
# Define listitervalue (ITER) (ITER-> next-> value)
 
Okay, come to an end. Do you understand some general data structure design ideas?
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.