threadsafe-hiredis 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,126 +0,0 @@
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 */
@@ -1,16 +0,0 @@
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
@@ -1,1285 +0,0 @@
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 = redisContextInit();
1018
- c->flags |= REDIS_BLOCK;
1019
- redisContextConnectTcp(c,ip,port,NULL);
1020
- return c;
1021
- }
1022
-
1023
- redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
1024
- redisContext *c = redisContextInit();
1025
- c->flags |= REDIS_BLOCK;
1026
- redisContextConnectTcp(c,ip,port,&tv);
1027
- return c;
1028
- }
1029
-
1030
- redisContext *redisConnectNonBlock(const char *ip, int port) {
1031
- redisContext *c = redisContextInit();
1032
- c->flags &= ~REDIS_BLOCK;
1033
- redisContextConnectTcp(c,ip,port,NULL);
1034
- return c;
1035
- }
1036
-
1037
- redisContext *redisConnectUnix(const char *path) {
1038
- redisContext *c = redisContextInit();
1039
- c->flags |= REDIS_BLOCK;
1040
- redisContextConnectUnix(c,path,NULL);
1041
- return c;
1042
- }
1043
-
1044
- redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
1045
- redisContext *c = redisContextInit();
1046
- c->flags |= REDIS_BLOCK;
1047
- redisContextConnectUnix(c,path,&tv);
1048
- return c;
1049
- }
1050
-
1051
- redisContext *redisConnectUnixNonBlock(const char *path) {
1052
- redisContext *c = redisContextInit();
1053
- c->flags &= ~REDIS_BLOCK;
1054
- redisContextConnectUnix(c,path,NULL);
1055
- return c;
1056
- }
1057
-
1058
- /* Set read/write timeout on a blocking socket. */
1059
- int redisSetTimeout(redisContext *c, struct timeval tv) {
1060
- if (c->flags & REDIS_BLOCK)
1061
- return redisContextSetTimeout(c,tv);
1062
- return REDIS_ERR;
1063
- }
1064
-
1065
- /* Use this function to handle a read event on the descriptor. It will try
1066
- * and read some bytes from the socket and feed them to the reply parser.
1067
- *
1068
- * After this function is called, you may use redisContextReadReply to
1069
- * see if there is a reply available. */
1070
- int redisBufferRead(redisContext *c) {
1071
- char buf[1024*16];
1072
- int nread;
1073
-
1074
- /* Return early when the context has seen an error. */
1075
- if (c->err)
1076
- return REDIS_ERR;
1077
-
1078
- nread = read(c->fd,buf,sizeof(buf));
1079
- if (nread == -1) {
1080
- if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
1081
- /* Try again later */
1082
- } else {
1083
- __redisSetError(c,REDIS_ERR_IO,NULL);
1084
- return REDIS_ERR;
1085
- }
1086
- } else if (nread == 0) {
1087
- __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
1088
- return REDIS_ERR;
1089
- } else {
1090
- if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
1091
- __redisSetError(c,c->reader->err,c->reader->errstr);
1092
- return REDIS_ERR;
1093
- }
1094
- }
1095
- return REDIS_OK;
1096
- }
1097
-
1098
- /* Write the output buffer to the socket.
1099
- *
1100
- * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
1101
- * succesfully written to the socket. When the buffer is empty after the
1102
- * write operation, "done" is set to 1 (if given).
1103
- *
1104
- * Returns REDIS_ERR if an error occured trying to write and sets
1105
- * c->errstr to hold the appropriate error string.
1106
- */
1107
- int redisBufferWrite(redisContext *c, int *done) {
1108
- int nwritten;
1109
-
1110
- /* Return early when the context has seen an error. */
1111
- if (c->err)
1112
- return REDIS_ERR;
1113
-
1114
- if (sdslen(c->obuf) > 0) {
1115
- nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
1116
- if (nwritten == -1) {
1117
- if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
1118
- /* Try again later */
1119
- } else {
1120
- __redisSetError(c,REDIS_ERR_IO,NULL);
1121
- return REDIS_ERR;
1122
- }
1123
- } else if (nwritten > 0) {
1124
- if (nwritten == (signed)sdslen(c->obuf)) {
1125
- sdsfree(c->obuf);
1126
- c->obuf = sdsempty();
1127
- } else {
1128
- c->obuf = sdsrange(c->obuf,nwritten,-1);
1129
- }
1130
- }
1131
- }
1132
- if (done != NULL) *done = (sdslen(c->obuf) == 0);
1133
- return REDIS_OK;
1134
- }
1135
-
1136
- /* Internal helper function to try and get a reply from the reader,
1137
- * or set an error in the context otherwise. */
1138
- int redisGetReplyFromReader(redisContext *c, void **reply) {
1139
- if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
1140
- __redisSetError(c,c->reader->err,c->reader->errstr);
1141
- return REDIS_ERR;
1142
- }
1143
- return REDIS_OK;
1144
- }
1145
-
1146
- int redisGetReply(redisContext *c, void **reply) {
1147
- int wdone = 0;
1148
- void *aux = NULL;
1149
-
1150
- /* Try to read pending replies */
1151
- if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1152
- return REDIS_ERR;
1153
-
1154
- /* For the blocking context, flush output buffer and read reply */
1155
- if (aux == NULL && c->flags & REDIS_BLOCK) {
1156
- /* Write until done */
1157
- do {
1158
- if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1159
- return REDIS_ERR;
1160
- } while (!wdone);
1161
-
1162
- /* Read until there is a reply */
1163
- do {
1164
- if (redisBufferRead(c) == REDIS_ERR)
1165
- return REDIS_ERR;
1166
- if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1167
- return REDIS_ERR;
1168
- } while (aux == NULL);
1169
- }
1170
-
1171
- /* Set reply object */
1172
- if (reply != NULL) *reply = aux;
1173
- return REDIS_OK;
1174
- }
1175
-
1176
-
1177
- /* Helper function for the redisAppendCommand* family of functions.
1178
- *
1179
- * Write a formatted command to the output buffer. When this family
1180
- * is used, you need to call redisGetReply yourself to retrieve
1181
- * the reply (or replies in pub/sub).
1182
- */
1183
- int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1184
- sds newbuf;
1185
-
1186
- newbuf = sdscatlen(c->obuf,cmd,len);
1187
- if (newbuf == NULL) {
1188
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1189
- return REDIS_ERR;
1190
- }
1191
-
1192
- c->obuf = newbuf;
1193
- return REDIS_OK;
1194
- }
1195
-
1196
- int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1197
- char *cmd;
1198
- int len;
1199
-
1200
- len = redisvFormatCommand(&cmd,format,ap);
1201
- if (len == -1) {
1202
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1203
- return REDIS_ERR;
1204
- }
1205
-
1206
- if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1207
- free(cmd);
1208
- return REDIS_ERR;
1209
- }
1210
-
1211
- free(cmd);
1212
- return REDIS_OK;
1213
- }
1214
-
1215
- int redisAppendCommand(redisContext *c, const char *format, ...) {
1216
- va_list ap;
1217
- int ret;
1218
-
1219
- va_start(ap,format);
1220
- ret = redisvAppendCommand(c,format,ap);
1221
- va_end(ap);
1222
- return ret;
1223
- }
1224
-
1225
- int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1226
- char *cmd;
1227
- int len;
1228
-
1229
- len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1230
- if (len == -1) {
1231
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1232
- return REDIS_ERR;
1233
- }
1234
-
1235
- if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1236
- free(cmd);
1237
- return REDIS_ERR;
1238
- }
1239
-
1240
- free(cmd);
1241
- return REDIS_OK;
1242
- }
1243
-
1244
- /* Helper function for the redisCommand* family of functions.
1245
- *
1246
- * Write a formatted command to the output buffer. If the given context is
1247
- * blocking, immediately read the reply into the "reply" pointer. When the
1248
- * context is non-blocking, the "reply" pointer will not be used and the
1249
- * command is simply appended to the write buffer.
1250
- *
1251
- * Returns the reply when a reply was succesfully retrieved. Returns NULL
1252
- * otherwise. When NULL is returned in a blocking context, the error field
1253
- * in the context will be set.
1254
- */
1255
- static void *__redisBlockForReply(redisContext *c) {
1256
- void *reply;
1257
-
1258
- if (c->flags & REDIS_BLOCK) {
1259
- if (redisGetReply(c,&reply) != REDIS_OK)
1260
- return NULL;
1261
- return reply;
1262
- }
1263
- return NULL;
1264
- }
1265
-
1266
- void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1267
- if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1268
- return NULL;
1269
- return __redisBlockForReply(c);
1270
- }
1271
-
1272
- void *redisCommand(redisContext *c, const char *format, ...) {
1273
- va_list ap;
1274
- void *reply = NULL;
1275
- va_start(ap,format);
1276
- reply = redisvCommand(c,format,ap);
1277
- va_end(ap);
1278
- return reply;
1279
- }
1280
-
1281
- void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1282
- if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1283
- return NULL;
1284
- return __redisBlockForReply(c);
1285
- }