hiredis 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {