]> git.saurik.com Git - redis.git/blob - deps/hiredis/hiredis.c
master branch merged into scripting.
[redis.git] / deps / hiredis / hiredis.c
1 /*
2 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3 * Copyright (c) 2010, 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 <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <errno.h>
37 #include <ctype.h>
38
39 #include "hiredis.h"
40 #include "net.h"
41 #include "sds.h"
42 #include "util.h"
43
44 typedef struct redisReader {
45 struct redisReplyObjectFunctions *fn;
46 sds error; /* holds optional error */
47 void *reply; /* holds temporary reply */
48
49 sds buf; /* read buffer */
50 size_t pos; /* buffer cursor */
51 size_t len; /* buffer length */
52
53 redisReadTask rstack[9]; /* stack of read tasks */
54 int ridx; /* index of stack */
55 void *privdata; /* user-settable arbitrary field */
56 } redisReader;
57
58 static redisReply *createReplyObject(int type);
59 static void *createStringObject(const redisReadTask *task, char *str, size_t len);
60 static void *createArrayObject(const redisReadTask *task, int elements);
61 static void *createIntegerObject(const redisReadTask *task, long long value);
62 static void *createNilObject(const redisReadTask *task);
63 static void redisSetReplyReaderError(redisReader *r, sds err);
64
65 /* Default set of functions to build the reply. */
66 static redisReplyObjectFunctions defaultFunctions = {
67 createStringObject,
68 createArrayObject,
69 createIntegerObject,
70 createNilObject,
71 freeReplyObject
72 };
73
74 /* Create a reply object */
75 static redisReply *createReplyObject(int type) {
76 redisReply *r = malloc(sizeof(*r));
77
78 if (!r) redisOOM();
79 r->type = type;
80 return r;
81 }
82
83 /* Free a reply object */
84 void freeReplyObject(void *reply) {
85 redisReply *r = reply;
86 size_t j;
87
88 switch(r->type) {
89 case REDIS_REPLY_INTEGER:
90 break; /* Nothing to free */
91 case REDIS_REPLY_ARRAY:
92 for (j = 0; j < r->elements; j++)
93 if (r->element[j]) freeReplyObject(r->element[j]);
94 free(r->element);
95 break;
96 case REDIS_REPLY_ERROR:
97 case REDIS_REPLY_STATUS:
98 case REDIS_REPLY_STRING:
99 free(r->str);
100 break;
101 }
102 free(r);
103 }
104
105 static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
106 redisReply *r = createReplyObject(task->type);
107 char *value = malloc(len+1);
108 if (!value) redisOOM();
109 assert(task->type == REDIS_REPLY_ERROR ||
110 task->type == REDIS_REPLY_STATUS ||
111 task->type == REDIS_REPLY_STRING);
112
113 /* Copy string value */
114 memcpy(value,str,len);
115 value[len] = '\0';
116 r->str = value;
117 r->len = len;
118
119 if (task->parent) {
120 redisReply *parent = task->parent->obj;
121 assert(parent->type == REDIS_REPLY_ARRAY);
122 parent->element[task->idx] = r;
123 }
124 return r;
125 }
126
127 static void *createArrayObject(const redisReadTask *task, int elements) {
128 redisReply *r = createReplyObject(REDIS_REPLY_ARRAY);
129 r->elements = elements;
130 if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
131 redisOOM();
132 if (task->parent) {
133 redisReply *parent = task->parent->obj;
134 assert(parent->type == REDIS_REPLY_ARRAY);
135 parent->element[task->idx] = r;
136 }
137 return r;
138 }
139
140 static void *createIntegerObject(const redisReadTask *task, long long value) {
141 redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
142 r->integer = value;
143 if (task->parent) {
144 redisReply *parent = task->parent->obj;
145 assert(parent->type == REDIS_REPLY_ARRAY);
146 parent->element[task->idx] = r;
147 }
148 return r;
149 }
150
151 static void *createNilObject(const redisReadTask *task) {
152 redisReply *r = createReplyObject(REDIS_REPLY_NIL);
153 if (task->parent) {
154 redisReply *parent = task->parent->obj;
155 assert(parent->type == REDIS_REPLY_ARRAY);
156 parent->element[task->idx] = r;
157 }
158 return r;
159 }
160
161 static char *readBytes(redisReader *r, unsigned int bytes) {
162 char *p;
163 if (r->len-r->pos >= bytes) {
164 p = r->buf+r->pos;
165 r->pos += bytes;
166 return p;
167 }
168 return NULL;
169 }
170
171 /* Find pointer to \r\n. */
172 static char *seekNewline(char *s, size_t len) {
173 int pos = 0;
174 int _len = len-1;
175
176 /* Position should be < len-1 because the character at "pos" should be
177 * followed by a \n. Note that strchr cannot be used because it doesn't
178 * allow to search a limited length and the buffer that is being searched
179 * might not have a trailing NULL character. */
180 while (pos < _len) {
181 while(pos < _len && s[pos] != '\r') pos++;
182 if (s[pos] != '\r') {
183 /* Not found. */
184 return NULL;
185 } else {
186 if (s[pos+1] == '\n') {
187 /* Found. */
188 return s+pos;
189 } else {
190 /* Continue searching. */
191 pos++;
192 }
193 }
194 }
195 return NULL;
196 }
197
198 /* Read a long long value starting at *s, under the assumption that it will be
199 * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
200 static long long readLongLong(char *s) {
201 long long v = 0;
202 int dec, mult = 1;
203 char c;
204
205 if (*s == '-') {
206 mult = -1;
207 s++;
208 } else if (*s == '+') {
209 mult = 1;
210 s++;
211 }
212
213 while ((c = *(s++)) != '\r') {
214 dec = c - '0';
215 if (dec >= 0 && dec < 10) {
216 v *= 10;
217 v += dec;
218 } else {
219 /* Should not happen... */
220 return -1;
221 }
222 }
223
224 return mult*v;
225 }
226
227 static char *readLine(redisReader *r, int *_len) {
228 char *p, *s;
229 int len;
230
231 p = r->buf+r->pos;
232 s = seekNewline(p,(r->len-r->pos));
233 if (s != NULL) {
234 len = s-(r->buf+r->pos);
235 r->pos += len+2; /* skip \r\n */
236 if (_len) *_len = len;
237 return p;
238 }
239 return NULL;
240 }
241
242 static void moveToNextTask(redisReader *r) {
243 redisReadTask *cur, *prv;
244 while (r->ridx >= 0) {
245 /* Return a.s.a.p. when the stack is now empty. */
246 if (r->ridx == 0) {
247 r->ridx--;
248 return;
249 }
250
251 cur = &(r->rstack[r->ridx]);
252 prv = &(r->rstack[r->ridx-1]);
253 assert(prv->type == REDIS_REPLY_ARRAY);
254 if (cur->idx == prv->elements-1) {
255 r->ridx--;
256 } else {
257 /* Reset the type because the next item can be anything */
258 assert(cur->idx < prv->elements);
259 cur->type = -1;
260 cur->elements = -1;
261 cur->idx++;
262 return;
263 }
264 }
265 }
266
267 static int processLineItem(redisReader *r) {
268 redisReadTask *cur = &(r->rstack[r->ridx]);
269 void *obj;
270 char *p;
271 int len;
272
273 if ((p = readLine(r,&len)) != NULL) {
274 if (cur->type == REDIS_REPLY_INTEGER) {
275 if (r->fn && r->fn->createInteger)
276 obj = r->fn->createInteger(cur,readLongLong(p));
277 else
278 obj = (void*)REDIS_REPLY_INTEGER;
279 } else {
280 /* Type will be error or status. */
281 if (r->fn && r->fn->createString)
282 obj = r->fn->createString(cur,p,len);
283 else
284 obj = (void*)(size_t)(cur->type);
285 }
286
287 /* Set reply if this is the root object. */
288 if (r->ridx == 0) r->reply = obj;
289 moveToNextTask(r);
290 return 0;
291 }
292 return -1;
293 }
294
295 static int processBulkItem(redisReader *r) {
296 redisReadTask *cur = &(r->rstack[r->ridx]);
297 void *obj = NULL;
298 char *p, *s;
299 long len;
300 unsigned long bytelen;
301 int success = 0;
302
303 p = r->buf+r->pos;
304 s = seekNewline(p,r->len-r->pos);
305 if (s != NULL) {
306 p = r->buf+r->pos;
307 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
308 len = readLongLong(p);
309
310 if (len < 0) {
311 /* The nil object can always be created. */
312 if (r->fn && r->fn->createNil)
313 obj = r->fn->createNil(cur);
314 else
315 obj = (void*)REDIS_REPLY_NIL;
316 success = 1;
317 } else {
318 /* Only continue when the buffer contains the entire bulk item. */
319 bytelen += len+2; /* include \r\n */
320 if (r->pos+bytelen <= r->len) {
321 if (r->fn && r->fn->createString)
322 obj = r->fn->createString(cur,s+2,len);
323 else
324 obj = (void*)REDIS_REPLY_STRING;
325 success = 1;
326 }
327 }
328
329 /* Proceed when obj was created. */
330 if (success) {
331 r->pos += bytelen;
332
333 /* Set reply if this is the root object. */
334 if (r->ridx == 0) r->reply = obj;
335 moveToNextTask(r);
336 return 0;
337 }
338 }
339 return -1;
340 }
341
342 static int processMultiBulkItem(redisReader *r) {
343 redisReadTask *cur = &(r->rstack[r->ridx]);
344 void *obj;
345 char *p;
346 long elements;
347 int root = 0;
348
349 /* Set error for nested multi bulks with depth > 1 */
350 if (r->ridx == 8) {
351 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
352 "No support for nested multi bulk replies with depth > 7"));
353 return -1;
354 }
355
356 if ((p = readLine(r,NULL)) != NULL) {
357 elements = readLongLong(p);
358 root = (r->ridx == 0);
359
360 if (elements == -1) {
361 if (r->fn && r->fn->createNil)
362 obj = r->fn->createNil(cur);
363 else
364 obj = (void*)REDIS_REPLY_NIL;
365 moveToNextTask(r);
366 } else {
367 if (r->fn && r->fn->createArray)
368 obj = r->fn->createArray(cur,elements);
369 else
370 obj = (void*)REDIS_REPLY_ARRAY;
371
372 /* Modify task stack when there are more than 0 elements. */
373 if (elements > 0) {
374 cur->elements = elements;
375 cur->obj = obj;
376 r->ridx++;
377 r->rstack[r->ridx].type = -1;
378 r->rstack[r->ridx].elements = -1;
379 r->rstack[r->ridx].idx = 0;
380 r->rstack[r->ridx].obj = NULL;
381 r->rstack[r->ridx].parent = cur;
382 r->rstack[r->ridx].privdata = r->privdata;
383 } else {
384 moveToNextTask(r);
385 }
386 }
387
388 /* Set reply if this is the root object. */
389 if (root) r->reply = obj;
390 return 0;
391 }
392 return -1;
393 }
394
395 static int processItem(redisReader *r) {
396 redisReadTask *cur = &(r->rstack[r->ridx]);
397 char *p;
398 sds byte;
399
400 /* check if we need to read type */
401 if (cur->type < 0) {
402 if ((p = readBytes(r,1)) != NULL) {
403 switch (p[0]) {
404 case '-':
405 cur->type = REDIS_REPLY_ERROR;
406 break;
407 case '+':
408 cur->type = REDIS_REPLY_STATUS;
409 break;
410 case ':':
411 cur->type = REDIS_REPLY_INTEGER;
412 break;
413 case '$':
414 cur->type = REDIS_REPLY_STRING;
415 break;
416 case '*':
417 cur->type = REDIS_REPLY_ARRAY;
418 break;
419 default:
420 byte = sdscatrepr(sdsempty(),p,1);
421 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
422 "Protocol error, got %s as reply type byte", byte));
423 sdsfree(byte);
424 return -1;
425 }
426 } else {
427 /* could not consume 1 byte */
428 return -1;
429 }
430 }
431
432 /* process typed item */
433 switch(cur->type) {
434 case REDIS_REPLY_ERROR:
435 case REDIS_REPLY_STATUS:
436 case REDIS_REPLY_INTEGER:
437 return processLineItem(r);
438 case REDIS_REPLY_STRING:
439 return processBulkItem(r);
440 case REDIS_REPLY_ARRAY:
441 return processMultiBulkItem(r);
442 default:
443 assert(NULL);
444 return -1;
445 }
446 }
447
448 void *redisReplyReaderCreate(void) {
449 redisReader *r = calloc(sizeof(redisReader),1);
450 r->error = NULL;
451 r->fn = &defaultFunctions;
452 r->buf = sdsempty();
453 r->ridx = -1;
454 return r;
455 }
456
457 /* Set the function set to build the reply. Returns REDIS_OK when there
458 * is no temporary object and it can be set, REDIS_ERR otherwise. */
459 int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
460 redisReader *r = reader;
461 if (r->reply == NULL) {
462 r->fn = fn;
463 return REDIS_OK;
464 }
465 return REDIS_ERR;
466 }
467
468 /* Set the private data field that is used in the read tasks. This argument can
469 * be used to curry arbitrary data to the custom reply object functions. */
470 int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
471 redisReader *r = reader;
472 if (r->reply == NULL) {
473 r->privdata = privdata;
474 return REDIS_OK;
475 }
476 return REDIS_ERR;
477 }
478
479 /* External libraries wrapping hiredis might need access to the temporary
480 * variable while the reply is built up. When the reader contains an
481 * object in between receiving some bytes to parse, this object might
482 * otherwise be free'd by garbage collection. */
483 void *redisReplyReaderGetObject(void *reader) {
484 redisReader *r = reader;
485 return r->reply;
486 }
487
488 void redisReplyReaderFree(void *reader) {
489 redisReader *r = reader;
490 if (r->error != NULL)
491 sdsfree(r->error);
492 if (r->reply != NULL && r->fn)
493 r->fn->freeObject(r->reply);
494 if (r->buf != NULL)
495 sdsfree(r->buf);
496 free(r);
497 }
498
499 static void redisSetReplyReaderError(redisReader *r, sds err) {
500 if (r->reply != NULL)
501 r->fn->freeObject(r->reply);
502
503 /* Clear remaining buffer when we see a protocol error. */
504 if (r->buf != NULL) {
505 sdsfree(r->buf);
506 r->buf = sdsempty();
507 r->pos = r->len = 0;
508 }
509 r->ridx = -1;
510 r->error = err;
511 }
512
513 char *redisReplyReaderGetError(void *reader) {
514 redisReader *r = reader;
515 return r->error;
516 }
517
518 void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
519 redisReader *r = reader;
520
521 /* Copy the provided buffer. */
522 if (buf != NULL && len >= 1) {
523 /* Destroy internal buffer when it is empty and is quite large. */
524 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
525 sdsfree(r->buf);
526 r->buf = sdsempty();
527 r->pos = 0;
528 }
529
530 r->buf = sdscatlen(r->buf,buf,len);
531 r->len = sdslen(r->buf);
532 }
533 }
534
535 int redisReplyReaderGetReply(void *reader, void **reply) {
536 redisReader *r = reader;
537 if (reply != NULL) *reply = NULL;
538
539 /* When the buffer is empty, there will never be a reply. */
540 if (r->len == 0)
541 return REDIS_OK;
542
543 /* Set first item to process when the stack is empty. */
544 if (r->ridx == -1) {
545 r->rstack[0].type = -1;
546 r->rstack[0].elements = -1;
547 r->rstack[0].idx = -1;
548 r->rstack[0].obj = NULL;
549 r->rstack[0].parent = NULL;
550 r->rstack[0].privdata = r->privdata;
551 r->ridx = 0;
552 }
553
554 /* Process items in reply. */
555 while (r->ridx >= 0)
556 if (processItem(r) < 0)
557 break;
558
559 /* Discard part of the buffer when we've consumed at least 1k, to avoid
560 * doing unnecessary calls to memmove() in sds.c. */
561 if (r->pos >= 1024) {
562 r->buf = sdsrange(r->buf,r->pos,-1);
563 r->pos = 0;
564 r->len = sdslen(r->buf);
565 }
566
567 /* Emit a reply when there is one. */
568 if (r->ridx == -1) {
569 void *aux = r->reply;
570 r->reply = NULL;
571
572 /* Check if there actually *is* a reply. */
573 if (r->error != NULL) {
574 return REDIS_ERR;
575 } else {
576 if (reply != NULL) *reply = aux;
577 }
578 }
579 return REDIS_OK;
580 }
581
582 /* Calculate the number of bytes needed to represent an integer as string. */
583 static int intlen(int i) {
584 int len = 0;
585 if (i < 0) {
586 len++;
587 i = -i;
588 }
589 do {
590 len++;
591 i /= 10;
592 } while(i);
593 return len;
594 }
595
596 /* Helper function for redisvFormatCommand(). */
597 static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
598 (*argc)++;
599 if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
600 if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
601 (*argv)[(*argc)-1] = a;
602 }
603
604 int redisvFormatCommand(char **target, const char *format, va_list ap) {
605 size_t size;
606 const char *arg, *c = format;
607 char *cmd = NULL; /* final command */
608 int pos; /* position in final command */
609 sds current; /* current argument */
610 int touched = 0; /* was the current argument touched? */
611 char **argv = NULL;
612 int argc = 0, j;
613 int totlen = 0;
614
615 /* Abort if there is not target to set */
616 if (target == NULL)
617 return -1;
618
619 /* Build the command string accordingly to protocol */
620 current = sdsempty();
621 while(*c != '\0') {
622 if (*c != '%' || c[1] == '\0') {
623 if (*c == ' ') {
624 if (touched) {
625 addArgument(current, &argv, &argc, &totlen);
626 current = sdsempty();
627 touched = 0;
628 }
629 } else {
630 current = sdscatlen(current,c,1);
631 touched = 1;
632 }
633 } else {
634 switch(c[1]) {
635 case 's':
636 arg = va_arg(ap,char*);
637 size = strlen(arg);
638 if (size > 0)
639 current = sdscatlen(current,arg,size);
640 break;
641 case 'b':
642 arg = va_arg(ap,char*);
643 size = va_arg(ap,size_t);
644 if (size > 0)
645 current = sdscatlen(current,arg,size);
646 break;
647 case '%':
648 current = sdscat(current,"%");
649 break;
650 default:
651 /* Try to detect printf format */
652 {
653 char _format[16];
654 const char *_p = c+1;
655 size_t _l = 0;
656 va_list _cpy;
657
658 /* Flags */
659 if (*_p != '\0' && *_p == '#') _p++;
660 if (*_p != '\0' && *_p == '0') _p++;
661 if (*_p != '\0' && *_p == '-') _p++;
662 if (*_p != '\0' && *_p == ' ') _p++;
663 if (*_p != '\0' && *_p == '+') _p++;
664
665 /* Field width */
666 while (*_p != '\0' && isdigit(*_p)) _p++;
667
668 /* Precision */
669 if (*_p == '.') {
670 _p++;
671 while (*_p != '\0' && isdigit(*_p)) _p++;
672 }
673
674 /* Modifiers */
675 if (*_p != '\0') {
676 if (*_p == 'h' || *_p == 'l') {
677 /* Allow a single repetition for these modifiers */
678 if (_p[0] == _p[1]) _p++;
679 _p++;
680 }
681 }
682
683 /* Conversion specifier */
684 if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
685 _l = (_p+1)-c;
686 if (_l < sizeof(_format)-2) {
687 memcpy(_format,c,_l);
688 _format[_l] = '\0';
689 va_copy(_cpy,ap);
690 current = sdscatvprintf(current,_format,_cpy);
691 va_end(_cpy);
692
693 /* Update current position (note: outer blocks
694 * increment c twice so compensate here) */
695 c = _p-1;
696 }
697 }
698
699 /* Consume and discard vararg */
700 va_arg(ap,void);
701 }
702 }
703 touched = 1;
704 c++;
705 }
706 c++;
707 }
708
709 /* Add the last argument if needed */
710 if (touched) {
711 addArgument(current, &argv, &argc, &totlen);
712 } else {
713 sdsfree(current);
714 }
715
716 /* Add bytes needed to hold multi bulk count */
717 totlen += 1+intlen(argc)+2;
718
719 /* Build the command at protocol level */
720 cmd = malloc(totlen+1);
721 if (!cmd) redisOOM();
722 pos = sprintf(cmd,"*%d\r\n",argc);
723 for (j = 0; j < argc; j++) {
724 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
725 memcpy(cmd+pos,argv[j],sdslen(argv[j]));
726 pos += sdslen(argv[j]);
727 sdsfree(argv[j]);
728 cmd[pos++] = '\r';
729 cmd[pos++] = '\n';
730 }
731 assert(pos == totlen);
732 free(argv);
733 cmd[totlen] = '\0';
734 *target = cmd;
735 return totlen;
736 }
737
738 /* Format a command according to the Redis protocol. This function
739 * takes a format similar to printf:
740 *
741 * %s represents a C null terminated string you want to interpolate
742 * %b represents a binary safe string
743 *
744 * When using %b you need to provide both the pointer to the string
745 * and the length in bytes. Examples:
746 *
747 * len = redisFormatCommand(target, "GET %s", mykey);
748 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
749 */
750 int redisFormatCommand(char **target, const char *format, ...) {
751 va_list ap;
752 int len;
753 va_start(ap,format);
754 len = redisvFormatCommand(target,format,ap);
755 va_end(ap);
756 return len;
757 }
758
759 /* Format a command according to the Redis protocol. This function takes the
760 * number of arguments, an array with arguments and an array with their
761 * lengths. If the latter is set to NULL, strlen will be used to compute the
762 * argument lengths.
763 */
764 int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
765 char *cmd = NULL; /* final command */
766 int pos; /* position in final command */
767 size_t len;
768 int totlen, j;
769
770 /* Calculate number of bytes needed for the command */
771 totlen = 1+intlen(argc)+2;
772 for (j = 0; j < argc; j++) {
773 len = argvlen ? argvlen[j] : strlen(argv[j]);
774 totlen += 1+intlen(len)+2+len+2;
775 }
776
777 /* Build the command at protocol level */
778 cmd = malloc(totlen+1);
779 if (!cmd) redisOOM();
780 pos = sprintf(cmd,"*%d\r\n",argc);
781 for (j = 0; j < argc; j++) {
782 len = argvlen ? argvlen[j] : strlen(argv[j]);
783 pos += sprintf(cmd+pos,"$%zu\r\n",len);
784 memcpy(cmd+pos,argv[j],len);
785 pos += len;
786 cmd[pos++] = '\r';
787 cmd[pos++] = '\n';
788 }
789 assert(pos == totlen);
790 cmd[totlen] = '\0';
791 *target = cmd;
792 return totlen;
793 }
794
795 void __redisSetError(redisContext *c, int type, const sds errstr) {
796 c->err = type;
797 if (errstr != NULL) {
798 c->errstr = errstr;
799 } else {
800 /* Only REDIS_ERR_IO may lack a description! */
801 assert(type == REDIS_ERR_IO);
802 c->errstr = sdsnew(strerror(errno));
803 }
804 }
805
806 static redisContext *redisContextInit(void) {
807 redisContext *c = calloc(sizeof(redisContext),1);
808 c->err = 0;
809 c->errstr = NULL;
810 c->obuf = sdsempty();
811 c->fn = &defaultFunctions;
812 c->reader = NULL;
813 return c;
814 }
815
816 void redisFree(redisContext *c) {
817 if (c->fd > 0)
818 close(c->fd);
819 if (c->errstr != NULL)
820 sdsfree(c->errstr);
821 if (c->obuf != NULL)
822 sdsfree(c->obuf);
823 if (c->reader != NULL)
824 redisReplyReaderFree(c->reader);
825 free(c);
826 }
827
828 /* Connect to a Redis instance. On error the field error in the returned
829 * context will be set to the return value of the error function.
830 * When no set of reply functions is given, the default set will be used. */
831 redisContext *redisConnect(const char *ip, int port) {
832 redisContext *c = redisContextInit();
833 c->flags |= REDIS_BLOCK;
834 redisContextConnectTcp(c,ip,port,NULL);
835 return c;
836 }
837
838 redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
839 redisContext *c = redisContextInit();
840 c->flags |= REDIS_BLOCK;
841 redisContextConnectTcp(c,ip,port,&tv);
842 return c;
843 }
844
845 redisContext *redisConnectNonBlock(const char *ip, int port) {
846 redisContext *c = redisContextInit();
847 c->flags &= ~REDIS_BLOCK;
848 redisContextConnectTcp(c,ip,port,NULL);
849 return c;
850 }
851
852 redisContext *redisConnectUnix(const char *path) {
853 redisContext *c = redisContextInit();
854 c->flags |= REDIS_BLOCK;
855 redisContextConnectUnix(c,path,NULL);
856 return c;
857 }
858
859 redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
860 redisContext *c = redisContextInit();
861 c->flags |= REDIS_BLOCK;
862 redisContextConnectUnix(c,path,&tv);
863 return c;
864 }
865
866 redisContext *redisConnectUnixNonBlock(const char *path) {
867 redisContext *c = redisContextInit();
868 c->flags &= ~REDIS_BLOCK;
869 redisContextConnectUnix(c,path,NULL);
870 return c;
871 }
872
873 /* Set read/write timeout on a blocking socket. */
874 int redisSetTimeout(redisContext *c, struct timeval tv) {
875 if (c->flags & REDIS_BLOCK)
876 return redisContextSetTimeout(c,tv);
877 return REDIS_ERR;
878 }
879
880 /* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
881 * was already initialized and the function set could not be re-set.
882 * Return REDIS_OK when they could be set. */
883 int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
884 if (c->reader != NULL)
885 return REDIS_ERR;
886 c->fn = fn;
887 return REDIS_OK;
888 }
889
890 /* Helper function to lazily create a reply reader. */
891 static void __redisCreateReplyReader(redisContext *c) {
892 if (c->reader == NULL) {
893 c->reader = redisReplyReaderCreate();
894 assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
895 }
896 }
897
898 /* Use this function to handle a read event on the descriptor. It will try
899 * and read some bytes from the socket and feed them to the reply parser.
900 *
901 * After this function is called, you may use redisContextReadReply to
902 * see if there is a reply available. */
903 int redisBufferRead(redisContext *c) {
904 char buf[2048];
905 int nread = read(c->fd,buf,sizeof(buf));
906 if (nread == -1) {
907 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
908 /* Try again later */
909 } else {
910 __redisSetError(c,REDIS_ERR_IO,NULL);
911 return REDIS_ERR;
912 }
913 } else if (nread == 0) {
914 __redisSetError(c,REDIS_ERR_EOF,
915 sdsnew("Server closed the connection"));
916 return REDIS_ERR;
917 } else {
918 __redisCreateReplyReader(c);
919 redisReplyReaderFeed(c->reader,buf,nread);
920 }
921 return REDIS_OK;
922 }
923
924 /* Write the output buffer to the socket.
925 *
926 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
927 * succesfully written to the socket. When the buffer is empty after the
928 * write operation, "wdone" is set to 1 (if given).
929 *
930 * Returns REDIS_ERR if an error occured trying to write and sets
931 * c->error to hold the appropriate error string.
932 */
933 int redisBufferWrite(redisContext *c, int *done) {
934 int nwritten;
935 if (sdslen(c->obuf) > 0) {
936 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
937 if (nwritten == -1) {
938 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
939 /* Try again later */
940 } else {
941 __redisSetError(c,REDIS_ERR_IO,NULL);
942 return REDIS_ERR;
943 }
944 } else if (nwritten > 0) {
945 if (nwritten == (signed)sdslen(c->obuf)) {
946 sdsfree(c->obuf);
947 c->obuf = sdsempty();
948 } else {
949 c->obuf = sdsrange(c->obuf,nwritten,-1);
950 }
951 }
952 }
953 if (done != NULL) *done = (sdslen(c->obuf) == 0);
954 return REDIS_OK;
955 }
956
957 /* Internal helper function to try and get a reply from the reader,
958 * or set an error in the context otherwise. */
959 int redisGetReplyFromReader(redisContext *c, void **reply) {
960 __redisCreateReplyReader(c);
961 if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
962 __redisSetError(c,REDIS_ERR_PROTOCOL,
963 sdsnew(((redisReader*)c->reader)->error));
964 return REDIS_ERR;
965 }
966 return REDIS_OK;
967 }
968
969 int redisGetReply(redisContext *c, void **reply) {
970 int wdone = 0;
971 void *aux = NULL;
972
973 /* Try to read pending replies */
974 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
975 return REDIS_ERR;
976
977 /* For the blocking context, flush output buffer and read reply */
978 if (aux == NULL && c->flags & REDIS_BLOCK) {
979 /* Write until done */
980 do {
981 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
982 return REDIS_ERR;
983 } while (!wdone);
984
985 /* Read until there is a reply */
986 do {
987 if (redisBufferRead(c) == REDIS_ERR)
988 return REDIS_ERR;
989 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
990 return REDIS_ERR;
991 } while (aux == NULL);
992 }
993
994 /* Set reply object */
995 if (reply != NULL) *reply = aux;
996 return REDIS_OK;
997 }
998
999
1000 /* Helper function for the redisAppendCommand* family of functions.
1001 *
1002 * Write a formatted command to the output buffer. When this family
1003 * is used, you need to call redisGetReply yourself to retrieve
1004 * the reply (or replies in pub/sub).
1005 */
1006 void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1007 c->obuf = sdscatlen(c->obuf,cmd,len);
1008 }
1009
1010 void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1011 char *cmd;
1012 int len;
1013 len = redisvFormatCommand(&cmd,format,ap);
1014 __redisAppendCommand(c,cmd,len);
1015 free(cmd);
1016 }
1017
1018 void redisAppendCommand(redisContext *c, const char *format, ...) {
1019 va_list ap;
1020 va_start(ap,format);
1021 redisvAppendCommand(c,format,ap);
1022 va_end(ap);
1023 }
1024
1025 void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1026 char *cmd;
1027 int len;
1028 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1029 __redisAppendCommand(c,cmd,len);
1030 free(cmd);
1031 }
1032
1033 /* Helper function for the redisCommand* family of functions.
1034 *
1035 * Write a formatted command to the output buffer. If the given context is
1036 * blocking, immediately read the reply into the "reply" pointer. When the
1037 * context is non-blocking, the "reply" pointer will not be used and the
1038 * command is simply appended to the write buffer.
1039 *
1040 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1041 * otherwise. When NULL is returned in a blocking context, the error field
1042 * in the context will be set.
1043 */
1044 static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
1045 void *aux = NULL;
1046 __redisAppendCommand(c,cmd,len);
1047
1048 if (c->flags & REDIS_BLOCK) {
1049 if (redisGetReply(c,&aux) == REDIS_OK)
1050 return aux;
1051 return NULL;
1052 }
1053 return NULL;
1054 }
1055
1056 void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1057 char *cmd;
1058 int len;
1059 void *reply = NULL;
1060 len = redisvFormatCommand(&cmd,format,ap);
1061 reply = __redisCommand(c,cmd,len);
1062 free(cmd);
1063 return reply;
1064 }
1065
1066 void *redisCommand(redisContext *c, const char *format, ...) {
1067 va_list ap;
1068 void *reply = NULL;
1069 va_start(ap,format);
1070 reply = redisvCommand(c,format,ap);
1071 va_end(ap);
1072 return reply;
1073 }
1074
1075 void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1076 char *cmd;
1077 int len;
1078 void *reply = NULL;
1079 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1080 reply = __redisCommand(c,cmd,len);
1081 free(cmd);
1082 return reply;
1083 }