Skip to content
This repository
Browse code

Update hiredis to 0.9.2

  • Loading branch information...
commit 71b6f64f2a3c0ae1587b45dbc6c1e18609325c97 1 parent c4e93ca
Pieter Noordhuis pietern authored
46 deps/hiredis/Makefile
@@ -6,32 +6,35 @@ OBJ = net.o hiredis.o sds.o async.o
6 6 BINS = hiredis-example hiredis-test
7 7
8 8 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
9   -OPTIMIZATION?=-O2
  9 +OPTIMIZATION?=-O3
10 10 ifeq ($(uname_S),SunOS)
11   - CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6
12   - CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
  11 + CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
  12 + CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
  13 + LDFLAGS?=-L. -Wl,-R,.
13 14 DYLIBNAME?=libhiredis.so
14   - DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
  15 + DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
15 16 STLIBNAME?=libhiredis.a
16 17 STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
17 18 else ifeq ($(uname_S),Darwin)
18   - CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
19   - CCLINK?= -lm -pthread
20   - OBJARCH?= -arch i386 -arch x86_64
  19 + CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
  20 + CCLINK?=-lm -pthread
  21 + LDFLAGS?=-L. -Wl,-rpath,.
  22 + OBJARCH?=-arch i386 -arch x86_64
21 23 DYLIBNAME?=libhiredis.dylib
22 24 DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
23 25 STLIBNAME?=libhiredis.a
24 26 STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
25 27 else
26   - CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
27   - CCLINK?= -lm -pthread
  28 + CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
  29 + CCLINK?=-lm -pthread
  30 + LDFLAGS?=-L. -Wl,-rpath,.
28 31 DYLIBNAME?=libhiredis.so
29 32 DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
30 33 STLIBNAME?=libhiredis.a
31 34 STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
32 35 endif
33   -CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
34   -DEBUG?= -g -ggdb
  36 +CCOPT= $(CFLAGS) $(CCLINK)
  37 +DEBUG?= -g -ggdb
