summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Nunberg <mnunberg@users.noreply.github.com>2019-08-27 06:59:32 -0400
committerGitHub <noreply@github.com>2019-08-27 06:59:32 -0400
commitff4fa45422dc104f3377cdc3bc88d2d24ac2fcf3 (patch)
treee78792fdfe41d39c887e5ca0f325e46865ad74aa
parentce7cb7bcc4ca53291aca8ab8dc0f31e27c032dcc (diff)
parent91de9c975ac66b6eaddb6de96440a282e4227447 (diff)
Merge pull request #697 from yossigo/resp3
Port RESP3 support from Redis.
-rw-r--r--.travis.yml6
-rw-r--r--hiredis.c76
-rw-r--r--hiredis.h4
-rw-r--r--read.c77
-rw-r--r--read.h10
5 files changed, 162 insertions, 11 deletions
diff --git a/.travis.yml b/.travis.yml
index 4a29823..dd8e0e7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,12 @@ os:
- linux
- osx
+branches:
+ only:
+ - staging
+ - trying
+ - master
+
before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
diff --git a/hiredis.c b/hiredis.c
index 5a6fcf8..9627832 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -48,7 +48,9 @@ static redisReply *createReplyObject(int type);
static void *createStringObject(const redisReadTask *task, char *str, size_t len);
static void *createArrayObject(const redisReadTask *task, size_t elements);
static void *createIntegerObject(const redisReadTask *task, long long value);
+static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);
static void *createNilObject(const redisReadTask *task);
+static void *createBoolObject(const redisReadTask *task, int bval);
/* Default set of functions to build the reply. Keep in mind that such a
* function returning NULL is interpreted as OOM. */
@@ -56,7 +58,9 @@ static redisReplyObjectFunctions defaultFunctions = {
createStringObject,
createArrayObject,
createIntegerObject,
+ createDoubleObject,
createNilObject,
+ createBoolObject,
freeReplyObject
};
@@ -83,6 +87,8 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_INTEGER:
break; /* Nothing to free */
case REDIS_REPLY_ARRAY:
+ case REDIS_REPLY_MAP:
+ case REDIS_REPLY_SET:
if (r->element != NULL) {
for (j = 0; j < r->elements; j++)
freeReplyObject(r->element[j]);
@@ -92,6 +98,7 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
+ case REDIS_REPLY_DOUBLE:
free(r->str);
break;
}
@@ -124,7 +131,9 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
if (task->parent) {
parent = task->parent->obj;
- assert(parent->type == REDIS_REPLY_ARRAY);
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
@@ -133,7 +142,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
static void *createArrayObject(const redisReadTask *task, size_t elements) {
redisReply *r, *parent;
- r = createReplyObject(REDIS_REPLY_ARRAY);
+ r = createReplyObject(task->type);
if (r == NULL)
return NULL;
@@ -149,7 +158,9 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
if (task->parent) {
parent = task->parent->obj;
- assert(parent->type == REDIS_REPLY_ARRAY);
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
@@ -166,7 +177,41 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
if (task->parent) {
parent = task->parent->obj;
- assert(parent->type == REDIS_REPLY_ARRAY);
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
+ parent->element[task->idx] = r;
+ }
+ return r;
+}
+
+static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
+ redisReply *r, *parent;
+
+ r = createReplyObject(REDIS_REPLY_DOUBLE);
+ if (r == NULL)
+ return NULL;
+
+ r->dval = value;
+ r->str = malloc(len+1);
+ if (r->str == NULL) {
+ freeReplyObject(r);
+ return NULL;
+ }
+
+ /* The double reply also has the original protocol string representing a
+ * double as a null terminated string. This way the caller does not need
+ * to format back for string conversion, especially since Redis does efforts
+ * to make the string more human readable avoiding the calssical double
+ * decimal string conversion artifacts. */
+ memcpy(r->str, str, len);
+ r->str[len] = '\0';
+
+ if (task->parent) {
+ parent = task->parent->obj;
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
@@ -181,7 +226,28 @@ static void *createNilObject(const redisReadTask *task) {
if (task->parent) {
parent = task->parent->obj;
- assert(parent->type == REDIS_REPLY_ARRAY);
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
+ parent->element[task->idx] = r;
+ }
+ return r;
+}
+
+static void *createBoolObject(const redisReadTask *task, int bval) {
+ redisReply *r, *parent;
+
+ r = createReplyObject(REDIS_REPLY_BOOL);
+ if (r == NULL)
+ return NULL;
+
+ r->integer = bval != 0;
+
+ if (task->parent) {
+ parent = task->parent->obj;
+ assert(parent->type == REDIS_REPLY_ARRAY ||
+ parent->type == REDIS_REPLY_MAP ||
+ parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
diff --git a/hiredis.h b/hiredis.h
index 63eef3a..d76a9e3 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -101,8 +101,10 @@ extern "C" {
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
+ double dval; /* The double when type is REDIS_REPLY_DOUBLE */
size_t len; /* Length of string */
- char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
+ char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
+ and REDIS_REPLY_DOUBLE (in additionl to dval). */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
diff --git a/read.c b/read.c
index 4767448..2553c01 100644
--- a/read.c
+++ b/read.c
@@ -29,9 +29,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-
#include "fmacros.h"
#include <string.h>
+#include <strings.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h>
@@ -40,6 +40,7 @@
#include <errno.h>
#include <ctype.h>
#include <limits.h>
+#include <math.h>
#include "read.h"
#include "sds.h"
@@ -243,7 +244,9 @@ static void moveToNextTask(redisReader *r) {
cur = &(r->rstack[r->ridx]);
prv = &(r->rstack[r->ridx-1]);
- assert(prv->type == REDIS_REPLY_ARRAY);
+ assert(prv->type == REDIS_REPLY_ARRAY ||
+ prv->type == REDIS_REPLY_MAP ||
+ prv->type == REDIS_REPLY_SET);
if (cur->idx == prv->elements-1) {
r->ridx--;
} else {
@@ -276,6 +279,47 @@ static int processLineItem(redisReader *r) {
} else {
obj = (void*)REDIS_REPLY_INTEGER;
}
+ } else if (cur->type == REDIS_REPLY_DOUBLE) {
+ if (r->fn && r->fn->createDouble) {
+ char buf[326], *eptr;
+ double d;
+
+ if ((size_t)len >= sizeof(buf)) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Double value is too large");
+ return REDIS_ERR;
+ }
+
+ memcpy(buf,p,len);
+ buf[len] = '\0';
+
+ if (strcasecmp(buf,",inf") == 0) {
+ d = 1.0/0.0; /* Positive infinite. */
+ } else if (strcasecmp(buf,",-inf") == 0) {
+ d = -1.0/0.0; /* Nevative infinite. */
+ } else {
+ d = strtod((char*)buf,&eptr);
+ if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Bad double value");
+ return REDIS_ERR;
+ }
+ }
+ obj = r->fn->createDouble(cur,d,buf,len);
+ } else {
+ obj = (void*)REDIS_REPLY_DOUBLE;
+ }
+ } else if (cur->type == REDIS_REPLY_NIL) {
+ if (r->fn && r->fn->createNil)
+ obj = r->fn->createNil(cur);
+ else
+ obj = (void*)REDIS_REPLY_NIL;
+ } else if (cur->type == REDIS_REPLY_BOOL) {
+ int bval = p[0] == 't' || p[0] == 'T';
+ if (r->fn && r->fn->createBool)
+ obj = r->fn->createBool(cur,bval);
+ else
+ obj = (void*)REDIS_REPLY_BOOL;
} else {
/* Type will be error or status. */
if (r->fn && r->fn->createString)
@@ -362,7 +406,8 @@ static int processBulkItem(redisReader *r) {
return REDIS_ERR;
}
-static int processMultiBulkItem(redisReader *r) {
+/* Process the array, map and set types. */
+static int processAggregateItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
void *obj;
char *p;
@@ -404,10 +449,12 @@ static int processMultiBulkItem(redisReader *r) {
moveToNextTask(r);
} else {
+ if (cur->type == REDIS_REPLY_MAP) elements *= 2;
+
if (r->fn && r->fn->createArray)
obj = r->fn->createArray(cur,elements);
else
- obj = (void*)REDIS_REPLY_ARRAY;
+ obj = (void*)(long)cur->type;
if (obj == NULL) {
__redisReaderSetErrorOOM(r);
@@ -455,12 +502,27 @@ static int processItem(redisReader *r) {
case ':':
cur->type = REDIS_REPLY_INTEGER;
break;
+ case ',':
+ cur->type = REDIS_REPLY_DOUBLE;
+ break;
+ case '_':
+ cur->type = REDIS_REPLY_NIL;
+ break;
case '$':
cur->type = REDIS_REPLY_STRING;
break;
case '*':
cur->type = REDIS_REPLY_ARRAY;
break;
+ case '%':
+ cur->type = REDIS_REPLY_MAP;
+ break;
+ case '~':
+ cur->type = REDIS_REPLY_SET;
+ break;
+ case '#':
+ cur->type = REDIS_REPLY_BOOL;
+ break;
default:
__redisReaderSetErrorProtocolByte(r,*p);
return REDIS_ERR;
@@ -476,11 +538,16 @@ static int processItem(redisReader *r) {
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_INTEGER:
+ case REDIS_REPLY_DOUBLE:
+ case REDIS_REPLY_NIL:
+ case REDIS_REPLY_BOOL:
return processLineItem(r);
case REDIS_REPLY_STRING:
return processBulkItem(r);
case REDIS_REPLY_ARRAY:
- return processMultiBulkItem(r);
+ case REDIS_REPLY_MAP:
+ case REDIS_REPLY_SET:
+ return processAggregateItem(r);
default:
assert(NULL);
return REDIS_ERR; /* Avoid warning. */
diff --git a/read.h b/read.h
index a6008b7..af02aaf 100644
--- a/read.h
+++ b/read.h
@@ -54,6 +54,14 @@
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
+#define REDIS_REPLY_DOUBLE 7
+#define REDIS_REPLY_BOOL 8
+#define REDIS_REPLY_VERB 9
+#define REDIS_REPLY_MAP 9
+#define REDIS_REPLY_SET 10
+#define REDIS_REPLY_ATTR 11
+#define REDIS_REPLY_PUSH 12
+#define REDIS_REPLY_BIGNUM 13
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
@@ -74,7 +82,9 @@ typedef struct redisReplyObjectFunctions {
void *(*createString)(const redisReadTask*, char*, size_t);
void *(*createArray)(const redisReadTask*, size_t);
void *(*createInteger)(const redisReadTask*, long long);
+ void *(*createDouble)(const redisReadTask*, double, char*, size_t);
void *(*createNil)(const redisReadTask*);
+ void *(*createBool)(const redisReadTask*, int);
void (*freeObject)(void*);
} redisReplyObjectFunctions;