RESP3 support changes from Redis.
This corresponds to commits d5c54f0b..bea09a7f in the redis repository.master
parent
a7a1886b7e
commit
91de9c975a
|
@ -8,6 +8,12 @@ os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- staging
|
||||||
|
- trying
|
||||||
|
- master
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
||||||
|
|
||||||
|
|
76
hiredis.c
76
hiredis.c
|
@ -48,7 +48,9 @@ static redisReply *createReplyObject(int type);
|
||||||
static void *createStringObject(const redisReadTask *task, char *str, size_t len);
|
static void *createStringObject(const redisReadTask *task, char *str, size_t len);
|
||||||
static void *createArrayObject(const redisReadTask *task, int elements);
|
static void *createArrayObject(const redisReadTask *task, int elements);
|
||||||
static void *createIntegerObject(const redisReadTask *task, long long value);
|
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 *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
|
/* Default set of functions to build the reply. Keep in mind that such a
|
||||||
* function returning NULL is interpreted as OOM. */
|
* function returning NULL is interpreted as OOM. */
|
||||||
|
@ -56,7 +58,9 @@ static redisReplyObjectFunctions defaultFunctions = {
|
||||||
createStringObject,
|
createStringObject,
|
||||||
createArrayObject,
|
createArrayObject,
|
||||||
createIntegerObject,
|
createIntegerObject,
|
||||||
|
createDoubleObject,
|
||||||
createNilObject,
|
createNilObject,
|
||||||
|
createBoolObject,
|
||||||
freeReplyObject
|
freeReplyObject
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,6 +87,8 @@ void freeReplyObject(void *reply) {
|
||||||
case REDIS_REPLY_INTEGER:
|
case REDIS_REPLY_INTEGER:
|
||||||
break; /* Nothing to free */
|
break; /* Nothing to free */
|
||||||
case REDIS_REPLY_ARRAY:
|
case REDIS_REPLY_ARRAY:
|
||||||
|
case REDIS_REPLY_MAP:
|
||||||
|
case REDIS_REPLY_SET:
|
||||||
if (r->element != NULL) {
|
if (r->element != NULL) {
|
||||||
for (j = 0; j < r->elements; j++)
|
for (j = 0; j < r->elements; j++)
|
||||||
freeReplyObject(r->element[j]);
|
freeReplyObject(r->element[j]);
|
||||||
|
@ -92,6 +98,7 @@ void freeReplyObject(void *reply) {
|
||||||
case REDIS_REPLY_ERROR:
|
case REDIS_REPLY_ERROR:
|
||||||
case REDIS_REPLY_STATUS:
|
case REDIS_REPLY_STATUS:
|
||||||
case REDIS_REPLY_STRING:
|
case REDIS_REPLY_STRING:
|
||||||
|
case REDIS_REPLY_DOUBLE:
|
||||||
free(r->str);
|
free(r->str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -124,7 +131,9 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||||
|
|
||||||
if (task->parent) {
|
if (task->parent) {
|
||||||
parent = task->parent->obj;
|
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;
|
parent->element[task->idx] = r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
@ -133,7 +142,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||||
static void *createArrayObject(const redisReadTask *task, int elements) {
|
static void *createArrayObject(const redisReadTask *task, int elements) {
|
||||||
redisReply *r, *parent;
|
redisReply *r, *parent;
|
||||||
|
|
||||||
r = createReplyObject(REDIS_REPLY_ARRAY);
|
r = createReplyObject(task->type);
|
||||||
if (r == NULL)
|
if (r == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -149,7 +158,9 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
|
||||||
|
|
||||||
if (task->parent) {
|
if (task->parent) {
|
||||||
parent = task->parent->obj;
|
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;
|
parent->element[task->idx] = r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
@ -166,7 +177,41 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
|
||||||
|
|
||||||
if (task->parent) {
|
if (task->parent) {
|
||||||
parent = task->parent->obj;
|
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;
|
parent->element[task->idx] = r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
@ -181,7 +226,28 @@ static void *createNilObject(const redisReadTask *task) {
|
||||||
|
|
||||||
if (task->parent) {
|
if (task->parent) {
|
||||||
parent = task->parent->obj;
|
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;
|
parent->element[task->idx] = r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -101,8 +101,10 @@ extern "C" {
|
||||||
typedef struct redisReply {
|
typedef struct redisReply {
|
||||||
int type; /* REDIS_REPLY_* */
|
int type; /* REDIS_REPLY_* */
|
||||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
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 */
|
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 */
|
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
||||||
} redisReply;
|
} redisReply;
|
||||||
|
|
60
read.c
60
read.c
|
@ -29,9 +29,9 @@
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "fmacros.h"
|
#include "fmacros.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -40,6 +40,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
#include "sds.h"
|
#include "sds.h"
|
||||||
|
@ -243,7 +244,9 @@ static void moveToNextTask(redisReader *r) {
|
||||||
|
|
||||||
cur = &(r->rstack[r->ridx]);
|
cur = &(r->rstack[r->ridx]);
|
||||||
prv = &(r->rstack[r->ridx-1]);
|
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) {
|
if (cur->idx == prv->elements-1) {
|
||||||
r->ridx--;
|
r->ridx--;
|
||||||
} else {
|
} else {
|
||||||
|
@ -276,6 +279,47 @@ static int processLineItem(redisReader *r) {
|
||||||
} else {
|
} else {
|
||||||
obj = (void*)REDIS_REPLY_INTEGER;
|
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 {
|
} else {
|
||||||
/* Type will be error or status. */
|
/* Type will be error or status. */
|
||||||
if (r->fn && r->fn->createString)
|
if (r->fn && r->fn->createString)
|
||||||
|
@ -458,6 +502,12 @@ static int processItem(redisReader *r) {
|
||||||
case ':':
|
case ':':
|
||||||
cur->type = REDIS_REPLY_INTEGER;
|
cur->type = REDIS_REPLY_INTEGER;
|
||||||
break;
|
break;
|
||||||
|
case ',':
|
||||||
|
cur->type = REDIS_REPLY_DOUBLE;
|
||||||
|
break;
|
||||||
|
case '_':
|
||||||
|
cur->type = REDIS_REPLY_NIL;
|
||||||
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
cur->type = REDIS_REPLY_STRING;
|
cur->type = REDIS_REPLY_STRING;
|
||||||
break;
|
break;
|
||||||
|
@ -470,6 +520,9 @@ static int processItem(redisReader *r) {
|
||||||
case '~':
|
case '~':
|
||||||
cur->type = REDIS_REPLY_SET;
|
cur->type = REDIS_REPLY_SET;
|
||||||
break;
|
break;
|
||||||
|
case '#':
|
||||||
|
cur->type = REDIS_REPLY_BOOL;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
__redisReaderSetErrorProtocolByte(r,*p);
|
__redisReaderSetErrorProtocolByte(r,*p);
|
||||||
return REDIS_ERR;
|
return REDIS_ERR;
|
||||||
|
@ -485,6 +538,9 @@ static int processItem(redisReader *r) {
|
||||||
case REDIS_REPLY_ERROR:
|
case REDIS_REPLY_ERROR:
|
||||||
case REDIS_REPLY_STATUS:
|
case REDIS_REPLY_STATUS:
|
||||||
case REDIS_REPLY_INTEGER:
|
case REDIS_REPLY_INTEGER:
|
||||||
|
case REDIS_REPLY_DOUBLE:
|
||||||
|
case REDIS_REPLY_NIL:
|
||||||
|
case REDIS_REPLY_BOOL:
|
||||||
return processLineItem(r);
|
return processLineItem(r);
|
||||||
case REDIS_REPLY_STRING:
|
case REDIS_REPLY_STRING:
|
||||||
return processBulkItem(r);
|
return processBulkItem(r);
|
||||||
|
|
2
read.h
2
read.h
|
@ -82,7 +82,9 @@ typedef struct redisReplyObjectFunctions {
|
||||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
void *(*createString)(const redisReadTask*, char*, size_t);
|
||||||
void *(*createArray)(const redisReadTask*, int);
|
void *(*createArray)(const redisReadTask*, int);
|
||||||
void *(*createInteger)(const redisReadTask*, long long);
|
void *(*createInteger)(const redisReadTask*, long long);
|
||||||
|
void *(*createDouble)(const redisReadTask*, double, char*, size_t);
|
||||||
void *(*createNil)(const redisReadTask*);
|
void *(*createNil)(const redisReadTask*);
|
||||||
|
void *(*createBool)(const redisReadTask*, int);
|
||||||
void (*freeObject)(void*);
|
void (*freeObject)(void*);
|
||||||
} redisReplyObjectFunctions;
|
} redisReplyObjectFunctions;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue