Skip to content

Instantly share code, notes, and snippets.

@charsyam
Created July 17, 2013 10:59
Show Gist options
  • Save charsyam/6019587 to your computer and use it in GitHub Desktop.
Save charsyam/6019587 to your computer and use it in GitHub Desktop.
redis protocol parsing code and test.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#define CR '\r'
#define LF '\n'
class message {
public:
int state;
char *pos;
int narg;
int ret;
int rlen;
int rexpectedlen;
int needsplit;
};
void parse(char *buffer, int buf_size, message *msg) {
enum {
START,
NARG,
NARG_LF,
ARGN_LEN_MARK,
ARGN_LEN,
ARGN_LEN_LF,
ARGN,
ARGN_LF,
};
int state = msg->state;
char *pos = msg->pos;
char *end = buffer + buf_size;
char ch;
int len;
for ( ;pos < end; pos++) {
ch = *pos;
switch(state) {
case START:
if (ch != '*') {
goto error;
}
state = NARG;
msg->narg = 0;
msg->rlen = 0;
msg->rexpectedlen = 0;
break;
case NARG:
if (isdigit(ch)) {
msg->narg = msg->narg * 10 + (uint32_t)(ch - '0');
} else if (ch == CR) {
if (msg->narg == 0) {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
state = NARG_LF;
} else {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
break;
case NARG_LF:
if (ch != LF) {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
state = ARGN_LEN_MARK;
break;
case ARGN_LEN_MARK:
if (ch == '$') {
state = ARGN_LEN;
msg->rlen = 0;
} else {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
break;
case ARGN_LEN:
if (isdigit(ch)) {
msg->rlen = msg->rlen * 10 + (uint32_t)(ch - '0');
} else if (ch == CR) {
state = ARGN_LEN_LF;
} else {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
break;
case ARGN_LEN_LF:
if (ch != LF) {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
state = ARGN;
break;
case ARGN:
len = msg->rexpectedlen ? msg->rexpectedlen : msg->rlen;
if (pos + len > end) {
msg->rexpectedlen = (uint32_t)(pos + len - end);
pos = end - 1;
} else {
if (*(pos + len) != CR) {
printf("ERROR: %d(%d:%s)\n", __LINE__, len, pos);
goto error;
}
pos += len;
msg->rexpectedlen = 0;
msg->rlen = 0;
state = ARGN_LF;
msg->narg--;
}
break;
case ARGN_LF:
if (ch != LF) {
printf("ERROR: %d(%s)\n", __LINE__, pos);
goto error;
}
if (msg->narg == 0) {
goto done;
}
state = ARGN_LEN_MARK;
break;
}
}
msg->state = state;
msg->pos = pos;
//need next packet
msg->ret = 3;
return;
error:
msg->ret = -1;
return;
done:
msg->state = START;
msg->pos = pos + 1;
msg->ret = 0;
if (msg->pos != end) {
msg->needsplit = 1;
}
return;
}
int main(int argc, char *argv[]) {
message msg;
memset(&msg, 0, sizeof(message));
char *ptr = (char *)"*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
msg.pos = ptr;
parse(ptr, strlen(ptr), &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
memset(&msg, 0, sizeof(message));
ptr = (char *)"*3\r\n$3\r\nset\r\n$3";
msg.pos = ptr;
parse(ptr, strlen(ptr), &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
ptr = (char *)"\r\nkey\r\n$5\r\nvalue\r\n";
msg.pos = ptr;
parse(ptr, strlen(ptr), &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
memset(&msg, 0, sizeof(message));
ptr = (char *)"*3\r\n$3\r\nset\r\n$3";
msg.pos = ptr;
parse(ptr, strlen(ptr), &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
ptr = (char *)"\r\nkey\r\n$5\r\nvalue\r\n$ab";
msg.pos = ptr;
parse(ptr, strlen(ptr), &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
parse(msg.pos, 2, &msg);
printf("ret: %d(%d)\n", msg.ret, msg.needsplit);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment