threadsafe-hiredis 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1285 @@
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
+ }