35 38
36 39 PREFIX?= /usr/local
37 40 INSTALL_INC= $(PREFIX)/include/hiredis
@@ -43,8 +46,6 @@ all: ${DYLIBNAME} ${BINS}
43 46 # Deps (use make dep to generate this)
44 47 net.o: net.c fmacros.h net.h
45 48 async.o: async.c async.h hiredis.h sds.h util.h
46   -example-libev.o: example-libev.c hiredis.h async.h adapters/libev.h
47   -example-libevent.o: example-libevent.c hiredis.h async.h adapters/libevent.h
48 49 example.o: example.c hiredis.h
49 50 hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
50 51 sds.o: sds.c sds.h
@@ -60,14 +61,23 @@ dynamic: ${DYLIBNAME}
60 61 static: ${STLIBNAME}
61 62
62 63 # Binaries:
63   -hiredis-example-libevent: example-libevent.o ${DYLIBNAME}
64   - $(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -levent -Wl,-rpath,. example-libevent.c
  64 +hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
  65 + $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c
  66 +
  67 +hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
  68 + $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c
65 69
66   -hiredis-example-libev: example-libev.o ${DYLIBNAME}
67   - $(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -lev -Wl,-rpath,. example-libev.c
  70 +ifndef AE_DIR
  71 +hiredis-example-ae:
  72 + @echo "Please specify AE_DIR (e.g. <redis repository>/src)"
  73 + @false
  74 +else
  75 +hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
  76 + $(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
  77 +endif
68 78
69 79 hiredis-%: %.o ${DYLIBNAME}
70   - $(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -Wl,-rpath,. $<
  80 + $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $<
71 81
72 82 test: hiredis-test
73 83 ./hiredis-test
63 deps/hiredis/README.md
Source Rendered
@@ -35,16 +35,18 @@ To consume the synchronous API, there are only a few function calls that need to
35 35
36 36 ### Connecting
37 37
38   -The function `redisConnect` is used to create a so-called `redisContext`. The context is where
39   -Hiredis holds state for a connection. The `redisContext` struct has an `error` field that is
40   -non-NULL when the connection is in an error state. It contains a string with a textual
41   -representation of the error. After trying to connect to Redis using `redisConnect` you should
42   -check the `error` field to see if establishing the connection was successful:
  38 +The function `redisConnect` is used to create a so-called `redisContext`. The
  39 +context is where Hiredis holds state for a connection. The `redisContext`
  40 +struct has an integer `err` field that is non-zero when an the connection is in
  41 +an error state. The field `errstr` will contain a string with a description of
  42 +the error. More information on errors can be found in the **Errors** section.
  43 +After trying to connect to Redis using `redisConnect` you should
  44 +check the `err` field to see if establishing the connection was successful:
43 45
44 46 redisContext *c = redisConnect("127.0.0.1", 6379);
45   - if (c->error != NULL) {
46   - printf("Error: %s\n", c->error);
47   - // handle error
  47 + if (c->err) {
  48 + printf("Error: %s\n", c->errstr);
  49 + // handle error
48 50 }
49 51
50 52 ### Sending commands
@@ -76,8 +78,8 @@ anywhere in an argument:
76 78 ### Using replies
77 79
78 80 The return value of `redisCommand` holds a reply when the command was
79   -successfully executed. When the return value is `NULL`, the `error` field
80   -in the context can be used to find out what was the cause of failure.
  81 +successfully executed. When an error occurs, the return value is `NULL` and
  82 +the `err` field in the context will be set (see section on **Errors**).
81 83 Once an error is returned the context cannot be reused and you should set up
82 84 a new connection.
83 85
@@ -166,7 +168,7 @@ to the `redisCommand` family, apart from not returning a reply:
166 168 After calling either function one or more times, `redisGetReply` can be used to receive the
167 169 subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
168 170 the latter means an error occurred while reading a reply. Just as with the other commands,
169   -the `error` field in the context can be used to find out what the cause of this error is.
  171 +the `err` field in the context can be used to find out what the cause of this error is.
170 172
171 173 The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
172 174 a single call to `write(2)`):
@@ -184,10 +186,35 @@ This API can also be used to implement a blocking subscriber:
184 186 reply = redisCommand(context,"SUBSCRIBE foo");
185 187 freeReplyObject(reply);
186 188 while(redisGetReply(context,&reply) == REDIS_OK) {
187   - // consume message
188   - freeReplyObject(reply);
  189 + // consume message
  190 + freeReplyObject(reply);
189 191 }
190 192
  193 +### Errors
  194 +
  195 +When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
  196 +returned. The `err` field inside the context will be non-zero and set to one of the
  197 +following constants:
  198 +
  199 +* **`REDIS_ERR_IO`**:
  200 + There was an I/O error while creating the connection, trying to write
  201 + to the socket or read from the socket. If you included `errno.h` in your
  202 + application, you can use the global `errno` variable to find out what is
  203 + wrong.
  204 +
  205 +* **`REDIS_ERR_EOF`**:
  206 + The server closed the connection which resulted in an empty read.
  207 +
  208 +* **`REDIS_ERR_PROTOCOL`**:
  209 + There was an error while parsing the protocol.
  210 +
  211 +* **`REDIS_ERR_OTHER`**:
  212 + Any other error. Currently, it is only used when a specified hostname to connect
  213 + to cannot be resolved.
  214 +
  215 +In every case, the `errstr` field in the context will be set to hold a string representation
  216 +of the error.
  217 +
191 218 ## Asynchronous API
192 219
193 220 Hiredis comes with an asynchronous API that works easily with any event library.
@@ -197,15 +224,15 @@ and [libevent](http://monkey.org/~provos/libevent/).
197 224 ### Connecting
198 225
199 226 The function `redisAsyncConnect` can be used to establish a non-blocking connection to
200   -Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `error` field
  227 +Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
201 228 should be checked after creation to see if there were errors creating the connection.
202 229 Because the connection that will be created is non-blocking, the kernel is not able to
203 230 instantly return if the specified host and port is able to accept a connection.
204 231
205 232 redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
206   - if (c->error != NULL) {
207   - printf("Error: %s\n", c->error);
208   - // handle error
  233 + if (c->err) {
  234 + printf("Error: %s\n", c->errstr);
  235 + // handle error
209 236 }
210 237
211 238 The asynchronous context can hold a disconnect callback function that is called when the
@@ -215,7 +242,7 @@ have the following prototype:
215 242 void(const redisAsyncContext *c, int status);
216 243
217 244 On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
218   -user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `error`
  245 +user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
219 246 field in the context can be accessed to find out the cause of the error.
220 247
221 248 The context object is always free'd after the disconnect callback fired. When a reconnect is needed,
95 deps/hiredis/adapters/ae.h
... ... @@ -0,0 +1,95 @@
  1 +#include <sys/types.h>
  2 +#include <ae.h>
  3 +#include "../hiredis.h"
  4 +#include "../async.h"
  5 +
  6 +typedef struct redisAeEvents {
  7 + redisAsyncContext *context;
  8 + aeEventLoop *loop;
  9 + int fd;
  10 + int reading, writing;
  11 +} redisAeEvents;
  12 +
  13 +void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
  14 + ((void)el); ((void)fd); ((void)mask);
  15 +
  16 + redisAeEvents *e = (redisAeEvents*)privdata;
  17 + redisAsyncHandleRead(e->context);
  18 +}
  19 +
  20 +void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
  21 + ((void)el); ((void)fd); ((void)mask);
  22 +
  23 + redisAeEvents *e = (redisAeEvents*)privdata;
  24 + redisAsyncHandleWrite(e->context);
  25 +}
  26 +
  27 +void redisAeAddRead(void *privdata) {
  28 + redisAeEvents *e = (redisAeEvents*)privdata;
  29 + aeEventLoop *loop = e->loop;
  30 + if (!e->reading) {
  31 + e->reading = 1;
  32 + aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
  33 + }
  34 +}
  35 +
  36 +void redisAeDelRead(void *privdata) {
  37 + redisAeEvents *e = (redisAeEvents*)privdata;
  38 + aeEventLoop *loop = e->loop;
  39 + if (e->reading) {
  40 + e->reading = 0;
  41 + aeDeleteFileEvent(loop,e->fd,AE_READABLE);
  42 + }
  43 +}
  44 +
  45 +void redisAeAddWrite(void *privdata) {
  46 + redisAeEvents *e = (redisAeEvents*)privdata;
  47 + aeEventLoop *loop = e->loop;
  48 + if (!e->writing) {
  49 + e->writing = 1;
  50 + aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
  51 + }
  52 +}
  53 +
  54 +void redisAeDelWrite(void *privdata) {
  55 + redisAeEvents *e = (redisAeEvents*)privdata;
  56 + aeEventLoop *loop = e->loop;
  57 + if (e->writing) {
  58 + e->writing = 0;
  59 + aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
  60 + }
  61 +}
  62 +
  63 +void redisAeCleanup(void *privdata) {
  64 + redisAeEvents *e = (redisAeEvents*)privdata;
  65 + redisAeDelRead(privdata);
  66 + redisAeDelWrite(privdata);
  67 + free(e);
  68 +}
  69 +
  70 +int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
  71 + redisContext *c = &(ac->c);
  72 + redisAeEvents *e;
  73 +
  74 + /* Nothing should be attached when something is already attached */
  75 + if (ac->_adapter_data != NULL)
  76 + return REDIS_ERR;
  77 +
  78 + /* Create container for context and r/w events */
  79 + e = (redisAeEvents*)malloc(sizeof(*e));
  80 + e->context = ac;
  81 + e->loop = loop;
  82 + e->fd = c->fd;
  83 + e->reading = e->writing = 0;
  84 +
  85 + /* Register functions to start/stop listening for events */
  86 + ac->evAddRead = redisAeAddRead;
  87 + ac->evDelRead = redisAeDelRead;
  88 + ac->evAddWrite = redisAeAddWrite;
  89 + ac->evDelWrite = redisAeDelWrite;
  90 + ac->evCleanup = redisAeCleanup;
  91 + ac->_adapter_data = e;
  92 +
  93 + return REDIS_OK;
  94 +}
  95 +
59 deps/hiredis/adapters/libev.h
@@ -10,69 +10,89 @@ typedef struct redisLibevEvents {
10 10 ev_io rev, wev;
11 11 } redisLibevEvents;
12 12
13   -void redisLibevReadEvent(struct ev_loop *loop, ev_io *watcher, int revents) {
14   - ((void)loop); ((void)revents);
15   - redisLibevEvents *e = watcher->data;
  13 +void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
  14 +#if EV_MULTIPLICITY
  15 + ((void)loop);
  16 +#endif
  17 + ((void)revents);
  18 +
  19 + redisLibevEvents *e = (redisLibevEvents*)watcher->data;
16 20 redisAsyncHandleRead(e->context);
17 21 }
18 22
19   -void redisLibevWriteEvent(struct ev_loop *loop, ev_io *watcher, int revents) {
20   - ((void)loop); ((void)revents);
21   - redisLibevEvents *e = watcher->data;
  23 +void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
  24 +#if EV_MULTIPLICITY
  25 + ((void)loop);
  26 +#endif
  27 + ((void)revents);
  28 +
  29 + redisLibevEvents *e = (redisLibevEvents*)watcher->data;
22 30 redisAsyncHandleWrite(e->context);
23 31 }
24 32
25 33 void redisLibevAddRead(void *privdata) {
26   - redisLibevEvents *e = privdata;
  34 + redisLibevEvents *e = (redisLibevEvents*)privdata;
  35 + struct ev_loop *loop = e->loop;
  36 + ((void)loop);
27 37 if (!e->reading) {
28 38 e->reading = 1;
29   - ev_io_start(e->loop,&e->rev);
  39 + ev_io_start(EV_A_ &e->rev);
30 40 }
31 41 }
32 42
33 43 void redisLibevDelRead(void *privdata) {
34   - redisLibevEvents *e = privdata;
  44 + redisLibevEvents *e = (redisLibevEvents*)privdata;
  45 + struct ev_loop *loop = e->loop;
  46 + ((void)loop);
35 47 if (e->reading) {
36 48 e->reading = 0;
37   - ev_io_stop(e->loop,&e->rev);
  49 + ev_io_stop(EV_A_ &e->rev);
38 50 }
39 51 }
40 52
41 53 void redisLibevAddWrite(void *privdata) {
42   - redisLibevEvents *e = privdata;
  54 + redisLibevEvents *e = (redisLibevEvents*)privdata;
  55 + struct ev_loop *loop = e->loop;
  56 + ((void)loop);
43 57 if (!e->writing) {
44 58 e->writing = 1;
45   - ev_io_start(e->loop,&e->wev);
  59 + ev_io_start(EV_A_ &e->wev);
46 60 }
47 61 }
48 62
49 63 void redisLibevDelWrite(void *privdata) {
50   - redisLibevEvents *e = privdata;
  64 + redisLibevEvents *e = (redisLibevEvents*)privdata;
  65 + struct ev_loop *loop = e->loop;
  66 + ((void)loop);
51 67 if (e->writing) {
52 68 e->writing = 0;
53   - ev_io_stop(e->loop,&e->wev);
  69 + ev_io_stop(EV_A_ &e->wev);
54 70 }
55 71 }
56 72
57 73 void redisLibevCleanup(void *privdata) {
58   - redisLibevEvents *e = privdata;
  74 + redisLibevEvents *e = (redisLibevEvents*)privdata;
59 75 redisLibevDelRead(privdata);
60 76 redisLibevDelWrite(privdata);
61 77 free(e);
62 78 }
63 79
64   -int redisLibevAttach(redisAsyncContext *ac, struct ev_loop *loop) {
  80 +int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
65 81 redisContext *c = &(ac->c);
66 82 redisLibevEvents *e;
67 83
68 84 /* Nothing should be attached when something is already attached */
69   - if (ac->data != NULL)
  85 + if (ac->_adapter_data != NULL)
70 86 return REDIS_ERR;
71 87
72 88 /* Create container for context and r/w events */
73   - e = malloc(sizeof(*e));
  89 + e = (redisLibevEvents*)malloc(sizeof(*e));
74 90 e->context = ac;
  91 +#if EV_MULTIPLICITY
75 92 e->loop = loop;
  93 +#else
  94 + e->loop = NULL;
  95 +#endif
76 96 e->reading = e->writing = 0;
77 97 e->rev.data = e;
78 98 e->wev.data = e;
@@ -83,10 +103,11 @@ int redisLibevAttach(redisAsyncContext *ac, struct ev_loop *loop) {
83 103 ac->evAddWrite = redisLibevAddWrite;
84 104 ac->evDelWrite = redisLibevDelWrite;
85 105 ac->evCleanup = redisLibevCleanup;
86   - ac->data = e;
  106 + ac->_adapter_data = e;
87 107
88 108 /* Initialize read/write events */
89 109 ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
90 110 ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
91 111 return REDIS_OK;
92 112 }
  113 +
20 deps/hiredis/adapters/libevent.h
@@ -10,38 +10,38 @@ typedef struct redisLibeventEvents {
10 10
11 11 void redisLibeventReadEvent(int fd, short event, void *arg) {
12 12 ((void)fd); ((void)event);
13   - redisLibeventEvents *e = arg;
  13 + redisLibeventEvents *e = (redisLibeventEvents*)arg;
14 14 redisAsyncHandleRead(e->context);
15 15 }
16 16
17 17 void redisLibeventWriteEvent(int fd, short event, void *arg) {
18 18 ((void)fd); ((void)event);
19   - redisLibeventEvents *e = arg;
  19 + redisLibeventEvents *e = (redisLibeventEvents*)arg;
20 20 redisAsyncHandleWrite(e->context);
21 21 }
22 22
23 23 void redisLibeventAddRead(void *privdata) {
24   - redisLibeventEvents *e = privdata;
  24 + redisLibeventEvents *e = (redisLibeventEvents*)privdata;
25 25 event_add(&e->rev,NULL);
26 26 }
27 27
28 28 void redisLibeventDelRead(void *privdata) {
29   - redisLibeventEvents *e = privdata;
  29 + redisLibeventEvents *e = (redisLibeventEvents*)privdata;
30 30 event_del(&e->rev);
31 31 }
32 32
33 33 void redisLibeventAddWrite(void *privdata) {
34   - redisLibeventEvents *e = privdata;
  34 + redisLibeventEvents *e = (redisLibeventEvents*)privdata;
35 35 event_add(&e->wev,NULL);
36 36 }
37 37
38 38 void redisLibeventDelWrite(void *privdata) {
39   - redisLibeventEvents *e = privdata;
  39 + redisLibeventEvents *e = (redisLibeventEvents*)privdata;
40 40 event_del(&e->wev);
41 41 }
42 42
43 43 void redisLibeventCleanup(void *privdata) {
44   - redisLibeventEvents *e = privdata;
  44 + redisLibeventEvents *e = (redisLibeventEvents*)privdata;
45 45 event_del(&e->rev);
46 46 event_del(&e->wev);
47 47 free(e);
@@ -52,11 +52,11 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
52 52 redisLibeventEvents *e;
53 53
54 54 /* Nothing should be attached when something is already attached */
55   - if (ac->data != NULL)
  55 + if (ac->_adapter_data != NULL)
56 56 return REDIS_ERR;
57 57
58 58 /* Create container for context and r/w events */
59   - e = malloc(sizeof(*e));
  59 + e = (redisLibeventEvents*)malloc(sizeof(*e));
60 60 e->context = ac;
61 61
62 62 /* Register functions to start/stop listening for events */
@@ -65,7 +65,7 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
65 65 ac->evAddWrite = redisLibeventAddWrite;
66 66 ac->evDelWrite = redisLibeventDelWrite;
67 67 ac->evCleanup = redisLibeventCleanup;
68   - ac->data = e;
  68 + ac->_adapter_data = e;
69 69
70 70 /* Initialize and install read/write events */
71 71 event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
55 deps/hiredis/async.c
... ... @@ -1,5 +1,7 @@
1 1 /*
2 2 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
  3 + * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
  4 + *
3 5 * All rights reserved.
4 6 *
5 7 * Redistribution and use in source and binary forms, with or without
@@ -38,8 +40,29 @@ void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
38 40
39 41 static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
40 42 redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
41   - /* Set all bytes in the async part of the context to 0 */
42   - memset(ac+sizeof(redisContext),0,sizeof(redisAsyncContext)-sizeof(redisContext));
  43 + c = &(ac->c);
  44 +
  45 + /* The regular connect functions will always set the flag REDIS_CONNECTED.
  46 + * For the async API, we want to wait until the first write event is
  47 + * received up before setting this flag, so reset it here. */
  48 + c->flags &= ~REDIS_CONNECTED;
  49 +
  50 + ac->err = 0;
  51 + ac->errstr = NULL;
  52 + ac->data = NULL;
  53 + ac->_adapter_data = NULL;
  54 +
  55 + ac->evAddRead = NULL;
  56 + ac->evDelRead = NULL;
  57 + ac->evAddWrite = NULL;
  58 + ac->evDelWrite = NULL;
  59 + ac->evCleanup = NULL;
  60 +
  61 + ac->onConnect = NULL;
  62 + ac->onDisconnect = NULL;
  63 +
  64 + ac->replies.head = NULL;
  65 + ac->replies.tail = NULL;
43 66 return ac;
44 67 }
45 68
@@ -70,6 +93,14 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
70 93 return redisSetReplyObjectFunctions(c,fn);
71 94 }
72 95
  96 +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
  97 + if (ac->onConnect == NULL) {
  98 + ac->onConnect = fn;
  99 + return REDIS_OK;
  100 + }
  101 + return REDIS_ERR;
  102 +}
  103 +
73 104 int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
74 105 if (ac->onDisconnect == NULL) {
75 106 ac->onDisconnect = fn;
@@ -153,7 +184,7 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
153 184 }
154 185
155 186 /* Signal event lib to clean up */
156   - if (ac->evCleanup) ac->evCleanup(ac->data);
  187 + if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
157 188
158 189 /* Execute callback with proper status */
159 190 if (ac->onDisconnect) ac->onDisconnect(ac,status);
@@ -206,7 +237,7 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
206 237 __redisAsyncDisconnect(ac);
207 238 } else {
208 239 /* Always re-schedule reads */
209   - if (ac->evAddRead) ac->evAddRead(ac->data);
  240 + if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
210 241 redisProcessCallbacks(ac);
211 242 }
212 243 }
@@ -220,13 +251,19 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
220 251 } else {
221 252 /* Continue writing when not done, stop writing otherwise */
222 253 if (!done) {
223   - if (ac->evAddWrite) ac->evAddWrite(ac->data);
  254 + if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
224 255 } else {
225   - if (ac->evDelWrite) ac->evDelWrite(ac->data);
  256 + if (ac->evDelWrite) ac->evDelWrite(ac->_adapter_data);
226 257 }
227 258
228   - /* Always schedule reads when something was written */
229   - if (ac->evAddRead) ac->evAddRead(ac->data);
  259 + /* Always schedule reads after writes */
  260 + if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
  261 +
  262 + /* Fire onConnect when this is the first write event. */
  263 + if (!(c->flags & REDIS_CONNECTED)) {
  264 + c->flags |= REDIS_CONNECTED;
  265 + if (ac->onConnect) ac->onConnect(ac);
  266 + }
230 267 }
231 268 }
232 269
@@ -249,7 +286,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
249 286 __redisPushCallback(&ac->replies,&cb);
250 287
251 288 /* Always schedule a write when the write buffer is non-empty */
252   - if (ac->evAddWrite) ac->evAddWrite(ac->data);
  289 + if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
253 290
254 291 return REDIS_OK;
255 292 }
22 deps/hiredis/async.h
@@ -31,6 +31,10 @@
31 31 #define __HIREDIS_ASYNC_H
32 32 #include "hiredis.h"
33 33
  34 +#ifdef __cplusplus
  35 +extern "C" {
  36 +#endif
  37 +
34 38 struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
35 39
36 40 /* Reply callback prototype and container */
@@ -46,8 +50,9 @@ typedef struct redisCallbackList {
46 50 redisCallback *head, *tail;
47 51 } redisCallbackList;
48 52
49   -/* Disconnect callback prototype */
  53 +/* Connection callback prototypes */
50 54 typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
  55 +typedef void (redisConnectCallback)(const struct redisAsyncContext*);
51 56
52 57 /* Context for an async connection to Redis */
53 58 typedef struct redisAsyncContext {
@@ -58,6 +63,12 @@ typedef struct redisAsyncContext {
58 63 int err;
59 64 char *errstr;
60 65
  66 + /* Not used by hiredis */
  67 + void *data;
  68 +
  69 + /* Used by the different event lib adapters to store their private data */
  70 + void *_adapter_data;
  71 +
61 72 /* Called when the library expects to start reading/writing.
62 73 * The supplied functions should be idempotent. */
63 74 void (*evAddRead)(void *privdata);
@@ -65,12 +76,14 @@ typedef struct redisAsyncContext {
65 76 void (*evAddWrite)(void *privdata);
66 77 void (*evDelWrite)(void *privdata);
67 78 void (*evCleanup)(void *privdata);
68   - void *data;
69 79
70 80 /* Called when either the connection is terminated due to an error or per
71 81 * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
72 82 redisDisconnectCallback *onDisconnect;
73 83
  84 + /* Called when the first write event was received. */
  85 + redisConnectCallback *onConnect;
  86 +
74 87 /* Reply callbacks */
75 88 redisCallbackList replies;
76 89 } redisAsyncContext;
@@ -78,6 +91,7 @@ typedef struct redisAsyncContext {
78 91 /* Functions that proxy to hiredis */
79 92 redisAsyncContext *redisAsyncConnect(const char *ip, int port);
80 93 int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
  94 +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
81 95 int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
82 96 void redisAsyncDisconnect(redisAsyncContext *ac);
83 97
@@ -91,4 +105,8 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
91 105 int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
92 106 int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
93 107
  108 +#ifdef __cplusplus
  109 +}
  110 +#endif
  111 +
94 112 #endif
53 deps/hiredis/example-ae.c
... ... @@ -0,0 +1,53 @@
  1 +#include <stdio.h>
  2 +#include <stdlib.h>
  3 +#include <string.h>
  4 +#include <signal.h>
  5 +#include "hiredis.h"
  6 +#include "async.h"
  7 +#include "adapters/ae.h"
  8 +
  9 +/* Put event loop in the global scope, so it can be explicitly stopped */
  10 +static aeEventLoop *loop;
  11 +
  12 +void getCallback(redisAsyncContext *c, void *r, void *privdata) {
  13 + redisReply *reply = r;
  14 + if (reply == NULL) return;
  15 + printf("argv[%s]: %s\n", (char*)privdata, reply->str);
  16 +
  17 + /* Disconnect after receiving the reply to GET */
  18 + redisAsyncDisconnect(c);
  19 +}
  20 +
  21 +void connectCallback(const redisAsyncContext *c) {
  22 + ((void)c);
  23 + printf("connected...\n");
  24 +}
  25 +
  26 +void disconnectCallback(const redisAsyncContext *c, int status) {
  27 + if (status != REDIS_OK) {
  28 + printf("Error: %s\n", c->errstr);
  29 + }
  30 + printf("disconnected...\n");
  31 + aeStop(loop);
  32 +}
  33 +
  34 +int main (int argc, char **argv) {
  35 + signal(SIGPIPE, SIG_IGN);
  36 +
  37 + redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
  38 + if (c->err) {
  39 + /* Let *c leak for now... */
  40 + printf("Error: %s\n", c->errstr);
  41 + return 1;
  42 + }
  43 +
  44 + loop = aeCreateEventLoop();
  45 + redisAeAttach(loop, c);
  46 + redisAsyncSetConnectCallback(c,connectCallback);
  47 + redisAsyncSetDisconnectCallback(c,disconnectCallback);
  48 + redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
  49 + redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
  50 + aeMain(loop);
  51 + return 0;
  52 +}
  53 +
12 deps/hiredis/example-libev.c
@@ -15,15 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
15 15 redisAsyncDisconnect(c);
16 16 }
17 17
  18 +void connectCallback(const redisAsyncContext *c) {
  19 + ((void)c);
  20 + printf("connected...\n");
  21 +}
  22 +
18 23 void disconnectCallback(const redisAsyncContext *c, int status) {
19 24 if (status != REDIS_OK) {
20 25 printf("Error: %s\n", c->errstr);
21 26 }
  27 + printf("disconnected...\n");
22 28 }
23 29
24 30 int main (int argc, char **argv) {
25 31 signal(SIGPIPE, SIG_IGN);
26   - struct ev_loop *loop = ev_default_loop(0);
27 32
28 33 redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
29 34 if (c->err) {
@@ -32,10 +37,11 @@ int main (int argc, char **argv) {
32 37 return 1;
33 38 }
34 39
35   - redisLibevAttach(c,loop);
  40 + redisLibevAttach(EV_DEFAULT_ c);
  41 + redisAsyncSetConnectCallback(c,connectCallback);
36 42 redisAsyncSetDisconnectCallback(c,disconnectCallback);
37 43 redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
38 44 redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
39   - ev_loop(loop, 0);
  45 + ev_loop(EV_DEFAULT_ 0);
40 46 return 0;
41 47 }
7 deps/hiredis/example-libevent.c
@@ -15,10 +15,16 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
15 15 redisAsyncDisconnect(c);
16 16 }
17 17
  18 +void connectCallback(const redisAsyncContext *c) {
  19 + ((void)c);
  20 + printf("connected...\n");
  21 +}
  22 +
18 23 void disconnectCallback(const redisAsyncContext *c, int status) {
19 24 if (status != REDIS_OK) {
20 25 printf("Error: %s\n", c->errstr);
21 26 }
  27 + printf("disconnected...\n");
22 28 }
23 29
24 30 int main (int argc, char **argv) {
@@ -33,6 +39,7 @@ int main (int argc, char **argv) {
33 39 }
34 40
35 41 redisLibeventAttach(c,base);
  42 + redisAsyncSetConnectCallback(c,connectCallback);
36 43 redisAsyncSetDisconnectCallback(c,disconnectCallback);
37 44 redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
38 45 redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
2  deps/hiredis/example.c
@@ -17,7 +17,7 @@ int main(void) {
17 17
18 18 /* PING server */
19 19 reply = redisCommand(c,"PING");
20   - printf("PONG: %s\n", reply->str);
  20 + printf("PING: %s\n", reply->str);
21 21 freeReplyObject(reply);
22 22
23 23 /* Set a key */
241 deps/hiredis/hiredis.c
... ... @@ -1,5 +1,7 @@
1 1 /*
2 2 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
  3 + * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
  4 + *
3 5 * All rights reserved.
4 6 *
5 7 * Redistribution and use in source and binary forms, with or without
@@ -32,6 +34,7 @@
32 34 #include <unistd.h>
33 35 #include <assert.h>
34 36 #include <errno.h>
  37 +#include <ctype.h>
35 38
36 39 #include "hiredis.h"
37 40 #include "net.h"
@@ -44,10 +47,12 @@ typedef struct redisReader {
44 47 void *reply; /* holds temporary reply */
45 48
46 49 sds buf; /* read buffer */
47   - unsigned int pos; /* buffer cursor */
  50 + size_t pos; /* buffer cursor */
  51 + size_t len; /* buffer length */
48 52
49 53 redisReadTask rstack[3]; /* stack of read tasks */
50 54 int ridx; /* index of stack */
  55 + void *privdata; /* user-settable arbitrary field */
51 56 } redisReader;
52 57
53 58 static redisReply *createReplyObject(int type);
@@ -68,7 +73,7 @@ static redisReplyObjectFunctions defaultFunctions = {
68 73
69 74 /* Create a reply object */
70 75 static redisReply *createReplyObject(int type) {
71   - redisReply *r = calloc(sizeof(*r),1);
  76 + redisReply *r = malloc(sizeof(*r));
72 77
73 78 if (!r) redisOOM();
74 79 r->type = type;
@@ -88,9 +93,10 @@ void freeReplyObject(void *reply) {
88 93 if (r->element[j]) freeReplyObject(r->element[j]);
89 94 free(r->element);
90 95 break;
91   - default:
92   - if (r->str != NULL)
93   - free(r->str);
  96 + case REDIS_REPLY_ERROR:
  97 + case REDIS_REPLY_STATUS:
  98 + case REDIS_REPLY_STRING:
  99 + free(r->str);
94 100 break;
95 101 }
96 102 free(r);
@@ -111,7 +117,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
111 117 r->len = len;
112 118
113 119 if (task->parent) {
114   - redisReply *parent = task->parent;
  120 + redisReply *parent = task->parent->obj;
115 121 assert(parent->type == REDIS_REPLY_ARRAY);
116 122 parent->element[task->idx] = r;
117 123 }
@@ -124,7 +130,7 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
124 130 if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
125 131 redisOOM();
126 132 if (task->parent) {
127   - redisReply *parent = task->parent;
  133 + redisReply *parent = task->parent->obj;
128 134 assert(parent->type == REDIS_REPLY_ARRAY);
129 135 parent->element[task->idx] = r;
130 136 }
@@ -135,7 +141,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
135 141 redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
136 142 r->integer = value;
137 143 if (task->parent) {
138   - redisReply *parent = task->parent;
  144 + redisReply *parent = task->parent->obj;
139 145 assert(parent->type == REDIS_REPLY_ARRAY);
140 146 parent->element[task->idx] = r;
141 147 }
@@ -145,7 +151,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
145 151 static void *createNilObject(const redisReadTask *task) {
146 152 redisReply *r = createReplyObject(REDIS_REPLY_NIL);
147 153 if (task->parent) {
148   - redisReply *parent = task->parent;
  154 + redisReply *parent = task->parent->obj;
149 155 assert(parent->type == REDIS_REPLY_ARRAY);
150 156 parent->element[task->idx] = r;
151 157 }
@@ -154,7 +160,7 @@ static void *createNilObject(const redisReadTask *task) {
154 160
155 161 static char *readBytes(redisReader *r, unsigned int bytes) {
156 162 char *p;
157   - if (sdslen(r->buf)-r->pos >= bytes) {
  163 + if (r->len-r->pos >= bytes) {
158 164 p = r->buf+r->pos;
159 165 r->pos += bytes;
160 166 return p;
@@ -162,20 +168,60 @@ static char *readBytes(redisReader *r, unsigned int bytes) {
162 168 return NULL;
163 169 }
164 170
165   -static char *seekNewline(char *s) {
166   - /* Find pointer to \r\n without strstr */
167   - while (s != NULL) {
168   - s = strchr(s,'\r');
169   - if (s != NULL) {
170   - if (s[1] == '\n')
171   - break;
172   - else
173   - s++;
  171 +/* Find pointer to \r\n. */
  172 +static char *seekNewline(char *s, size_t len) {
  173 + int pos = 0;
  174 + int _len = len-1;
  175 +
  176 + /* Position should be < len-1 because the character at "pos" should be
  177 + * followed by a \n. Note that strchr cannot be used because it doesn't
  178 + * allow to search a limited length and the buffer that is being searched
  179 + * might not have a trailing NULL character. */
  180 + while (pos < _len) {
  181 + while(pos < _len && s[pos] != '\r') pos++;
  182 + if (s[pos] != '\r') {
  183 + /* Not found. */
  184 + return NULL;
174 185 } else {
175   - break;
  186 + if (s[pos+1] == '\n') {
  187 + /* Found. */
  188 + return s+pos;
  189 + } else {
  190 + /* Continue searching. */
  191 + pos++;
  192 + }
176 193 }
177 194 }
178   - return s;
  195 + return NULL;
  196 +}
  197 +
  198 +/* Read a long long value starting at *s, under the assumption that it will be
  199 + * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
  200 +static long long readLongLong(char *s) {
  201 + long long v = 0;
  202 + int dec, mult = 1;
  203 + char c;
  204 +
  205 + if (*s == '-') {
  206 + mult = -1;
  207 + s++;
  208 + } else if (*s == '+') {
  209 + mult = 1;
  210 + s++;
  211 + }
  212 +
  213 + while ((c = *(s++)) != '\r') {
  214 + dec = c - '0';
  215 + if (dec >= 0 && dec < 10) {
  216 + v *= 10;
  217 + v += dec;
  218 + } else {
  219 + /* Should not happen... */
  220 + return -1;
  221 + }
  222 + }
  223 +
  224 + return mult*v;
179 225 }
180 226
181 227 static char *readLine(redisReader *r, int *_len) {
@@ -183,7 +229,7 @@ static char *readLine(redisReader *r, int *_len) {
183 229 int len;
184 230
185 231 p = r->buf+r->pos;
186   - s = seekNewline(p);
  232 + s = seekNewline(p,(r->len-r->pos));
187 233 if (s != NULL) {
188 234 len = s-(r->buf+r->pos);
189 235 r->pos += len+2; /* skip \r\n */
@@ -227,7 +273,7 @@ static int processLineItem(redisReader *r) {
227 273 if ((p = readLine(r,&len)) != NULL) {
228 274 if (r->fn) {
229 275 if (cur->type == REDIS_REPLY_INTEGER) {
230   - obj = r->fn->createInteger(cur,strtoll(p,NULL,10));
  276 + obj = r->fn->createInteger(cur,readLongLong(p));
231 277 } else {
232 278 obj = r->fn->createString(cur,p,len);
233 279 }
@@ -235,9 +281,8 @@ static int processLineItem(redisReader *r) {
235 281 obj = (void*)(size_t)(cur->type);
236 282 }
237 283
238   - /* If there is no root yet, register this object as root. */
239   - if (r->reply == NULL)
240   - r->reply = obj;
  284 + /* Set reply if this is the root object. */
  285 + if (r->ridx == 0) r->reply = obj;
241 286 moveToNextTask(r);
242 287 return 0;
243 288 }
@@ -250,32 +295,36 @@ static int processBulkItem(redisReader *r) {
250 295 char *p, *s;
251 296 long len;
252 297 unsigned long bytelen;
  298 + int success = 0;
253 299
254 300 p = r->buf+r->pos;
255   - s = seekNewline(p);
  301 + s = seekNewline(p,r->len-r->pos);
256 302 if (s != NULL) {
257 303 p = r->buf+r->pos;
258 304 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
259   - len = strtol(p,NULL,10);
  305 + len = readLongLong(p);
260 306
261 307 if (len < 0) {
262 308 /* The nil object can always be created. */
263 309 obj = r->fn ? r->fn->createNil(cur) :
264 310 (void*)REDIS_REPLY_NIL;
  311 + success = 1;
265 312 } else {
266 313 /* Only continue when the buffer contains the entire bulk item. */
267 314 bytelen += len+2; /* include \r\n */
268   - if (r->pos+bytelen <= sdslen(r->buf)) {
  315 + if (r->pos+bytelen <= r->len) {
269 316 obj = r->fn ? r->fn->createString(cur,s+2,len) :
270 317 (void*)REDIS_REPLY_STRING;
  318 + success = 1;
271 319 }
272 320 }
273 321
274 322 /* Proceed when obj was created. */
275   - if (obj != NULL) {
  323 + if (success) {
276 324 r->pos += bytelen;
277   - if (r->reply == NULL)
278   - r->reply = obj;
  325 +
  326 + /* Set reply if this is the root object. */
  327 + if (r->ridx == 0) r->reply = obj;
279 328 moveToNextTask(r);
280 329 return 0;
281 330 }
@@ -288,9 +337,19 @@ static int processMultiBulkItem(redisReader *r) {
288 337 void *obj;
289 338 char *p;
290 339 long elements;
  340 + int root = 0;
  341 +
  342 + /* Set error for nested multi bulks with depth > 1 */
  343 + if (r->ridx == 2) {
  344 + redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
  345 + "No support for nested multi bulk replies with depth > 1"));
  346 + return -1;
  347 + }
291 348
292 349 if ((p = readLine(r,NULL)) != NULL) {
293   - elements = strtol(p,NULL,10);
  350 + elements = readLongLong(p);
  351 + root = (r->ridx == 0);
  352 +
294 353 if (elements == -1) {
295 354 obj = r->fn ? r->fn->createNil(cur) :
296 355 (void*)REDIS_REPLY_NIL;
@@ -302,19 +361,21 @@ static int processMultiBulkItem(redisReader *r) {
302 361 /* Modify task stack when there are more than 0 elements. */
303 362 if (elements > 0) {
304 363 cur->elements = elements;
  364 + cur->obj = obj;
305 365 r->ridx++;
306 366 r->rstack[r->ridx].type = -1;
307 367 r->rstack[r->ridx].elements = -1;
308   - r->rstack[r->ridx].parent = obj;
309 368 r->rstack[r->ridx].idx = 0;
  369 + r->rstack[r->ridx].obj = NULL;
  370 + r->rstack[r->ridx].parent = cur;
  371 + r->rstack[r->ridx].privdata = r->privdata;
310 372 } else {
311 373 moveToNextTask(r);
312 374 }
313 375 }
314 376
315   - /* Object was created, so we can always continue. */
316   - if (r->reply == NULL)
317   - r->reply = obj;
  377 + /* Set reply if this is the root object. */
  378 + if (root) r->reply = obj;
318 379 return 0;
319 380 }
320 381 return -1;
@@ -347,7 +408,7 @@ static int processItem(redisReader *r) {
347 408 default:
348 409 byte = sdscatrepr(sdsempty(),p,1);
349 410 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
350   - "protocol error, got %s as reply type byte", byte));
  411 + "Protocol error, got %s as reply type byte", byte));
351 412 sdsfree(byte);
352 413 return -1;
353 414 }
@@ -368,8 +429,7 @@ static int processItem(redisReader *r) {
368 429 case REDIS_REPLY_ARRAY:
369 430 return processMultiBulkItem(r);
370 431 default:
371   - redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
372   - "unknown item type '%d'", cur->type));
  432 + assert(NULL);
373 433 return -1;
374 434 }
375 435 }
@@ -394,6 +454,17 @@ int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFuncti
394 454 return REDIS_ERR;
395 455 }
396 456
  457 +/* Set the private data field that is used in the read tasks. This argument can
  458 + * be used to curry arbitrary data to the custom reply object functions. */
  459 +int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
  460 + redisReader *r = reader;
  461 + if (r->reply == NULL) {
  462 + r->privdata = privdata;
  463 + return REDIS_OK;
  464 + }
  465 + return REDIS_ERR;
  466 +}
  467 +
397 468 /* External libraries wrapping hiredis might need access to the temporary
398 469 * variable while the reply is built up. When the reader contains an
399 470 * object in between receiving some bytes to parse, this object might
@@ -437,8 +508,10 @@ void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
437 508 redisReader *r = reader;
438 509
439 510 /* Copy the provided buffer. */
440   - if (buf != NULL && len >= 1)
  511 + if (buf != NULL && len >= 1) {
441 512 r->buf = sdscatlen(r->buf,buf,len);
  513 + r->len = sdslen(r->buf);
  514 + }
442 515 }
443 516
444 517 int redisReplyReaderGetReply(void *reader, void **reply) {
@@ -446,15 +519,17 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
446 519 if (reply != NULL) *reply = NULL;
447 520
448 521 /* When the buffer is empty, there will never be a reply. */
449   - if (sdslen(r->buf) == 0)
  522 + if (r->len == 0)
450 523 return REDIS_OK;
451 524
452 525 /* Set first item to process when the stack is empty. */
453 526 if (r->ridx == -1) {
454 527 r->rstack[0].type = -1;
455 528 r->rstack[0].elements = -1;
456   - r->rstack[0].parent = NULL;
457 529 r->rstack[0].idx = -1;
  530 + r->rstack[0].obj = NULL;
  531 + r->rstack[0].parent = NULL;
  532 + r->rstack[0].privdata = r->privdata;
458 533 r->ridx = 0;
459 534 }
460 535
@@ -465,14 +540,15 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
465 540
466 541 /* Discard the consumed part of the buffer. */
467 542 if (r->pos > 0) {
468   - if (r->pos == sdslen(r->buf)) {
  543 + if (r->pos == r->len) {
469 544 /* sdsrange has a quirck on this edge case. */
470 545 sdsfree(r->buf);
471 546 r->buf = sdsempty();
472 547 } else {
473   - r->buf = sdsrange(r->buf,r->pos,sdslen(r->buf));
  548 + r->buf = sdsrange(r->buf,r->pos,r->len);
474 549 }
475 550 r->pos = 0;
  551 + r->len = sdslen(r->buf);
476 552 }
477 553
478 554 /* Emit a reply when there is one. */
@@ -481,7 +557,7 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
481 557 r->reply = NULL;
482