diff options
| -rw-r--r-- | .travis.yml | 6 | ||||
| -rw-r--r-- | hiredis.c | 76 | ||||
| -rw-r--r-- | hiredis.h | 4 | ||||
| -rw-r--r-- | read.c | 77 | ||||
| -rw-r--r-- | read.h | 10 | 
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 @@ -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; @@ -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; @@ -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. */ @@ -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; | 
