methodmissing_hiredis 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ /* Hash table implementation.
2
+ *
3
+ * This file implements in memory hash tables with insert/del/replace/find/
4
+ * get-random-element operations. Hash tables will auto resize if needed
5
+ * tables of power of two in size are used, collisions are handled by
6
+ * chaining. See the source code for more information... :)
7
+ *
8
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
9
+ * All rights reserved.
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without
12
+ * modification, are permitted provided that the following conditions are met:
13
+ *
14
+ * * Redistributions of source code must retain the above copyright notice,
15
+ * this list of conditions and the following disclaimer.
16
+ * * Redistributions in binary form must reproduce the above copyright
17
+ * notice, this list of conditions and the following disclaimer in the
18
+ * documentation and/or other materials provided with the distribution.
19
+ * * Neither the name of Redis nor the names of its contributors may be used
20
+ * to endorse or promote products derived from this software without
21
+ * specific prior written permission.
22
+ *
23
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ * POSSIBILITY OF SUCH DAMAGE.
34
+ */
35
+
36
+ #ifndef __DICT_H
37
+ #define __DICT_H
38
+
39
+ #define DICT_OK 0
40
+ #define DICT_ERR 1
41
+
42
+ /* Unused arguments generate annoying warnings... */
43
+ #define DICT_NOTUSED(V) ((void) V)
44
+
45
+ typedef struct dictEntry {
46
+ void *key;
47
+ void *val;
48
+ struct dictEntry *next;
49
+ } dictEntry;
50
+
51
+ typedef struct dictType {
52
+ unsigned int (*hashFunction)(const void *key);
53
+ void *(*keyDup)(void *privdata, const void *key);
54
+ void *(*valDup)(void *privdata, const void *obj);
55
+ int (*keyCompare)(void *privdata, const void *key1, const void *key2);
56
+ void (*keyDestructor)(void *privdata, void *key);
57
+ void (*valDestructor)(void *privdata, void *obj);
58
+ } dictType;
59
+
60
+ typedef struct dict {
61
+ dictEntry **table;
62
+ dictType *type;
63
+ unsigned long size;
64
+ unsigned long sizemask;
65
+ unsigned long used;
66
+ void *privdata;
67
+ } dict;
68
+
69
+ typedef struct dictIterator {
70
+ dict *ht;
71
+ int index;
72
+ dictEntry *entry, *nextEntry;
73
+ } dictIterator;
74
+
75
+ /* This is the initial size of every hash table */
76
+ #define DICT_HT_INITIAL_SIZE 4
77
+
78
+ /* ------------------------------- Macros ------------------------------------*/
79
+ #define dictFreeEntryVal(ht, entry) \
80
+ if ((ht)->type->valDestructor) \
81
+ (ht)->type->valDestructor((ht)->privdata, (entry)->val)
82
+
83
+ #define dictSetHashVal(ht, entry, _val_) do { \
84
+ if ((ht)->type->valDup) \
85
+ entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
86
+ else \
87
+ entry->val = (_val_); \
88
+ } while(0)
89
+
90
+ #define dictFreeEntryKey(ht, entry) \
91
+ if ((ht)->type->keyDestructor) \
92
+ (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
93
+
94
+ #define dictSetHashKey(ht, entry, _key_) do { \
95
+ if ((ht)->type->keyDup) \
96
+ entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
97
+ else \
98
+ entry->key = (_key_); \
99
+ } while(0)
100
+
101
+ #define dictCompareHashKeys(ht, key1, key2) \
102
+ (((ht)->type->keyCompare) ? \
103
+ (ht)->type->keyCompare((ht)->privdata, key1, key2) : \
104
+ (key1) == (key2))
105
+
106
+ #define dictHashKey(ht, key) (ht)->type->hashFunction(key)
107
+
108
+ #define dictGetEntryKey(he) ((he)->key)
109
+ #define dictGetEntryVal(he) ((he)->val)
110
+ #define dictSlots(ht) ((ht)->size)
111
+ #define dictSize(ht) ((ht)->used)
112
+
113
+ /* API */
114
+ static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
115
+ static dict *dictCreate(dictType *type, void *privDataPtr);
116
+ static int dictExpand(dict *ht, unsigned long size);
117
+ static int dictAdd(dict *ht, void *key, void *val);
118
+ static int dictReplace(dict *ht, void *key, void *val);
119
+ static int dictDelete(dict *ht, const void *key);
120
+ static void dictRelease(dict *ht);
121
+ static dictEntry * dictFind(dict *ht, const void *key);
122
+ static dictIterator *dictGetIterator(dict *ht);
123
+ static dictEntry *dictNext(dictIterator *iter);
124
+ static void dictReleaseIterator(dictIterator *iter);
125
+
126
+ #endif /* __DICT_H */
@@ -0,0 +1,16 @@
1
+ #ifndef __HIREDIS_FMACRO_H
2
+ #define __HIREDIS_FMACRO_H
3
+
4
+ #if !defined(_BSD_SOURCE)
5
+ #define _BSD_SOURCE
6
+ #endif
7
+
8
+ #if defined(__sun__)
9
+ #define _POSIX_C_SOURCE 200112L
10
+ #elif defined(__linux__)
11
+ #define _XOPEN_SOURCE 600
12
+ #else
13
+ #define _XOPEN_SOURCE
14
+ #endif
15
+
16
+ #endif
@@ -0,0 +1,1315 @@
1
+ /*
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
+ *
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * * Redistributions in binary form must reproduce the above copyright
13
+ * notice, this list of conditions and the following disclaimer in the
14
+ * documentation and/or other materials provided with the distribution.
15
+ * * Neither the name of Redis nor the names of its contributors may be used
16
+ * to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ * POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+
32
+ #include "fmacros.h"
33
+ #include <string.h>
34
+ #include <stdlib.h>
35
+ #include <unistd.h>
36
+ #include <assert.h>
37
+ #include <errno.h>
38
+ #include <ctype.h>
39
+
40
+ #include "hiredis.h"
41
+ #include "net.h"
42
+ #include "sds.h"
43
+
44
+ static redisReply *createReplyObject(int type);
45
+ static void *createStringObject(const redisReadTask *task, char *str, size_t len);
46
+ static void *createArrayObject(const redisReadTask *task, int elements);
47
+ static void *createIntegerObject(const redisReadTask *task, long long value);
48
+ static void *createNilObject(const redisReadTask *task);
49
+
50
+ /* Default set of functions to build the reply. Keep in mind that such a
51
+ * function returning NULL is interpreted as OOM. */
52
+ static redisReplyObjectFunctions defaultFunctions = {
53
+ createStringObject,
54
+ createArrayObject,
55
+ createIntegerObject,
56
+ createNilObject,
57
+ freeReplyObject
58
+ };
59
+
60
+ /* Create a reply object */
61
+ static redisReply *createReplyObject(int type) {
62
+ redisReply *r = calloc(1,sizeof(*r));
63
+
64
+ if (r == NULL)
65
+ return NULL;
66
+
67
+ r->type = type;
68
+ return r;
69
+ }
70
+
71
+ /* Free a reply object */
72
+ void freeReplyObject(void *reply) {
73
+ redisReply *r = reply;
74
+ size_t j;
75
+
76
+ switch(r->type) {
77
+ case REDIS_REPLY_INTEGER:
78
+ break; /* Nothing to free */
79
+ case REDIS_REPLY_ARRAY:
80
+ if (r->element != NULL) {
81
+ for (j = 0; j < r->elements; j++)
82
+ if (r->element[j] != NULL)
83
+ freeReplyObject(r->element[j]);
84
+ free(r->element);
85
+ }
86
+ break;
87
+ case REDIS_REPLY_ERROR:
88
+ case REDIS_REPLY_STATUS:
89
+ case REDIS_REPLY_STRING:
90
+ if (r->str != NULL)
91
+ free(r->str);
92
+ break;
93
+ }
94
+ free(r);
95
+ }
96
+
97
+ static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
98
+ redisReply *r, *parent;
99
+ char *buf;
100
+
101
+ r = createReplyObject(task->type);
102
+ if (r == NULL)
103
+ return NULL;
104
+
105
+ buf = malloc(len+1);
106
+ if (buf == NULL) {
107
+ freeReplyObject(r);
108
+ return NULL;
109
+ }
110
+
111
+ assert(task->type == REDIS_REPLY_ERROR ||
112
+ task->type == REDIS_REPLY_STATUS ||
113
+ task->type == REDIS_REPLY_STRING);
114
+
115
+ /* Copy string value */
116
+ memcpy(buf,str,len);
117
+ buf[len] = '\0';
118
+ r->str = buf;
119
+ r->len = len;
120
+
121
+ if (task->parent) {
122
+ parent = task->parent->obj;
123
+ assert(parent->type == REDIS_REPLY_ARRAY);
124
+ parent->element[task->idx] = r;
125
+ }
126
+ return r;
127
+ }
128
+
129
+ static void *createArrayObject(const redisReadTask *task, int elements) {
130
+ redisReply *r, *parent;
131
+
132
+ r = createReplyObject(REDIS_REPLY_ARRAY);
133
+ if (r == NULL)
134
+ return NULL;
135
+
136
+ if (elements > 0) {
137
+ r->element = calloc(elements,sizeof(redisReply*));
138
+ if (r->element == NULL) {
139
+ freeReplyObject(r);
140
+ return NULL;
141
+ }
142
+ }
143
+
144
+ r->elements = elements;
145
+
146
+ if (task->parent) {
147
+ parent = task->parent->obj;
148
+ assert(parent->type == REDIS_REPLY_ARRAY);
149
+ parent->element[task->idx] = r;
150
+ }
151
+ return r;
152
+ }
153
+
154
+ static void *createIntegerObject(const redisReadTask *task, long long value) {
155
+ redisReply *r, *parent;
156
+
157
+ r = createReplyObject(REDIS_REPLY_INTEGER);
158
+ if (r == NULL)
159
+ return NULL;
160
+
161
+ r->integer = value;
162
+
163
+ if (task->parent) {
164
+ parent = task->parent->obj;
165
+ assert(parent->type == REDIS_REPLY_ARRAY);
166
+ parent->element[task->idx] = r;
167
+ }
168
+ return r;
169
+ }
170
+
171
+ static void *createNilObject(const redisReadTask *task) {
172
+ redisReply *r, *parent;
173
+
174
+ r = createReplyObject(REDIS_REPLY_NIL);
175
+ if (r == NULL)
176
+ return NULL;
177
+
178
+ if (task->parent) {
179
+ parent = task->parent->obj;
180
+ assert(parent->type == REDIS_REPLY_ARRAY);
181
+ parent->element[task->idx] = r;
182
+ }
183
+ return r;
184
+ }
185
+
186
+ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
187
+ size_t len;
188
+
189
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
190
+ r->fn->freeObject(r->reply);
191
+ r->reply = NULL;
192
+ }
193
+
194
+ /* Clear input buffer on errors. */
195
+ if (r->buf != NULL) {
196
+ sdsfree(r->buf);
197
+ r->buf = NULL;
198
+ r->pos = r->len = 0;
199
+ }
200
+
201
+ /* Reset task stack. */
202
+ r->ridx = -1;
203
+
204
+ /* Set error. */
205
+ r->err = type;
206
+ len = strlen(str);
207
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
208
+ memcpy(r->errstr,str,len);
209
+ r->errstr[len] = '\0';
210
+ }
211
+
212
+ static size_t chrtos(char *buf, size_t size, char byte) {
213
+ size_t len = 0;
214
+
215
+ switch(byte) {
216
+ case '\\':
217
+ case '"':
218
+ len = snprintf(buf,size,"\"\\%c\"",byte);
219
+ break;
220
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
221
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
222
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
223
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
224
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
225
+ default:
226
+ if (isprint(byte))
227
+ len = snprintf(buf,size,"\"%c\"",byte);
228
+ else
229
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
230
+ break;
231
+ }
232
+
233
+ return len;
234
+ }
235
+
236
+ static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
237
+ char cbuf[8], sbuf[128];
238
+
239
+ chrtos(cbuf,sizeof(cbuf),byte);
240
+ snprintf(sbuf,sizeof(sbuf),
241
+ "Protocol error, got %s as reply type byte", cbuf);
242
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
243
+ }
244
+
245
+ static void __redisReaderSetErrorOOM(redisReader *r) {
246
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
247
+ }
248
+
249
+ static char *readBytes(redisReader *r, unsigned int bytes) {
250
+ char *p;
251
+ if (r->len-r->pos >= bytes) {
252
+ p = r->buf+r->pos;
253
+ r->pos += bytes;
254
+ return p;
255
+ }
256
+ return NULL;
257
+ }
258
+
259
+ /* Find pointer to \r\n. */
260
+ static char *seekNewline(char *s, size_t len) {
261
+ int pos = 0;
262
+ int _len = len-1;
263
+
264
+ /* Position should be < len-1 because the character at "pos" should be
265
+ * followed by a \n. Note that strchr cannot be used because it doesn't
266
+ * allow to search a limited length and the buffer that is being searched
267
+ * might not have a trailing NULL character. */
268
+ while (pos < _len) {
269
+ while(pos < _len && s[pos] != '\r') pos++;
270
+ if (s[pos] != '\r') {
271
+ /* Not found. */
272
+ return NULL;
273
+ } else {
274
+ if (s[pos+1] == '\n') {
275
+ /* Found. */
276
+ return s+pos;
277
+ } else {
278
+ /* Continue searching. */
279
+ pos++;
280
+ }
281
+ }
282
+ }
283
+ return NULL;
284
+ }
285
+
286
+ /* Read a long long value starting at *s, under the assumption that it will be
287
+ * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
288
+ static long long readLongLong(char *s) {
289
+ long long v = 0;
290
+ int dec, mult = 1;
291
+ char c;
292
+
293
+ if (*s == '-') {
294
+ mult = -1;
295
+ s++;
296
+ } else if (*s == '+') {
297
+ mult = 1;
298
+ s++;
299
+ }
300
+
301
+ while ((c = *(s++)) != '\r') {
302
+ dec = c - '0';
303
+ if (dec >= 0 && dec < 10) {
304
+ v *= 10;
305
+ v += dec;
306
+ } else {
307
+ /* Should not happen... */
308
+ return -1;
309
+ }
310
+ }
311
+
312
+ return mult*v;
313
+ }
314
+
315
+ static char *readLine(redisReader *r, int *_len) {
316
+ char *p, *s;
317
+ int len;
318
+
319
+ p = r->buf+r->pos;
320
+ s = seekNewline(p,(r->len-r->pos));
321
+ if (s != NULL) {
322
+ len = s-(r->buf+r->pos);
323
+ r->pos += len+2; /* skip \r\n */
324
+ if (_len) *_len = len;
325
+ return p;
326
+ }
327
+ return NULL;
328
+ }
329
+
330
+ static void moveToNextTask(redisReader *r) {
331
+ redisReadTask *cur, *prv;
332
+ while (r->ridx >= 0) {
333
+ /* Return a.s.a.p. when the stack is now empty. */
334
+ if (r->ridx == 0) {
335
+ r->ridx--;
336
+ return;
337
+ }
338
+
339
+ cur = &(r->rstack[r->ridx]);
340
+ prv = &(r->rstack[r->ridx-1]);
341
+ assert(prv->type == REDIS_REPLY_ARRAY);
342
+ if (cur->idx == prv->elements-1) {
343
+ r->ridx--;
344
+ } else {
345
+ /* Reset the type because the next item can be anything */
346
+ assert(cur->idx < prv->elements);
347
+ cur->type = -1;
348
+ cur->elements = -1;
349
+ cur->idx++;
350
+ return;
351
+ }
352
+ }
353
+ }
354
+
355
+ static int processLineItem(redisReader *r) {
356
+ redisReadTask *cur = &(r->rstack[r->ridx]);
357
+ void *obj;
358
+ char *p;
359
+ int len;
360
+
361
+ if ((p = readLine(r,&len)) != NULL) {
362
+ if (cur->type == REDIS_REPLY_INTEGER) {
363
+ if (r->fn && r->fn->createInteger)
364
+ obj = r->fn->createInteger(cur,readLongLong(p));
365
+ else
366
+ obj = (void*)REDIS_REPLY_INTEGER;
367
+ } else {
368
+ /* Type will be error or status. */
369
+ if (r->fn && r->fn->createString)
370
+ obj = r->fn->createString(cur,p,len);
371
+ else
372
+ obj = (void*)(size_t)(cur->type);
373
+ }
374
+
375
+ if (obj == NULL) {
376
+ __redisReaderSetErrorOOM(r);
377
+ return REDIS_ERR;
378
+ }
379
+
380
+ /* Set reply if this is the root object. */
381
+ if (r->ridx == 0) r->reply = obj;
382
+ moveToNextTask(r);
383
+ return REDIS_OK;
384
+ }
385
+
386
+ return REDIS_ERR;
387
+ }
388
+
389
+ static int processBulkItem(redisReader *r) {
390
+ redisReadTask *cur = &(r->rstack[r->ridx]);
391
+ void *obj = NULL;
392
+ char *p, *s;
393
+ long len;
394
+ unsigned long bytelen;
395
+ int success = 0;
396
+
397
+ p = r->buf+r->pos;
398
+ s = seekNewline(p,r->len-r->pos);
399
+ if (s != NULL) {
400
+ p = r->buf+r->pos;
401
+ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
402
+ len = readLongLong(p);
403
+
404
+ if (len < 0) {
405
+ /* The nil object can always be created. */
406
+ if (r->fn && r->fn->createNil)
407
+ obj = r->fn->createNil(cur);
408
+ else
409
+ obj = (void*)REDIS_REPLY_NIL;
410
+ success = 1;
411
+ } else {
412
+ /* Only continue when the buffer contains the entire bulk item. */
413
+ bytelen += len+2; /* include \r\n */
414
+ if (r->pos+bytelen <= r->len) {
415
+ if (r->fn && r->fn->createString)
416
+ obj = r->fn->createString(cur,s+2,len);
417
+ else
418
+ obj = (void*)REDIS_REPLY_STRING;
419
+ success = 1;
420
+ }
421
+ }
422
+
423
+ /* Proceed when obj was created. */
424
+ if (success) {
425
+ if (obj == NULL) {
426
+ __redisReaderSetErrorOOM(r);
427
+ return REDIS_ERR;
428
+ }
429
+
430
+ r->pos += bytelen;
431
+
432
+ /* Set reply if this is the root object. */
433
+ if (r->ridx == 0) r->reply = obj;
434
+ moveToNextTask(r);
435
+ return REDIS_OK;
436
+ }
437
+ }
438
+
439
+ return REDIS_ERR;
440
+ }
441
+
442
+ static int processMultiBulkItem(redisReader *r) {
443
+ redisReadTask *cur = &(r->rstack[r->ridx]);
444
+ void *obj;
445
+ char *p;
446
+ long elements;
447
+ int root = 0;
448
+
449
+ /* Set error for nested multi bulks with depth > 7 */
450
+ if (r->ridx == 8) {
451
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
452
+ "No support for nested multi bulk replies with depth > 7");
453
+ return REDIS_ERR;
454
+ }
455
+
456
+ if ((p = readLine(r,NULL)) != NULL) {
457
+ elements = readLongLong(p);
458
+ root = (r->ridx == 0);
459
+
460
+ if (elements == -1) {
461
+ if (r->fn && r->fn->createNil)
462
+ obj = r->fn->createNil(cur);
463
+ else
464
+ obj = (void*)REDIS_REPLY_NIL;
465
+
466
+ if (obj == NULL) {
467
+ __redisReaderSetErrorOOM(r);
468
+ return REDIS_ERR;
469
+ }
470
+
471
+ moveToNextTask(r);
472
+ } else {
473
+ if (r->fn && r->fn->createArray)
474
+ obj = r->fn->createArray(cur,elements);
475
+ else
476
+ obj = (void*)REDIS_REPLY_ARRAY;
477
+
478
+ if (obj == NULL) {
479
+ __redisReaderSetErrorOOM(r);
480
+ return REDIS_ERR;
481
+ }
482
+
483
+ /* Modify task stack when there are more than 0 elements. */
484
+ if (elements > 0) {
485
+ cur->elements = elements;
486
+ cur->obj = obj;
487
+ r->ridx++;
488
+ r->rstack[r->ridx].type = -1;
489
+ r->rstack[r->ridx].elements = -1;
490
+ r->rstack[r->ridx].idx = 0;
491
+ r->rstack[r->ridx].obj = NULL;
492
+ r->rstack[r->ridx].parent = cur;
493
+ r->rstack[r->ridx].privdata = r->privdata;
494
+ } else {
495
+ moveToNextTask(r);
496
+ }
497
+ }
498
+
499
+ /* Set reply if this is the root object. */
500
+ if (root) r->reply = obj;
501
+ return REDIS_OK;
502
+ }
503
+
504
+ return REDIS_ERR;
505
+ }
506
+
507
+ static int processItem(redisReader *r) {
508
+ redisReadTask *cur = &(r->rstack[r->ridx]);
509
+ char *p;
510
+
511
+ /* check if we need to read type */
512
+ if (cur->type < 0) {
513
+ if ((p = readBytes(r,1)) != NULL) {
514
+ switch (p[0]) {
515
+ case '-':
516
+ cur->type = REDIS_REPLY_ERROR;
517
+ break;
518
+ case '+':
519
+ cur->type = REDIS_REPLY_STATUS;
520
+ break;
521
+ case ':':
522
+ cur->type = REDIS_REPLY_INTEGER;
523
+ break;
524
+ case '$':
525
+ cur->type = REDIS_REPLY_STRING;
526
+ break;
527
+ case '*':
528
+ cur->type = REDIS_REPLY_ARRAY;
529
+ break;
530
+ default:
531
+ __redisReaderSetErrorProtocolByte(r,*p);
532
+ return REDIS_ERR;
533
+ }
534
+ } else {
535
+ /* could not consume 1 byte */
536
+ return REDIS_ERR;
537
+ }
538
+ }
539
+
540
+ /* process typed item */
541
+ switch(cur->type) {
542
+ case REDIS_REPLY_ERROR:
543
+ case REDIS_REPLY_STATUS:
544
+ case REDIS_REPLY_INTEGER:
545
+ return processLineItem(r);
546
+ case REDIS_REPLY_STRING:
547
+ return processBulkItem(r);
548
+ case REDIS_REPLY_ARRAY:
549
+ return processMultiBulkItem(r);
550
+ default:
551
+ assert(NULL);
552
+ return REDIS_ERR; /* Avoid warning. */
553
+ }
554
+ }
555
+
556
+ redisReader *redisReaderCreate(void) {
557
+ redisReader *r;
558
+
559
+ r = calloc(sizeof(redisReader),1);
560
+ if (r == NULL)
561
+ return NULL;
562
+
563
+ r->err = 0;
564
+ r->errstr[0] = '\0';
565
+ r->fn = &defaultFunctions;
566
+ r->buf = sdsempty();
567
+ r->maxbuf = REDIS_READER_MAX_BUF;
568
+ if (r->buf == NULL) {
569
+ free(r);
570
+ return NULL;
571
+ }
572
+
573
+ r->ridx = -1;
574
+ return r;
575
+ }
576
+
577
+ void redisReaderFree(redisReader *r) {
578
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
579
+ r->fn->freeObject(r->reply);
580
+ if (r->buf != NULL)
581
+ sdsfree(r->buf);
582
+ free(r);
583
+ }
584
+
585
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
586
+ sds newbuf;
587
+
588
+ /* Return early when this reader is in an erroneous state. */
589
+ if (r->err)
590
+ return REDIS_ERR;
591
+
592
+ /* Copy the provided buffer. */
593
+ if (buf != NULL && len >= 1) {
594
+ /* Destroy internal buffer when it is empty and is quite large. */
595
+ if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
596
+ sdsfree(r->buf);
597
+ r->buf = sdsempty();
598
+ r->pos = 0;
599
+
600
+ /* r->buf should not be NULL since we just free'd a larger one. */
601
+ assert(r->buf != NULL);
602
+ }
603
+
604
+ newbuf = sdscatlen(r->buf,buf,len);
605
+ if (newbuf == NULL) {
606
+ __redisReaderSetErrorOOM(r);
607
+ return REDIS_ERR;
608
+ }
609
+
610
+ r->buf = newbuf;
611
+ r->len = sdslen(r->buf);
612
+ }
613
+
614
+ return REDIS_OK;
615
+ }
616
+
617
+ int redisReaderGetReply(redisReader *r, void **reply) {
618
+ /* Default target pointer to NULL. */
619
+ if (reply != NULL)
620
+ *reply = NULL;
621
+
622
+ /* Return early when this reader is in an erroneous state. */
623
+ if (r->err)
624
+ return REDIS_ERR;
625
+
626
+ /* When the buffer is empty, there will never be a reply. */
627
+ if (r->len == 0)
628
+ return REDIS_OK;
629
+
630
+ /* Set first item to process when the stack is empty. */
631
+ if (r->ridx == -1) {
632
+ r->rstack[0].type = -1;
633
+ r->rstack[0].elements = -1;
634
+ r->rstack[0].idx = -1;
635
+ r->rstack[0].obj = NULL;
636
+ r->rstack[0].parent = NULL;
637
+ r->rstack[0].privdata = r->privdata;
638
+ r->ridx = 0;
639
+ }
640
+
641
+ /* Process items in reply. */
642
+ while (r->ridx >= 0)
643
+ if (processItem(r) != REDIS_OK)
644
+ break;
645
+
646
+ /* Return ASAP when an error occurred. */
647
+ if (r->err)
648
+ return REDIS_ERR;
649
+
650
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
651
+ * doing unnecessary calls to memmove() in sds.c. */
652
+ if (r->pos >= 1024) {
653
+ r->buf = sdsrange(r->buf,r->pos,-1);
654
+ r->pos = 0;
655
+ r->len = sdslen(r->buf);
656
+ }
657
+
658
+ /* Emit a reply when there is one. */
659
+ if (r->ridx == -1) {
660
+ if (reply != NULL)
661
+ *reply = r->reply;
662
+ r->reply = NULL;
663
+ }
664
+ return REDIS_OK;
665
+ }
666
+
667
+ /* Calculate the number of bytes needed to represent an integer as string. */
668
+ static int intlen(int i) {
669
+ int len = 0;
670
+ if (i < 0) {
671
+ len++;
672
+ i = -i;
673
+ }
674
+ do {
675
+ len++;
676
+ i /= 10;
677
+ } while(i);
678
+ return len;
679
+ }
680
+
681
+ /* Helper that calculates the bulk length given a certain string length. */
682
+ static size_t bulklen(size_t len) {
683
+ return 1+intlen(len)+2+len+2;
684
+ }
685
+
686
+ int redisvFormatCommand(char **target, const char *format, va_list ap) {
687
+ const char *c = format;
688
+ char *cmd = NULL; /* final command */
689
+ int pos; /* position in final command */
690
+ sds curarg, newarg; /* current argument */
691
+ int touched = 0; /* was the current argument touched? */
692
+ char **curargv = NULL, **newargv = NULL;
693
+ int argc = 0;
694
+ int totlen = 0;
695
+ int j;
696
+
697
+ /* Abort if there is not target to set */
698
+ if (target == NULL)
699
+ return -1;
700
+
701
+ /* Build the command string accordingly to protocol */
702
+ curarg = sdsempty();
703
+ if (curarg == NULL)
704
+ return -1;
705
+
706
+ while(*c != '\0') {
707
+ if (*c != '%' || c[1] == '\0') {
708
+ if (*c == ' ') {
709
+ if (touched) {
710
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
711
+ if (newargv == NULL) goto err;
712
+ curargv = newargv;
713
+ curargv[argc++] = curarg;
714
+ totlen += bulklen(sdslen(curarg));
715
+
716
+ /* curarg is put in argv so it can be overwritten. */
717
+ curarg = sdsempty();
718
+ if (curarg == NULL) goto err;
719
+ touched = 0;
720
+ }
721
+ } else {
722
+ newarg = sdscatlen(curarg,c,1);
723
+ if (newarg == NULL) goto err;
724
+ curarg = newarg;
725
+ touched = 1;
726
+ }
727
+ } else {
728
+ char *arg;
729
+ size_t size;
730
+
731
+ /* Set newarg so it can be checked even if it is not touched. */
732
+ newarg = curarg;
733
+
734
+ switch(c[1]) {
735
+ case 's':
736
+ arg = va_arg(ap,char*);
737
+ size = strlen(arg);
738
+ if (size > 0)
739
+ newarg = sdscatlen(curarg,arg,size);
740
+ break;
741
+ case 'b':
742
+ arg = va_arg(ap,char*);
743
+ size = va_arg(ap,size_t);
744
+ if (size > 0)
745
+ newarg = sdscatlen(curarg,arg,size);
746
+ break;
747
+ case '%':
748
+ newarg = sdscat(curarg,"%");
749
+ break;
750
+ default:
751
+ /* Try to detect printf format */
752
+ {
753
+ static const char intfmts[] = "diouxX";
754
+ char _format[16];
755
+ const char *_p = c+1;
756
+ size_t _l = 0;
757
+ va_list _cpy;
758
+
759
+ /* Flags */
760
+ if (*_p != '\0' && *_p == '#') _p++;
761
+ if (*_p != '\0' && *_p == '0') _p++;
762
+ if (*_p != '\0' && *_p == '-') _p++;
763
+ if (*_p != '\0' && *_p == ' ') _p++;
764
+ if (*_p != '\0' && *_p == '+') _p++;
765
+
766
+ /* Field width */
767
+ while (*_p != '\0' && isdigit(*_p)) _p++;
768
+
769
+ /* Precision */
770
+ if (*_p == '.') {
771
+ _p++;
772
+ while (*_p != '\0' && isdigit(*_p)) _p++;
773
+ }
774
+
775
+ /* Copy va_list before consuming with va_arg */
776
+ va_copy(_cpy,ap);
777
+
778
+ /* Integer conversion (without modifiers) */
779
+ if (strchr(intfmts,*_p) != NULL) {
780
+ va_arg(ap,int);
781
+ goto fmt_valid;
782
+ }
783
+
784
+ /* Double conversion (without modifiers) */
785
+ if (strchr("eEfFgGaA",*_p) != NULL) {
786
+ va_arg(ap,double);
787
+ goto fmt_valid;
788
+ }
789
+
790
+ /* Size: char */
791
+ if (_p[0] == 'h' && _p[1] == 'h') {
792
+ _p += 2;
793
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
794
+ va_arg(ap,int); /* char gets promoted to int */
795
+ goto fmt_valid;
796
+ }
797
+ goto fmt_invalid;
798
+ }
799
+
800
+ /* Size: short */
801
+ if (_p[0] == 'h') {
802
+ _p += 1;
803
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
804
+ va_arg(ap,int); /* short gets promoted to int */
805
+ goto fmt_valid;
806
+ }
807
+ goto fmt_invalid;
808
+ }
809
+
810
+ /* Size: long long */
811
+ if (_p[0] == 'l' && _p[1] == 'l') {
812
+ _p += 2;
813
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
814
+ va_arg(ap,long long);
815
+ goto fmt_valid;
816
+ }
817
+ goto fmt_invalid;
818
+ }
819
+
820
+ /* Size: long */
821
+ if (_p[0] == 'l') {
822
+ _p += 1;
823
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
824
+ va_arg(ap,long);
825
+ goto fmt_valid;
826
+ }
827
+ goto fmt_invalid;
828
+ }
829
+
830
+ fmt_invalid:
831
+ va_end(_cpy);
832
+ goto err;
833
+
834
+ fmt_valid:
835
+ _l = (_p+1)-c;
836
+ if (_l < sizeof(_format)-2) {
837
+ memcpy(_format,c,_l);
838
+ _format[_l] = '\0';
839
+ newarg = sdscatvprintf(curarg,_format,_cpy);
840
+
841
+ /* Update current position (note: outer blocks
842
+ * increment c twice so compensate here) */
843
+ c = _p-1;
844
+ }
845
+
846
+ va_end(_cpy);
847
+ break;
848
+ }
849
+ }
850
+
851
+ if (newarg == NULL) goto err;
852
+ curarg = newarg;
853
+
854
+ touched = 1;
855
+ c++;
856
+ }
857
+ c++;
858
+ }
859
+
860
+ /* Add the last argument if needed */
861
+ if (touched) {
862
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
863
+ if (newargv == NULL) goto err;
864
+ curargv = newargv;
865
+ curargv[argc++] = curarg;
866
+ totlen += bulklen(sdslen(curarg));
867
+ } else {
868
+ sdsfree(curarg);
869
+ }
870
+
871
+ /* Clear curarg because it was put in curargv or was free'd. */
872
+ curarg = NULL;
873
+
874
+ /* Add bytes needed to hold multi bulk count */
875
+ totlen += 1+intlen(argc)+2;
876
+
877
+ /* Build the command at protocol level */
878
+ cmd = malloc(totlen+1);
879
+ if (cmd == NULL) goto err;
880
+
881
+ pos = sprintf(cmd,"*%d\r\n",argc);
882
+ for (j = 0; j < argc; j++) {
883
+ pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
884
+ memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
885
+ pos += sdslen(curargv[j]);
886
+ sdsfree(curargv[j]);
887
+ cmd[pos++] = '\r';
888
+ cmd[pos++] = '\n';
889
+ }
890
+ assert(pos == totlen);
891
+ cmd[pos] = '\0';
892
+
893
+ free(curargv);
894
+ *target = cmd;
895
+ return totlen;
896
+
897
+ err:
898
+ while(argc--)
899
+ sdsfree(curargv[argc]);
900
+ free(curargv);
901
+
902
+ if (curarg != NULL)
903
+ sdsfree(curarg);
904
+
905
+ /* No need to check cmd since it is the last statement that can fail,
906
+ * but do it anyway to be as defensive as possible. */
907
+ if (cmd != NULL)
908
+ free(cmd);
909
+
910
+ return -1;
911
+ }
912
+
913
+ /* Format a command according to the Redis protocol. This function
914
+ * takes a format similar to printf:
915
+ *
916
+ * %s represents a C null terminated string you want to interpolate
917
+ * %b represents a binary safe string
918
+ *
919
+ * When using %b you need to provide both the pointer to the string
920
+ * and the length in bytes. Examples:
921
+ *
922
+ * len = redisFormatCommand(target, "GET %s", mykey);
923
+ * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
924
+ */
925
+ int redisFormatCommand(char **target, const char *format, ...) {
926
+ va_list ap;
927
+ int len;
928
+ va_start(ap,format);
929
+ len = redisvFormatCommand(target,format,ap);
930
+ va_end(ap);
931
+ return len;
932
+ }
933
+
934
+ /* Format a command according to the Redis protocol. This function takes the
935
+ * number of arguments, an array with arguments and an array with their
936
+ * lengths. If the latter is set to NULL, strlen will be used to compute the
937
+ * argument lengths.
938
+ */
939
+ int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
940
+ char *cmd = NULL; /* final command */
941
+ int pos; /* position in final command */
942
+ size_t len;
943
+ int totlen, j;
944
+
945
+ /* Calculate number of bytes needed for the command */
946
+ totlen = 1+intlen(argc)+2;
947
+ for (j = 0; j < argc; j++) {
948
+ len = argvlen ? argvlen[j] : strlen(argv[j]);
949
+ totlen += bulklen(len);
950
+ }
951
+
952
+ /* Build the command at protocol level */
953
+ cmd = malloc(totlen+1);
954
+ if (cmd == NULL)
955
+ return -1;
956
+
957
+ pos = sprintf(cmd,"*%d\r\n",argc);
958
+ for (j = 0; j < argc; j++) {
959
+ len = argvlen ? argvlen[j] : strlen(argv[j]);
960
+ pos += sprintf(cmd+pos,"$%zu\r\n",len);
961
+ memcpy(cmd+pos,argv[j],len);
962
+ pos += len;
963
+ cmd[pos++] = '\r';
964
+ cmd[pos++] = '\n';
965
+ }
966
+ assert(pos == totlen);
967
+ cmd[pos] = '\0';
968
+
969
+ *target = cmd;
970
+ return totlen;
971
+ }
972
+
973
+ void __redisSetError(redisContext *c, int type, const char *str) {
974
+ size_t len;
975
+
976
+ c->err = type;
977
+ if (str != NULL) {
978
+ len = strlen(str);
979
+ len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
980
+ memcpy(c->errstr,str,len);
981
+ c->errstr[len] = '\0';
982
+ } else {
983
+ /* Only REDIS_ERR_IO may lack a description! */
984
+ assert(type == REDIS_ERR_IO);
985
+ strerror_r(errno,c->errstr,sizeof(c->errstr));
986
+ }
987
+ }
988
+
989
+ static redisContext *redisContextInit(void) {
990
+ redisContext *c;
991
+
992
+ c = calloc(1,sizeof(redisContext));
993
+ if (c == NULL)
994
+ return NULL;
995
+
996
+ c->err = 0;
997
+ c->errstr[0] = '\0';
998
+ c->obuf = sdsempty();
999
+ c->reader = redisReaderCreate();
1000
+ return c;
1001
+ }
1002
+
1003
+ void redisFree(redisContext *c) {
1004
+ if (c->fd > 0)
1005
+ close(c->fd);
1006
+ if (c->obuf != NULL)
1007
+ sdsfree(c->obuf);
1008
+ if (c->reader != NULL)
1009
+ redisReaderFree(c->reader);
1010
+ free(c);
1011
+ }
1012
+
1013
+ /* Connect to a Redis instance. On error the field error in the returned
1014
+ * context will be set to the return value of the error function.
1015
+ * When no set of reply functions is given, the default set will be used. */
1016
+ redisContext *redisConnect(const char *ip, int port) {
1017
+ redisContext *c;
1018
+
1019
+ c = redisContextInit();
1020
+ if (c == NULL)
1021
+ return NULL;
1022
+
1023
+ c->flags |= REDIS_BLOCK;
1024
+ redisContextConnectTcp(c,ip,port,NULL);
1025
+ return c;
1026
+ }
1027
+
1028
+ redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
1029
+ redisContext *c;
1030
+
1031
+ c = redisContextInit();
1032
+ if (c == NULL)
1033
+ return NULL;
1034
+
1035
+ c->flags |= REDIS_BLOCK;
1036
+ redisContextConnectTcp(c,ip,port,&tv);
1037
+ return c;
1038
+ }
1039
+
1040
+ redisContext *redisConnectNonBlock(const char *ip, int port) {
1041
+ redisContext *c;
1042
+
1043
+ c = redisContextInit();
1044
+ if (c == NULL)
1045
+ return NULL;
1046
+
1047
+ c->flags &= ~REDIS_BLOCK;
1048
+ redisContextConnectTcp(c,ip,port,NULL);
1049
+ return c;
1050
+ }
1051
+
1052
+ redisContext *redisConnectUnix(const char *path) {
1053
+ redisContext *c;
1054
+
1055
+ c = redisContextInit();
1056
+ if (c == NULL)
1057
+ return NULL;
1058
+
1059
+ c->flags |= REDIS_BLOCK;
1060
+ redisContextConnectUnix(c,path,NULL);
1061
+ return c;
1062
+ }
1063
+
1064
+ redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
1065
+ redisContext *c;
1066
+
1067
+ c = redisContextInit();
1068
+ if (c == NULL)
1069
+ return NULL;
1070
+
1071
+ c->flags |= REDIS_BLOCK;
1072
+ redisContextConnectUnix(c,path,&tv);
1073
+ return c;
1074
+ }
1075
+
1076
+ redisContext *redisConnectUnixNonBlock(const char *path) {
1077
+ redisContext *c;
1078
+
1079
+ c = redisContextInit();
1080
+ if (c == NULL)
1081
+ return NULL;
1082
+
1083
+ c->flags &= ~REDIS_BLOCK;
1084
+ redisContextConnectUnix(c,path,NULL);
1085
+ return c;
1086
+ }
1087
+
1088
+ /* Set read/write timeout on a blocking socket. */
1089
+ int redisSetTimeout(redisContext *c, struct timeval tv) {
1090
+ if (c->flags & REDIS_BLOCK)
1091
+ return redisContextSetTimeout(c,tv);
1092
+ return REDIS_ERR;
1093
+ }
1094
+
1095
+ /* Use this function to handle a read event on the descriptor. It will try
1096
+ * and read some bytes from the socket and feed them to the reply parser.
1097
+ *
1098
+ * After this function is called, you may use redisContextReadReply to
1099
+ * see if there is a reply available. */
1100
+ int redisBufferRead(redisContext *c) {
1101
+ char buf[1024*16];
1102
+ int nread;
1103
+
1104
+ /* Return early when the context has seen an error. */
1105
+ if (c->err)
1106
+ return REDIS_ERR;
1107
+
1108
+ nread = read(c->fd,buf,sizeof(buf));
1109
+ if (nread == -1) {
1110
+ if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
1111
+ /* Try again later */
1112
+ } else {
1113
+ __redisSetError(c,REDIS_ERR_IO,NULL);
1114
+ return REDIS_ERR;
1115
+ }
1116
+ } else if (nread == 0) {
1117
+ __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
1118
+ return REDIS_ERR;
1119
+ } else {
1120
+ if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
1121
+ __redisSetError(c,c->reader->err,c->reader->errstr);
1122
+ return REDIS_ERR;
1123
+ }
1124
+ }
1125
+ return REDIS_OK;
1126
+ }
1127
+
1128
+ /* Write the output buffer to the socket.
1129
+ *
1130
+ * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
1131
+ * succesfully written to the socket. When the buffer is empty after the
1132
+ * write operation, "done" is set to 1 (if given).
1133
+ *
1134
+ * Returns REDIS_ERR if an error occured trying to write and sets
1135
+ * c->errstr to hold the appropriate error string.
1136
+ */
1137
+ int redisBufferWrite(redisContext *c, int *done) {
1138
+ int nwritten;
1139
+
1140
+ /* Return early when the context has seen an error. */
1141
+ if (c->err)
1142
+ return REDIS_ERR;
1143
+
1144
+ if (sdslen(c->obuf) > 0) {
1145
+ nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
1146
+ if (nwritten == -1) {
1147
+ if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
1148
+ /* Try again later */
1149
+ } else {
1150
+ __redisSetError(c,REDIS_ERR_IO,NULL);
1151
+ return REDIS_ERR;
1152
+ }
1153
+ } else if (nwritten > 0) {
1154
+ if (nwritten == (signed)sdslen(c->obuf)) {
1155
+ sdsfree(c->obuf);
1156
+ c->obuf = sdsempty();
1157
+ } else {
1158
+ c->obuf = sdsrange(c->obuf,nwritten,-1);
1159
+ }
1160
+ }
1161
+ }
1162
+ if (done != NULL) *done = (sdslen(c->obuf) == 0);
1163
+ return REDIS_OK;
1164
+ }
1165
+
1166
+ /* Internal helper function to try and get a reply from the reader,
1167
+ * or set an error in the context otherwise. */
1168
+ int redisGetReplyFromReader(redisContext *c, void **reply) {
1169
+ if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
1170
+ __redisSetError(c,c->reader->err,c->reader->errstr);
1171
+ return REDIS_ERR;
1172
+ }
1173
+ return REDIS_OK;
1174
+ }
1175
+
1176
+ int redisGetReply(redisContext *c, void **reply) {
1177
+ int wdone = 0;
1178
+ void *aux = NULL;
1179
+
1180
+ /* Try to read pending replies */
1181
+ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1182
+ return REDIS_ERR;
1183
+
1184
+ /* For the blocking context, flush output buffer and read reply */
1185
+ if (aux == NULL && c->flags & REDIS_BLOCK) {
1186
+ /* Write until done */
1187
+ do {
1188
+ if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1189
+ return REDIS_ERR;
1190
+ } while (!wdone);
1191
+
1192
+ /* Read until there is a reply */
1193
+ do {
1194
+ if (redisBufferRead(c) == REDIS_ERR)
1195
+ return REDIS_ERR;
1196
+ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1197
+ return REDIS_ERR;
1198
+ } while (aux == NULL);
1199
+ }
1200
+
1201
+ /* Set reply object */
1202
+ if (reply != NULL) *reply = aux;
1203
+ return REDIS_OK;
1204
+ }
1205
+
1206
+
1207
+ /* Helper function for the redisAppendCommand* family of functions.
1208
+ *
1209
+ * Write a formatted command to the output buffer. When this family
1210
+ * is used, you need to call redisGetReply yourself to retrieve
1211
+ * the reply (or replies in pub/sub).
1212
+ */
1213
+ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1214
+ sds newbuf;
1215
+
1216
+ newbuf = sdscatlen(c->obuf,cmd,len);
1217
+ if (newbuf == NULL) {
1218
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1219
+ return REDIS_ERR;
1220
+ }
1221
+
1222
+ c->obuf = newbuf;
1223
+ return REDIS_OK;
1224
+ }
1225
+
1226
+ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1227
+ char *cmd;
1228
+ int len;
1229
+
1230
+ len = redisvFormatCommand(&cmd,format,ap);
1231
+ if (len == -1) {
1232
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1233
+ return REDIS_ERR;
1234
+ }
1235
+
1236
+ if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1237
+ free(cmd);
1238
+ return REDIS_ERR;
1239
+ }
1240
+
1241
+ free(cmd);
1242
+ return REDIS_OK;
1243
+ }
1244
+
1245
+ int redisAppendCommand(redisContext *c, const char *format, ...) {
1246
+ va_list ap;
1247
+ int ret;
1248
+
1249
+ va_start(ap,format);
1250
+ ret = redisvAppendCommand(c,format,ap);
1251
+ va_end(ap);
1252
+ return ret;
1253
+ }
1254
+
1255
+ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1256
+ char *cmd;
1257
+ int len;
1258
+
1259
+ len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1260
+ if (len == -1) {
1261
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1262
+ return REDIS_ERR;
1263
+ }
1264
+
1265
+ if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1266
+ free(cmd);
1267
+ return REDIS_ERR;
1268
+ }
1269
+
1270
+ free(cmd);
1271
+ return REDIS_OK;
1272
+ }
1273
+
1274
+ /* Helper function for the redisCommand* family of functions.
1275
+ *
1276
+ * Write a formatted command to the output buffer. If the given context is
1277
+ * blocking, immediately read the reply into the "reply" pointer. When the
1278
+ * context is non-blocking, the "reply" pointer will not be used and the
1279
+ * command is simply appended to the write buffer.
1280
+ *
1281
+ * Returns the reply when a reply was succesfully retrieved. Returns NULL
1282
+ * otherwise. When NULL is returned in a blocking context, the error field
1283
+ * in the context will be set.
1284
+ */
1285
+ static void *__redisBlockForReply(redisContext *c) {
1286
+ void *reply;
1287
+
1288
+ if (c->flags & REDIS_BLOCK) {
1289
+ if (redisGetReply(c,&reply) != REDIS_OK)
1290
+ return NULL;
1291
+ return reply;
1292
+ }
1293
+ return NULL;
1294
+ }
1295
+
1296
+ void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1297
+ if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1298
+ return NULL;
1299
+ return __redisBlockForReply(c);
1300
+ }
1301
+
1302
+ void *redisCommand(redisContext *c, const char *format, ...) {
1303
+ va_list ap;
1304
+ void *reply = NULL;
1305
+ va_start(ap,format);
1306
+ reply = redisvCommand(c,format,ap);
1307
+ va_end(ap);
1308
+ return reply;
1309
+ }
1310
+
1311
+ void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1312
+ if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1313
+ return NULL;
1314
+ return __redisBlockForReply(c);
1315
+ }