redis source (a): add your own list of type redis

This document is divided into three parts:

Introduction and demonstration of environmental effects

redis reception command execution logic to return data

Code

Important and difficult in the third part of the document, read this document fully the needs of readers have basic knowledge of c language and data structures.

Environmental effects of the introduction and demonstration of
environmental introduce
redis version 5.0.3 64 bit
operating system version is Ubuntu 18.10 64bit
source code can be viewed gdb debugging gedit
IDE can use eclipse + CDT

It demonstrates the effect of
the present case implements a linked list, list data corresponding to the redis type, the operation of the linked list to achieve the insertion set value, a new node is a node, the node acquires a certain range for a list of length, etc. The following table lists command specific implementation of the corresponding native commands redis

Redis native command to achieve the commanded command meaning
myrpush rpush inserted from the tail of the linked list
mylrange lrange acquires the range values
myrpop rpop values are popped from the right
llen length list acquired myllen
mylset LSET provided a node value
myinsert linsert inserted at the specified position value
acquiring specified position mylindex lindex value
realization command demonstrates:

127.0.0.1:6379> myrpush mygjw gjw0(integer)
1127.0.0.1:6379> myrpush mygjw gjw1
(integer) 2
127.0.0.1:6379> myrpush mygjw gjw2
(integer) 3
127.0.0.1:6379> mylrange 0 -1
(error) ERR wrong number of arguments for 'mylrange' command
127.0.0.1:6379> mylrange mygjw 0 -1
1) "gjw0"
2) "gjw1"
3) "gjw2"
127.0.0.1:6379> myrpop mygjw
"gjw2"
127.0.0.1:6379> mylrange mygjw 0 -1
1) "gjw0"
2) "gjw1"
127.0.0.1:6379> myllen mygjw
(integer) 2
127.0.0.1:6379> mylset mygjw 0 gjw00
OK127.0.0.1:6379> mylset mygjw 1 gjw01
OK127.0.0.1:6379> mylrange mygjw 0 -1
1) "gjw00"
2) "gjw01"
127.0.0.1:6379> 0 mylinsert mygjw gjw0
(Integer). 3
127.0.0.1:6379> mylinsert mygjw. 1 gjw1
(Integer). 4
127.0.0.1:6379> mylrange mygjw 0 -1
. 1) "gjw0"
2) "gjw1"
. 3 ) "gjw00"
. 4) "gjw01"
127.0.0.1:6379> mylindex mygjw 0
"gjw0"
127.0.0.1:6379> mylindex mygjw. 1
"gjw1"
Redis reception command execution logic to return data to
this section of this document is relevant only choice section do a brief introduction, the actual command related to saving files, master and slave synchronization, cluster distribution, etc. will be much more complicated. Let's start from processCommand function server.c file.

640?wx_fmt=png

 

Below timing diagram myrpush command to the documentation for the specific function and where the calls, other command execution methods are similar. Figure Chinese string is the file name box, type the command list all about handling functions are t_list in the file. c, other types of command processing function needs to look for the corresponding source file, Redis command is standardized, it is not difficult to find.

640?wx_fmt=png

 

Source code for reading and redis

Code implements
1. Add own source and header files
mylinkedlist.h, mylinkedlist.h
mylist type bidirectional linked list data structure, the data field using a dumb pointer facilitate insertion of any type of data, the code for convenience only achieved char type, node structure has a size attribute data field for storing the number of bytes occupied.

This section is much simpler to implement than redis source, but if you can read this code, it should be relatively easy to understand redis source. redis the list type data field is ziplist, 8k byte data field default maximum case, ziplist has its own data structure, where the list of lists relevant source file redis type of processing: quicklist.h, quicklist.c, ziplist.h, ziplist.c
header file as follows:

/*
* mylineklist.h
*
* Created on: Dec 29, 2018
* Author: gjw
*/typedef void * ADT;
typedef const void * CADT;
typedef int (*LIDESTROY) (ADT e);
typedef void (*LITRVAVEL) (ADT e);
struct NODE;
typedef struct NODE * PNODE;
typedef struct MylinkedListS MylinkedList;
typedef MylinkedList * MYLINKEDLIST;
MYLINKEDLIST MyCreateList();
void MyAppend(MYLINKEDLIST list,ADT data,int len);
int MyInsert(MYLINKEDLIST list,ADT data,unsigned int pos,int len);
void MyDelete(MYLINKEDLIST list,unsigned int pos,LIDESTROY destroy);
ADT Myrpop(MYLINKEDLIST list,int * sz);
int Mylset(MYLINKEDLIST list, ADT data, unsigned int pos,int len);
void MyClear (MYLINKEDLIST List, LIDESTROY the destroy);
int MyLength (MYLINKEDLIST List);
pNode MyIndex (MYLINKEDLIST List, unsigned int index);
void MyTraverse (MYLINKEDLIST List, LITRVAVEL litravel);
int MyIsEmpty (MYLINKEDLIST List);
pNode MyNext (pNode Node );
void the getData (n-pNode, char ** Data, SZ int *);
in this case not all of the statements have been achieved functions only implements several commands demonstrated above, the source file as follows:

/*
* mylinkedlist.c
*
* Created on: Dec 29, 2018
* Author: gjw
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mylinkedlist.h"
struct NODE {
ADT data;
int size;
PNODE next;
PNODE previous;
} ;
typedef struct NODE NODE;
struct MylinkedListS {
unsigned int len;
PNODE head, tail;
};

MylinkedList* MyCreateList() {
MylinkedList* list = (MylinkedList*) malloc(sizeof(MylinkedList));
PNODE head = (NODE *) malloc(sizeof(NODE));
PNODE tail = (NODE *) malloc(sizeof(NODE));
list->head = head;
list->tail = tail;
list->tail->next=NULL;
list->tail->previous=NULL;
list->head->next=NULL;
list->head->previous=NULL;
list->len=0;
printf("init a list");
return list;
}
void MyAppend(MYLINKEDLIST list, ADT data,int len) {
if (list->head->next == NULL) {
MyInsert(list, data, 0,len);
return;
}
printf("->");
PNODE ctail = (NODE *) malloc(sizeof(NODE));
ctail->data=(char *)malloc(len);
memcpy(ctail->data,data,len);
ctail->size=len;
ctail->next=NULL;
ctail->previous=list->tail->next;
list->tail->next->next = ctail;
list->tail->next = ctail;
list->len = list->len + 1;
}
int MyInsert(MYLINKEDLIST list, ADT data, unsigned int pos,int len) {
if ( pos > list->len) return 0;
printf("insert success!\n");
PNODE node = list->head;
for (unsigned int i = 0; i < pos; i++) {
node = node->next;
}
PNODE newNode = (NODE *) malloc(sizeof(NODE));
newNode->data=(char *)malloc(len);
memcpy(newNode->data,data,len);
newNode->size=len;
newNode->next = node->next;
newNode->previous=node; if(node->next){
newNode->next->previous=newNode;
}
node->next = newNode;
list->tail->next=newNode;
list->len = list->len + 1;
return 1;
}

ADT Myrpop(MYLINKEDLIST list,int * sz){
PNODE tail=list->tail->next;
if(list->len==0){
return NULL;
}
ADT data=tail->data;
*sz=tail->size; list->tail->next=tail->previous;
tail->previous->next=NULL;
list->len=list->len-1;
free(tail);
return data;
}int MyLength(MYLINKEDLIST list) {
return list->len;
}
int MyIsEmpty(MYLINKEDLIST list) {
return !list->len;
}
void getData(PNODE n,char ** data,int * sz) {
*data=n->data;
*sz=n->size;
}

PNODE MyNext(PNODE node) {
return node->next;
}
PNODE MyIndex(MYLINKEDLIST list,unsigned int index){
= N-list- pNode> head-> Next;
IF (index> = list-> len) {
return NULL;
}
for (int I = 0; I <index; I ++) {
n-= N-> Next;
}
return n- ;
}
int Mylset (MYLINKEDLIST List, the ADT Data, POS unsigned int, int len) {
pNode Node = list-> head-> Next;
IF (list-> len == 0) {
MyInsert (List, Data, POS, len );
return. 1;
}
IF (POS> = list-> len) {
return 0;
} for (int I = 0; I <POS; I ++) {
Node = node-> Next;
}
realloc (node-> Data, len);
node-> size = len;
the memcpy (node-> Data, Data, len);
return. 1;
}
2. modify server.h mainly own type declaration, the type of command to add custom handler added from definition header file, add the following:

server.h
#include "mylinkedlist.h" / * mylist, guojiagnwei the Add * /
#define MYOBJ_LIST. 11 / mylist Object * * /.
void myrpushCommand (Client * C);
void mylrangeCommand (Client * C);
void myrpopCommand (Client * C);
void myllenCommand (Client * C);
void mylsetCommand (Client * C);
void mylinsertCommand (Client * C);
void mylindexCommand (Client * C);
robj * createMylistObject (void);
3. modify this file object.c primarily used to operate redis object, add the following

* createMylistObject robj (void) {
MylinkedList MyCreateList * L = (); // * QuickList quicklistCreate L = ();
robj of createObject = O * (MYOBJ_LIST, L);
O-> encoding = OBJ_ENCODING_QUICKLIST;
return O;
}
4. Modify t_list.c files, declared in order to achieve server.h handler

//added by guojiangweivoid myrpushCommand(client *c){
int j, pushed = 0;
robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
for (j = 2; j < c->argc; j++) {
if (!lobj) {
lobj = createMylistObject();
dbAdd(c->db,c->argv[1],lobj);
}
MyAppend(lobj->ptr,c->argv[j]->ptr,sdslen(c->argv[j]->ptr));
pushed++;
}
addReplyLongLong(c, MyLength(lobj->ptr)); if (pushed) {
char *event = "rpush";
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(1,event,c->argv[1],c->db->id);
}
server.dirty += pushed;
}
void mylrangeCommand(client *c){
robj *o;
long start, end, llen,rangelen;
char * data;
int sz;
if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
(getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,MYOBJ_LIST)) return;
llen = MyLength(o->ptr); /* convert negative indexes */
if (start < 0) start = llen+start;
if (end < 0) end = llen+end;
if (start < 0) start = 0;
/* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) {
addReply(c,shared.emptymultibulk);
return;
} if (end >= llen) end = llen-1;
rangelen = (end-start)+1; /* Return the result in form of a multi-bulk reply */
addReplyMultiBulkLen(c,rangelen);
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
PNODE n = MyIndex(o->ptr, start);
while(rangelen--) {
getData( n,&data,&sz);
addReplyBulkCBuffer(c,data,sz);
n=MyNext(n);
}
} else {
serverPanic("List encoding is not QUICKLIST!");
}

}
void myrpopCommand(client *c){
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);
int sz;
if (o == NULL || checkType(c,o,MYOBJ_LIST)) return;
char * data=Myrpop(o->ptr,&sz);
robj *value = createStringObject(data,sz);
free(data); if (value == NULL) {
addReply(c,shared.nullbulk);
} else {
char *event = "rpop";
addReplyBulk(c,value);
decrRefCount(value);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
if (listTypeLength(o) == 0) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del", c->argv[1],c->db->id);
dbDelete(c->db,c->argv[1]);
} signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
}
void myllenCommand(client *c){
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,MYOBJ_LIST)) return;
addReplyLongLong(c,MyLength(o->ptr));
}
void mylsetCommand(client *c){
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
if (o == NULL || checkType(c,o,MYOBJ_LIST)) return;
long index;
robj *value = c->argv[3];
if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))
return;
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
MYLINKEDLIST ql = o->ptr;
int replaced = Mylset(ql,value->ptr, index,sdslen(value->ptr));
if (!replaced) {
addReply(c,shared.outofrangeerr);
} else {
addReply(c,shared.ok);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id);
server.dirty++;
}
} else {
serverPanic("Unknown list encoding");
}

}
void mylinsertCommand(client *c){
long index;
robj *subject;
int inserted = 0;
if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)){
addReply(c,shared.syntaxerr);
return;
}
if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,subject,MYOBJ_LIST)) return;
inserted=MyInsert(subject->ptr,c->argv[3]->ptr,index,sdslen(c->argv[3]->ptr));
if (inserted) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"linsert", c->argv[1],c->db->id);
server.dirty++;
} else { /* Notify client of a failed insert */
addReply(c,shared.cnegone);
return;
} addReplyLongLong(c,MyLength(subject->ptr));
}
void mylindexCommand(client *c){
long index;
int size;
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
if (o == NULL || checkType(c,o,MYOBJ_LIST)) return;
if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))
return;
PNODE n=MyIndex(o->ptr,index);
if(n==NULL){
addReply(c,shared.nullbulk);
return;
}

char *data;
getData( n,&data,&size);
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
if (data) {
* = value createStringObject robj (Data, size);
addReplyBulk (C, value);
decrRefCount (value);
} the else {
addReply (C, shared.nullbulk);
}
} the else {
serverPanic ( "Unknown encoding List");
}
}
5. modify server.c document, the variable struct redisCommand redisCommandTable [] add custom type command string and command processing function mapping

{ "myrpush", myrpushCommand, -3, "WMF", 0, NULL, 1,1,1,0,0},
{ "mylrange", mylrangeCommand,. 4, "WMF", 0, NULL, 1,1, } 1,0,0,
{ "myrpop", myrpopCommand, 2, "WMF", 0, NULL, 1,1,1,0,0},
{ "myllen", myllenCommand, 2, "WMF", 0, NULL, 1,1,1,0,0},
{ "mylset", mylsetCommand,. 4, "WMF", 0, NULL, 1,1,1,0,0},
{ "mylinsert", mylinsertCommand,. 4, "WMF", 0, NULL, 1,1,1,0,0},
{ "mylindex", mylindexCommand,. 3, "R & lt", 0, NULL, 1,1,1,0,0},
also need under make editing files, here is simple not listed separately.

Read redis source
code that implements section lists the contents of the file to edit and modify, redis source design is very good, very standardized naming, if the reader has been operated in accordance with the documents listed on redis source structure and program logic should have a general understanding, the author had intended to write redis list in detail the type of source to achieve, but thought, limited, difficult to do serious and popular, recommended we look at "redis design and implementation" this book. Personally I feel that the most important is to look at the source code and modify their own ongoing debug.

Guess you like

Origin www.cnblogs.com/hyhy904/p/10992435.html