Commit e621f313 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Expose API for streaming bytes to a reply

parent 4ec97f59
...@@ -38,14 +38,12 @@ ...@@ -38,14 +38,12 @@
#include "sds.h" #include "sds.h"
typedef struct redisReader { typedef struct redisReader {
char *buf; /* read buffer */ sds buf; /* read buffer */
int len; /* buffer length */ unsigned int pos; /* buffer cursor */
int avail; /* available bytes for consumption */
int pos; /* buffer cursor */
redisReply **rlist; /* list of items to process */ redisReply **rlist; /* list of items to process */
int rlen; /* list length */ unsigned int rlen; /* list length */
int rpos; /* list cursor */ unsigned int rpos; /* list cursor */
} redisReader; } redisReader;
static redisReply *redisReadReply(int fd); static redisReply *redisReadReply(int fd);
...@@ -105,9 +103,9 @@ static redisReply *redisIOError(void) { ...@@ -105,9 +103,9 @@ static redisReply *redisIOError(void) {
return createReplyObject(REDIS_REPLY_ERROR,sdsnew("I/O error")); return createReplyObject(REDIS_REPLY_ERROR,sdsnew("I/O error"));
} }
static char *readBytes(redisReader *r, int bytes) { static char *readBytes(redisReader *r, unsigned int bytes) {
char *p; char *p;
if (r->len-r->pos >= bytes) { if (sdslen(r->buf)-r->pos >= bytes) {
p = r->buf+r->pos; p = r->buf+r->pos;
r->pos += bytes; r->pos += bytes;
return p; return p;
...@@ -274,56 +272,96 @@ static int processItem(redisReader *r) { ...@@ -274,56 +272,96 @@ static int processItem(redisReader *r) {
#define READ_BUFFER_SIZE 2048 #define READ_BUFFER_SIZE 2048
static redisReply *redisReadReply(int fd) { static redisReply *redisReadReply(int fd) {
void *reader = redisCreateReplyReader();
redisReply *reply; redisReply *reply;
redisReader r; char buf[1024];
int bytes; int nread;
/* setup read buffer */ do {
r.buf = malloc(READ_BUFFER_SIZE+1); if ((nread = read(fd,buf,sizeof(buf))) <= 0) {
r.len = READ_BUFFER_SIZE; reply = redisIOError();
r.avail = 0; break;
r.pos = 0; } else {
reply = redisFeedReplyReader(reader,buf,nread);
/* setup list of items to process */
r.rlist = malloc(sizeof(redisReply*));
r.rlist[0] = createReplyObject(-1,NULL);
r.rlen = 1;
r.rpos = 0;
while (r.rpos < r.rlen) {
/* discard the buffer upto pos */
if (r.pos > 0) {
memmove(r.buf,r.buf+r.pos,r.len-r.pos);
r.avail -= r.pos;
r.pos = 0;
} }
} while (reply == NULL);
redisFreeReplyReader(reader);
return reply;
}
void *redisCreateReplyReader() {
redisReader *r = calloc(sizeof(redisReader),1);
r->buf = sdsempty();
return r;
}
void redisFreeReplyReader(void *reader) {
redisReader *r = reader;
if (r->buf != NULL) {
sdsfree(r->buf);
}
if (r->rlen > 0) {
freeReplyObject(r->rlist[0]);
free(r->rlist);
}
free(r);
}
void *redisFeedReplyReader(void *reader, char *buf, int len) {
redisReader *r = reader;
/* Check if we are able to do *something*. */
if (sdslen(r->buf) == 0 && (buf == NULL || len <= 0))
return NULL;
/* Copy the provided buffer. */
if (buf != NULL && len >= 1)
r->buf = sdscatlen(r->buf,buf,len);
/* Create first item to process when the item list is empty. */
if (r->rlen == 0) {
r->rlist = malloc(sizeof(redisReply*));
r->rlist[0] = createReplyObject(-1,NULL);
r->rlen = 1;
r->rpos = 0;
}
/* make sure there is room for at least BUFFER_SIZE */ /* Process items in reply. */
if (r.len-r.avail < READ_BUFFER_SIZE) { while (r->rpos < r->rlen)
r.buf = realloc(r.buf,r.avail+READ_BUFFER_SIZE+1); if (processItem(r) < 0)
r.len = r.avail+READ_BUFFER_SIZE; break;
/* Discard the consumed part of the buffer. */
if (r->pos > 0) {
if (r->pos == sdslen(r->buf)) {
/* sdsrange has a quirck on this edge case. */
sdsfree(r->buf);
r->buf = sdsempty();
} else {
r->buf = sdsrange(r->buf,r->pos,sdslen(r->buf));
} }
r->pos = 0;
}
/* Emit a reply when there is one. */
if (r->rpos == r->rlen) {
redisReply *reply = r->rlist[0];
/* read from socket into buffer */ /* Destroy the buffer when it is empty and is quite large. */
if ((bytes = read(fd,r.buf+r.avail,READ_BUFFER_SIZE)) <= 0) { if (sdslen(r->buf) == 0 && sdsavail(r->buf) > 16*1024) {
/* rlist[0] is the "root" reply object */ sdsfree(r->buf);
freeReplyObject(r.rlist[0]); r->buf = sdsempty();
free(r.buf); r->pos = 0;
free(r.rlist);
return redisIOError();
} }
r.avail += bytes;
r.buf[r.avail] = '\0';
/* process items in reply */ /* Free list of items to process. */
while (r.rpos < r.rlen) free(r->rlist);
if (processItem(&r) < 0) r->rlen = r->rpos = 0;
break; return reply;
} else {
return NULL;
} }
reply = r.rlist[0];
free(r.buf);
free(r.rlist);
return reply;
} }
/* Helper function for redisCommand(). It's used to append the next argument /* Helper function for redisCommand(). It's used to append the next argument
......
...@@ -51,5 +51,8 @@ typedef struct redisReply { ...@@ -51,5 +51,8 @@ typedef struct redisReply {
redisReply *redisConnect(int *fd, const char *ip, int port); redisReply *redisConnect(int *fd, const char *ip, int port);
void freeReplyObject(redisReply *r); void freeReplyObject(redisReply *r);
redisReply *redisCommand(int fd, const char *format, ...); redisReply *redisCommand(int fd, const char *format, ...);
void *redisCreateReplyReader();
void redisFreeReplyReader(void *ptr);
void *redisFeedReplyReader(void *reader, char *buf, int len);
#endif #endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment