hiredis 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
4
  *
5
5
  * All rights reserved.
6
6
  *
@@ -36,8 +36,8 @@
36
36
  #include <sys/time.h> /* for struct timeval */
37
37
 
38
38
  #define HIREDIS_MAJOR 0
39
- #define HIREDIS_MINOR 9
40
- #define HIREDIS_PATCH 2
39
+ #define HIREDIS_MINOR 10
40
+ #define HIREDIS_PATCH 0
41
41
 
42
42
  #define REDIS_ERR -1
43
43
  #define REDIS_OK 0
@@ -46,10 +46,11 @@
46
46
  * error that occured. REDIS_ERR_IO means there was an I/O error and you
47
47
  * should use the "errno" variable to find out what is wrong.
48
48
  * For other values, the "errstr" field will hold a description. */
49
- #define REDIS_ERR_IO 1 /* error in read or write */
50
- #define REDIS_ERR_EOF 3 /* eof */
51
- #define REDIS_ERR_PROTOCOL 4 /* protocol error */
52
- #define REDIS_ERR_OTHER 2 /* something else */
49
+ #define REDIS_ERR_IO 1 /* Error in read or write */
50
+ #define REDIS_ERR_EOF 3 /* End of file */
51
+ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */
52
+ #define REDIS_ERR_OOM 5 /* Out of memory */
53
+ #define REDIS_ERR_OTHER 2 /* Everything else... */
53
54
 
54
55
  /* Connection type can be blocking or non-blocking and is set in the
55
56
  * least significant bit of the flags field in redisContext. */
@@ -113,36 +114,56 @@ typedef struct redisReplyObjectFunctions {
113
114
  void (*freeObject)(void*);
114
115
  } redisReplyObjectFunctions;
115
116
 
116
- struct redisContext; /* need forward declaration of redisContext */
117
-
118
- /* Context for a connection to Redis */
119
- typedef struct redisContext {
120
- int fd;
121
- int flags;
122
- char *obuf; /* Write buffer */
117
+ /* State for the protocol parser */
118
+ typedef struct redisReader {
123
119
  int err; /* Error flags, 0 when there is no error */
124
- char *errstr; /* String representation of error when applicable */
120
+ char errstr[128]; /* String representation of error when applicable */
125
121
 
126
- /* Function set for reply buildup and reply reader */
127
- redisReplyObjectFunctions *fn;
128
- void *reader;
129
- } redisContext;
122
+ char *buf; /* Read buffer */
123
+ size_t pos; /* Buffer cursor */
124
+ size_t len; /* Buffer length */
125
+
126
+ redisReadTask rstack[3];
127
+ int ridx; /* Index of current read task */
128
+ void *reply; /* Temporary reply pointer */
130
129
 
130
+ redisReplyObjectFunctions *fn;
131
+ void *privdata;
132
+ } redisReader;
133
+
134
+ /* Public API for the protocol parser. */
135
+ redisReader *redisReaderCreate(void);
136
+ void redisReaderFree(redisReader *r);
137
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len);
138
+ int redisReaderGetReply(redisReader *r, void **reply);
139
+
140
+ /* Backwards compatibility, can be removed on big version bump. */
141
+ #define redisReplyReaderCreate redisReaderCreate
142
+ #define redisReplyReaderFree redisReaderFree
143
+ #define redisReplyReaderFeed redisReaderFeed
144
+ #define redisReplyReaderGetReply redisReaderGetReply
145
+ #define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
146
+ #define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
147
+ #define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
148
+
149
+ /* Function to free the reply objects hiredis returns by default. */
131
150
  void freeReplyObject(void *reply);
132
- void *redisReplyReaderCreate(void);
133
- int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
134
- int redisReplyReaderSetPrivdata(void *reader, void *privdata);
135
- void *redisReplyReaderGetObject(void *reader);
136
- char *redisReplyReaderGetError(void *reader);
137
- void redisReplyReaderFree(void *ptr);
138
- void redisReplyReaderFeed(void *reader, const char *buf, size_t len);
139
- int redisReplyReaderGetReply(void *reader, void **reply);
140
151
 
141
152
  /* Functions to format a command according to the protocol. */
142
153
  int redisvFormatCommand(char **target, const char *format, va_list ap);
143
154
  int redisFormatCommand(char **target, const char *format, ...);
144
155
  int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
145
156
 
157
+ /* Context for a connection to Redis */
158
+ typedef struct redisContext {
159
+ int err; /* Error flags, 0 when there is no error */
160
+ char errstr[128]; /* String representation of error when applicable */
161
+ int fd;
162
+ int flags;
163
+ char *obuf; /* Write buffer */
164
+ redisReader *reader; /* Protocol reader */
165
+ } redisContext;
166
+
146
167
  redisContext *redisConnect(const char *ip, int port);
147
168
  redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
148
169
  redisContext *redisConnectNonBlock(const char *ip, int port);
@@ -150,7 +171,6 @@ redisContext *redisConnectUnix(const char *path);
150
171
  redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
151
172
  redisContext *redisConnectUnixNonBlock(const char *path);
152
173
  int redisSetTimeout(redisContext *c, struct timeval tv);
153
- int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
154
174
  void redisFree(redisContext *c);
155
175
  int redisBufferRead(redisContext *c);
156
176
  int redisBufferWrite(redisContext *c, int *done);
@@ -164,9 +184,9 @@ int redisGetReplyFromReader(redisContext *c, void **reply);
164
184
 
165
185
  /* Write a command to the output buffer. Use these functions in blocking mode
166
186
  * to get a pipeline of commands. */
167
- void redisvAppendCommand(redisContext *c, const char *format, va_list ap);
168
- void redisAppendCommand(redisContext *c, const char *format, ...);
169
- void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
187
+ int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
188
+ int redisAppendCommand(redisContext *c, const char *format, ...);
189
+ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
170
190
 
171
191
  /* Issue a command to Redis. In a blocking context, it is identical to calling
172
192
  * redisAppendCommand, followed by redisGetReply. The function will return
@@ -1,7 +1,7 @@
1
1
  /* Extracted from anet.c to work properly with Hiredis error reporting.
2
2
  *
3
- * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
+ * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
5
  *
6
6
  * All rights reserved.
7
7
  *
@@ -49,18 +49,28 @@
49
49
  #include "net.h"
50
50
  #include "sds.h"
51
51
 
52
- /* Forward declaration */
53
- void __redisSetError(redisContext *c, int type, sds err);
52
+ /* Defined in hiredis.c */
53
+ void __redisSetError(redisContext *c, int type, const char *str);
54
+
55
+ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
56
+ char buf[128];
57
+ size_t len = 0;
58
+
59
+ if (prefix != NULL)
60
+ len = snprintf(buf,sizeof(buf),"%s: ",prefix);
61
+ strerror_r(errno,buf+len,sizeof(buf)-len);
62
+ __redisSetError(c,type,buf);
63
+ }
54
64
 
55
65
  static int redisCreateSocket(redisContext *c, int type) {
56
66
  int s, on = 1;
57
67
  if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
58
- __redisSetError(c,REDIS_ERR_IO,NULL);
68
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
59
69
  return REDIS_ERR;
60
70
  }
61
71
  if (type == AF_INET) {
62
72
  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
63
- __redisSetError(c,REDIS_ERR_IO,NULL);
73
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
64
74
  close(s);
65
75
  return REDIS_ERR;
66
76
  }
@@ -75,8 +85,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
75
85
  * Note that fcntl(2) for F_GETFL and F_SETFL can't be
76
86
  * interrupted by a signal. */
77
87
  if ((flags = fcntl(fd, F_GETFL)) == -1) {
78
- __redisSetError(c,REDIS_ERR_IO,
79
- sdscatprintf(sdsempty(), "fcntl(F_GETFL): %s", strerror(errno)));
88
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
80
89
  close(fd);
81
90
  return REDIS_ERR;
82
91
  }
@@ -87,8 +96,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
87
96
  flags |= O_NONBLOCK;
88
97
 
89
98
  if (fcntl(fd, F_SETFL, flags) == -1) {
90
- __redisSetError(c,REDIS_ERR_IO,
91
- sdscatprintf(sdsempty(), "fcntl(F_SETFL): %s", strerror(errno)));
99
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
92
100
  close(fd);
93
101
  return REDIS_ERR;
94
102
  }
@@ -98,8 +106,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
98
106
  static int redisSetTcpNoDelay(redisContext *c, int fd) {
99
107
  int yes = 1;
100
108
  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
101
- __redisSetError(c,REDIS_ERR_IO,
102
- sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
109
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
103
110
  close(fd);
104
111
  return REDIS_ERR;
105
112
  }
@@ -124,15 +131,14 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
124
131
  FD_SET(fd, &wfd);
125
132
 
126
133
  if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) {
127
- __redisSetError(c,REDIS_ERR_IO,
128
- sdscatprintf(sdsempty(), "select(2): %s", strerror(errno)));
134
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"select(2)");
129
135
  close(fd);
130
136
  return REDIS_ERR;
131
137
  }
132
138
 
133
139
  if (!FD_ISSET(fd, &wfd)) {
134
140
  errno = ETIMEDOUT;
135
- __redisSetError(c,REDIS_ERR_IO,NULL);
141
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
136
142
  close(fd);
137
143
  return REDIS_ERR;
138
144
  }
@@ -140,15 +146,14 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
140
146
  err = 0;
141
147
  errlen = sizeof(err);
142
148
  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
143
- __redisSetError(c,REDIS_ERR_IO,
144
- sdscatprintf(sdsempty(), "getsockopt(SO_ERROR): %s", strerror(errno)));
149
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
145
150
  close(fd);
146
151
  return REDIS_ERR;
147
152
  }
148
153
 
149
154
  if (err) {
150
155
  errno = err;
151
- __redisSetError(c,REDIS_ERR_IO,NULL);
156
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
152
157
  close(fd);
153
158
  return REDIS_ERR;
154
159
  }
@@ -156,20 +161,18 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
156
161
  return REDIS_OK;
157
162
  }
158
163
 
159
- __redisSetError(c,REDIS_ERR_IO,NULL);
164
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
160
165
  close(fd);
161
166
  return REDIS_ERR;
162
167
  }
163
168
 
164
169
  int redisContextSetTimeout(redisContext *c, struct timeval tv) {
165
170
  if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
166
- __redisSetError(c,REDIS_ERR_IO,
167
- sdscatprintf(sdsempty(), "setsockopt(SO_RCVTIMEO): %s", strerror(errno)));
171
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
168
172
  return REDIS_ERR;
169
173
  }
170
174
  if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
171
- __redisSetError(c,REDIS_ERR_IO,
172
- sdscatprintf(sdsempty(), "setsockopt(SO_SNDTIMEO): %s", strerror(errno)));
175
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
173
176
  return REDIS_ERR;
174
177
  }
175
178
  return REDIS_OK;
@@ -192,8 +195,9 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
192
195
 
193
196
  he = gethostbyname(addr);
194
197
  if (he == NULL) {
195
- __redisSetError(c,REDIS_ERR_OTHER,
196
- sdscatprintf(sdsempty(),"Can't resolve: %s",addr));
198
+ char buf[128];
199
+ snprintf(buf,sizeof(buf),"Can't resolve: %s", addr);
200
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
197
201
  close(s);
198
202
  return REDIS_ERR;
199
203
  }
@@ -1,7 +1,7 @@
1
1
  /* Extracted from anet.c to work properly with Hiredis error reporting.
2
2
  *
3
- * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
+ * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
5
  *
6
6
  * All rights reserved.
7
7
  *
@@ -28,18 +28,18 @@
28
28
  * POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
- #define SDS_ABORT_ON_OOM
32
-
33
- #include "sds.h"
34
31
  #include <stdio.h>
35
32
  #include <stdlib.h>
36
33
  #include <string.h>
37
34
  #include <ctype.h>
35
+ #include "sds.h"
38
36
 
37
+ #ifdef SDS_ABORT_ON_OOM
39
38
  static void sdsOomAbort(void) {
40
39
  fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
41
40
  abort();
42
41
  }
42
+ #endif
43
43
 
44
44
  sds sdsnewlen(const void *init, size_t initlen) {
45
45
  struct sdshdr *sh;
@@ -69,11 +69,6 @@ sds sdsnew(const char *init) {
69
69
  return sdsnewlen(init, initlen);
70
70
  }
71
71
 
72
- size_t sdslen(const sds s) {
73
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
74
- return sh->len;
75
- }
76
-
77
72
  sds sdsdup(const sds s) {
78
73
  return sdsnewlen(s, sdslen(s));
79
74
  }
@@ -83,11 +78,6 @@ void sdsfree(sds s) {
83
78
  free(s-sizeof(struct sdshdr));
84
79
  }
85
80
 
86
- size_t sdsavail(sds s) {
87
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
88
- return sh->free;
89
- }
90
-
91
81
  void sdsupdatelen(sds s) {
92
82
  struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
93
83
  int reallen = strlen(s);
@@ -388,17 +378,19 @@ sds sdsfromlonglong(long long value) {
388
378
 
389
379
  sds sdscatrepr(sds s, char *p, size_t len) {
390
380
  s = sdscatlen(s,"\"",1);
381
+ if (s == NULL) return NULL;
382
+
391
383
  while(len--) {
392
384
  switch(*p) {
393
385
  case '\\':
394
386
  case '"':
395
387
  s = sdscatprintf(s,"\\%c",*p);
396
388
  break;
397
- case '\n': s = sdscatlen(s,"\\n",1); break;
398
- case '\r': s = sdscatlen(s,"\\r",1); break;
399
- case '\t': s = sdscatlen(s,"\\t",1); break;
400
- case '\a': s = sdscatlen(s,"\\a",1); break;
401
- case '\b': s = sdscatlen(s,"\\b",1); break;
389
+ case '\n': s = sdscatlen(s,"\\n",2); break;
390
+ case '\r': s = sdscatlen(s,"\\r",2); break;
391
+ case '\t': s = sdscatlen(s,"\\t",2); break;
392
+ case '\a': s = sdscatlen(s,"\\a",2); break;
393
+ case '\b': s = sdscatlen(s,"\\b",2); break;
402
394
  default:
403
395
  if (isprint(*p))
404
396
  s = sdscatprintf(s,"%c",*p);
@@ -407,6 +399,7 @@ sds sdscatrepr(sds s, char *p, size_t len) {
407
399
  break;
408
400
  }
409
401
  p++;
402
+ if (s == NULL) return NULL;
410
403
  }
411
404
  return sdscatlen(s,"\"",1);
412
405
  }
@@ -426,7 +419,7 @@ sds sdscatrepr(sds s, char *p, size_t len) {
426
419
  sds *sdssplitargs(char *line, int *argc) {
427
420
  char *p = line;
428
421
  char *current = NULL;
429
- char **vector = NULL;
422
+ char **vector = NULL, **_vector = NULL;
430
423
 
431
424
  *argc = 0;
432
425
  while(1) {
@@ -437,7 +430,11 @@ sds *sdssplitargs(char *line, int *argc) {
437
430
  int inq=0; /* set to 1 if we are in "quotes" */
438
431
  int done=0;
439
432
 
440
- if (current == NULL) current = sdsempty();
433
+ if (current == NULL) {
434
+ current = sdsempty();
435
+ if (current == NULL) goto err;
436
+ }
437
+
441
438
  while(!done) {
442
439
  if (inq) {
443
440
  if (*p == '\\' && *(p+1)) {
@@ -481,9 +478,13 @@ sds *sdssplitargs(char *line, int *argc) {
481
478
  }
482
479
  }
483
480
  if (*p) p++;
481
+ if (current == NULL) goto err;
484
482
  }
485
483
  /* add the token to the vector */
486
- vector = realloc(vector,((*argc)+1)*sizeof(char*));
484
+ _vector = realloc(vector,((*argc)+1)*sizeof(char*));
485
+ if (_vector == NULL) goto err;
486
+
487
+ vector = _vector;
487
488
  vector[*argc] = current;
488
489
  (*argc)++;
489
490
  current = NULL;
@@ -495,8 +496,8 @@ sds *sdssplitargs(char *line, int *argc) {
495
496
  err:
496
497
  while((*argc)--)
497
498
  sdsfree(vector[*argc]);
498
- free(vector);
499
- if (current) sdsfree(current);
499
+ if (vector != NULL) free(vector);
500
+ if (current != NULL) sdsfree(current);
500
501
  return NULL;
501
502
  }
502
503
 
@@ -42,6 +42,16 @@ struct sdshdr {
42
42
  char buf[];
43
43
  };
44
44
 
45
+ static inline size_t sdslen(const sds s) {
46
+ struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
47
+ return sh->len;
48
+ }
49
+
50
+ static inline size_t sdsavail(const sds s) {
51
+ struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
52
+ return sh->free;
53
+ }
54
+
45
55
  sds sdsnewlen(const void *init, size_t initlen);
46
56
  sds sdsnew(const char *init);
47
57
  sds sdsempty(void);
@@ -54,6 +54,12 @@ static void test_format_commands(void) {
54
54
  len == 4+4+(3+2)+4+(3+2)+4+(0+2));
55
55
  free(cmd);
56
56
 
57
+ test("Format command with an empty string in between proper interpolations: ");
58
+ len = redisFormatCommand(&cmd,"SET %s %s","","foo");
59
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
60
+ len == 4+4+(3+2)+4+(0+2)+4+(3+2));
61
+ free(cmd);
62
+
57
63
  test("Format command with %%b string interpolation: ");
58
64
  len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
59
65
  test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
@@ -262,75 +268,71 @@ static void test_blocking_connection(void) {
262
268
  }
263
269
 
264
270
  static void test_reply_reader(void) {
265
- void *reader;
271
+ redisReader *reader;
266
272
  void *reply;
267
- char *err;
268
273
  int ret;
269
274
 
270
275
  test("Error handling in reply parser: ");
271
- reader = redisReplyReaderCreate();
272
- redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
273
- ret = redisReplyReaderGetReply(reader,NULL);
274
- err = redisReplyReaderGetError(reader);
276
+ reader = redisReaderCreate();
277
+ redisReaderFeed(reader,(char*)"@foo\r\n",6);
278
+ ret = redisReaderGetReply(reader,NULL);
275
279
  test_cond(ret == REDIS_ERR &&
276
- strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
277
- redisReplyReaderFree(reader);
280
+ strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
281
+ redisReaderFree(reader);
278
282
 
279
283
  /* when the reply already contains multiple items, they must be free'd
280
284
  * on an error. valgrind will bark when this doesn't happen. */
281
285
  test("Memory cleanup in reply parser: ");
282
- reader = redisReplyReaderCreate();
283
- redisReplyReaderFeed(reader,(char*)"*2\r\n",4);
284
- redisReplyReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
285
- redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
286
- ret = redisReplyReaderGetReply(reader,NULL);
287
- err = redisReplyReaderGetError(reader);
286
+ reader = redisReaderCreate();
287
+ redisReaderFeed(reader,(char*)"*2\r\n",4);
288
+ redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
289
+ redisReaderFeed(reader,(char*)"@foo\r\n",6);
290
+ ret = redisReaderGetReply(reader,NULL);
288
291
  test_cond(ret == REDIS_ERR &&
289
- strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
290
- redisReplyReaderFree(reader);
292
+ strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
293
+ redisReaderFree(reader);
291
294
 
292
295
  test("Set error on nested multi bulks with depth > 1: ");
293
- reader = redisReplyReaderCreate();
294
- redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
295
- redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
296
- redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
297
- ret = redisReplyReaderGetReply(reader,NULL);
298
- err = redisReplyReaderGetError(reader);
296
+ reader = redisReaderCreate();
297
+ redisReaderFeed(reader,(char*)"*1\r\n",4);
298
+ redisReaderFeed(reader,(char*)"*1\r\n",4);
299
+ redisReaderFeed(reader,(char*)"*1\r\n",4);
300
+ ret = redisReaderGetReply(reader,NULL);
299
301
  test_cond(ret == REDIS_ERR &&
300
- strncasecmp(err,"No support for",14) == 0);
301
- redisReplyReaderFree(reader);
302
+ strncasecmp(reader->errstr,"No support for",14) == 0);
303
+ redisReaderFree(reader);
302
304
 
303
305
  test("Works with NULL functions for reply: ");
304
- reader = redisReplyReaderCreate();
305
- redisReplyReaderSetReplyObjectFunctions(reader,NULL);
306
- redisReplyReaderFeed(reader,(char*)"+OK\r\n",5);
307
- ret = redisReplyReaderGetReply(reader,&reply);
306
+ reader = redisReaderCreate();
307
+ reader->fn = NULL;
308
+ redisReaderFeed(reader,(char*)"+OK\r\n",5);
309
+ ret = redisReaderGetReply(reader,&reply);
308
310
  test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
309
- redisReplyReaderFree(reader);
311
+ redisReaderFree(reader);
310
312
 
311
313
  test("Works when a single newline (\\r\\n) covers two calls to feed: ");
312
- reader = redisReplyReaderCreate();
313
- redisReplyReaderSetReplyObjectFunctions(reader,NULL);
314
- redisReplyReaderFeed(reader,(char*)"+OK\r",4);
315
- ret = redisReplyReaderGetReply(reader,&reply);
314
+ reader = redisReaderCreate();
315
+ reader->fn = NULL;
316
+ redisReaderFeed(reader,(char*)"+OK\r",4);
317
+ ret = redisReaderGetReply(reader,&reply);
316
318
  assert(ret == REDIS_OK && reply == NULL);
317
- redisReplyReaderFeed(reader,(char*)"\n",1);
318
- ret = redisReplyReaderGetReply(reader,&reply);
319
+ redisReaderFeed(reader,(char*)"\n",1);
320
+ ret = redisReaderGetReply(reader,&reply);
319
321
  test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
320
- redisReplyReaderFree(reader);
322
+ redisReaderFree(reader);
321
323
 
322
- test("Properly reset state after protocol error: ");
323
- reader = redisReplyReaderCreate();
324
- redisReplyReaderSetReplyObjectFunctions(reader,NULL);
325
- redisReplyReaderFeed(reader,(char*)"x",1);
326
- ret = redisReplyReaderGetReply(reader,&reply);
324
+ test("Don't reset state after protocol error: ");
325
+ reader = redisReaderCreate();
326
+ reader->fn = NULL;
327
+ redisReaderFeed(reader,(char*)"x",1);
328
+ ret = redisReaderGetReply(reader,&reply);
327
329
  assert(ret == REDIS_ERR);
328
- ret = redisReplyReaderGetReply(reader,&reply);
329
- test_cond(ret == REDIS_OK && reply == NULL)
330
+ ret = redisReaderGetReply(reader,&reply);
331
+ test_cond(ret == REDIS_ERR && reply == NULL);
330
332
  }
331
333
 
332
334
  static void test_throughput(void) {
333
- int i;
335
+ int i, num;
334
336
  long long t1, t2;
335
337
  redisContext *c = blocking_context;
336
338
  redisReply **replies;
@@ -339,28 +341,57 @@ static void test_throughput(void) {
339
341
  for (i = 0; i < 500; i++)
340
342
  freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
341
343
 
342
- replies = malloc(sizeof(redisReply*)*1000);
344
+ num = 1000;
345
+ replies = malloc(sizeof(redisReply*)*num);
343
346
  t1 = usec();
344
- for (i = 0; i < 1000; i++) {
347
+ for (i = 0; i < num; i++) {
345
348
  replies[i] = redisCommand(c,"PING");
346
349
  assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
347
350
  }
348
351
  t2 = usec();
349
- for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
352
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
350
353
  free(replies);
351
- printf("\t(1000x PING: %.2fs)\n", (t2-t1)/1000000.0);
354
+ printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
352
355
 
353
- replies = malloc(sizeof(redisReply*)*1000);
356
+ replies = malloc(sizeof(redisReply*)*num);
354
357
  t1 = usec();
355
- for (i = 0; i < 1000; i++) {
358
+ for (i = 0; i < num; i++) {
356
359
  replies[i] = redisCommand(c,"LRANGE mylist 0 499");
357
360
  assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
358
361
  assert(replies[i] != NULL && replies[i]->elements == 500);
359
362
  }
360
363
  t2 = usec();
361
- for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
364
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
365
+ free(replies);
366
+ printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
367
+
368
+ num = 10000;
369
+ replies = malloc(sizeof(redisReply*)*num);
370
+ for (i = 0; i < num; i++)
371
+ redisAppendCommand(c,"PING");
372
+ t1 = usec();
373
+ for (i = 0; i < num; i++) {
374
+ assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
375
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
376
+ }
377
+ t2 = usec();
378
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
379
+ free(replies);
380
+ printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
381
+
382
+ replies = malloc(sizeof(redisReply*)*num);
383
+ for (i = 0; i < num; i++)
384
+ redisAppendCommand(c,"LRANGE mylist 0 499");
385
+ t1 = usec();
386
+ for (i = 0; i < num; i++) {
387
+ assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
388
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
389
+ assert(replies[i] != NULL && replies[i]->elements == 500);
390
+ }
391
+ t2 = usec();
392
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
362
393
  free(replies);
363
- printf("\t(1000x LRANGE with 500 elements: %.2fs)\n", (t2-t1)/1000000.0);
394
+ printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
364
395
  }
365
396
 
366
397
  static void cleanup(void) {