]> git.saurik.com Git - redis.git/blob - deps/hiredis/hiredis.c
Add function to create ziplist-backed sorted set
[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[3]; /* 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 (r->fn) {
275 if (cur->type == REDIS_REPLY_INTEGER) {
276 obj = r->fn->createInteger(cur,readLongLong(p));
277 } else {
278 obj = r->fn->createString(cur,p,len);
279 }
280 } else {
281 obj = (void*)(size_t)(cur->type);
282 }
283
284 /* Set reply if this is the root object. */
285 if (r->ridx == 0) r->reply = obj;
286 moveToNextTask(r);
287 return 0;
288 }
289 return -1;
290 }
291
292 static int processBulkItem(redisReader *r) {
293 redisReadTask *cur = &(r->rstack[r->ridx]);
294 void *obj = NULL;
295 char *p, *s;
296 long len;
297 unsigned long bytelen;
298 int success = 0;
299
300 p = r->buf+r->pos;
301 s = seekNewline(p,r->len-r->pos);
302 if (s != NULL) {
303 p = r->buf+r->pos;
304 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
305 len = readLongLong(p);
306
307 if (len < 0) {
308 /* The nil object can always be created. */
309 obj = r->fn ? r->fn->createNil(cur) :
310 (void*)REDIS_REPLY_NIL;
311 success = 1;
312 } else {
313 /* Only continue when the buffer contains the entire bulk item. */
314 bytelen += len+2; /* include \r\n */
315 if (r->pos+bytelen <= r->len) {
316 obj = r->fn ? r->fn->createString(cur,s+2,len) :
317 (void*)REDIS_REPLY_STRING;
318 success = 1;
319 }
320 }
321
322 /* Proceed when obj was created. */
323 if (success) {
324 r->pos += bytelen;
325
326 /* Set reply if this is the root object. */
327 if (r->ridx == 0) r->reply = obj;
328 moveToNextTask(r);
329 return 0;
330 }
331 }
332 return -1;
333 }
334
335 static int processMultiBulkItem(redisReader *r) {
336 redisReadTask *cur = &(r->rstack[r->ridx]);
337 void *obj;
338 char *p;
339 long elements;
340 int root = 0;
341
342 /* Set error for nested multi bulks with depth > 1 */
343 if (r->ridx == 2) {
344 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
345 "No support for nested multi bulk replies with depth > 1"));
346 return -1;
347 }
348
349 if ((p = readLine(r,NULL)) != NULL) {
350 elements = readLongLong(p);
351 root = (r->ridx == 0);
352
353 if (elements == -1) {
354 obj = r->fn ? r->fn->createNil(cur) :
355 (void*)REDIS_REPLY_NIL;
356 moveToNextTask(r);
357 } else {
358 obj = r->fn ? r->fn->createArray(cur,elements) :
359 (void*)REDIS_REPLY_ARRAY;
360
361 /* Modify task stack when there are more than 0 elements. */
362 if (elements > 0) {
363 cur->elements = elements;
364 cur->obj = obj;
365 r->ridx++;
366 r->rstack[r->ridx].type = -1;
367 r->rstack[r->ridx].elements = -1;
368 r->rstack[r->ridx].idx = 0;
369 r->rstack[r->ridx].obj = NULL;
370 r->rstack[r->ridx].parent = cur;
371 r->rstack[r->ridx].privdata = r->privdata;
372 } else {
373 moveToNextTask(r);
374 }
375 }
376
377 /* Set reply if this is the root object. */
378 if (root) r->reply = obj;
379 return 0;
380 }
381 return -1;
382 }
383
384 static int processItem(redisReader *r) {
385 redisReadTask *cur = &(r->rstack[r->ridx]);
386 char *p;
387 sds byte;
388
389 /* check if we need to read type */
390 if (cur->type < 0) {
391 if ((p = readBytes(r,1)) != NULL) {
392 switch (p[0]) {
393 case '-':
394 cur->type = REDIS_REPLY_ERROR;
395 break;
396 case '+':
397 cur->type = REDIS_REPLY_STATUS;
398 break;
399 case ':':
400 cur->type = REDIS_REPLY_INTEGER;
401 break;
402 case '$':
403 cur->type = REDIS_REPLY_STRING;
404 break;
405 case '*':
406 cur->type = REDIS_REPLY_ARRAY;
407 break;
408 default:
409 byte = sdscatrepr(sdsempty(),p,1);
410 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
411 "Protocol error, got %s as reply type byte", byte));
412 sdsfree(byte);
413 return -1;
414 }
415 } else {
416 /* could not consume 1 byte */
417 return -1;
418 }
419 }
420
421 /* process typed item */
422 switch(cur->type) {
423 case REDIS_REPLY_ERROR:
424 case REDIS_REPLY_STATUS:
425 case REDIS_REPLY_INTEGER:
426 return processLineItem(r);
427 case REDIS_REPLY_STRING:
428 return processBulkItem(r);
429 case REDIS_REPLY_ARRAY:
430 return processMultiBulkItem(r);
431 default:
432 assert(NULL);
433 return -1;
434 }
435 }
436
437 void *redisReplyReaderCreate() {
438 redisReader *r = calloc(sizeof(redisReader),1);
439 r->error = NULL;
440 r->fn = &defaultFunctions;
441 r->buf = sdsempty();
442 r->ridx = -1;
443 return r;
444 }
445
446 /* Set the function set to build the reply. Returns REDIS_OK when there
447 * is no temporary object and it can be set, REDIS_ERR otherwise. */
448 int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
449 redisReader *r = reader;
450 if (r->reply == NULL) {
451 r->fn = fn;
452 return REDIS_OK;
453 }
454 return REDIS_ERR;
455 }
456
457 /* Set the private data field that is used in the read tasks. This argument can
458 * be used to curry arbitrary data to the custom reply object functions. */
459 int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
460 redisReader *r = reader;
461 if (r->reply == NULL) {
462 r->privdata = privdata;
463 return REDIS_OK;
464 }
465 return REDIS_ERR;
466 }
467
468 /* External libraries wrapping hiredis might need access to the temporary
469 * variable while the reply is built up. When the reader contains an
470 * object in between receiving some bytes to parse, this object might
471 * otherwise be free'd by garbage collection. */
472 void *redisReplyReaderGetObject(void *reader) {
473 redisReader *r = reader;
474 return r->reply;
475 }
476
477 void redisReplyReaderFree(void *reader) {
478 redisReader *r = reader;
479 if (r->error != NULL)
480 sdsfree(r->error);
481 if (r->reply != NULL && r->fn)
482 r->fn->freeObject(r->reply);
483 if (r->buf != NULL)
484 sdsfree(r->buf);
485 free(r);
486 }
487
488 static void redisSetReplyReaderError(redisReader *r, sds err) {
489 if (r->reply != NULL)
490 r->fn->freeObject(r->reply);
491
492 /* Clear remaining buffer when we see a protocol error. */
493 if (r->buf != NULL) {
494 sdsfree(r->buf);
495 r->buf = sdsempty();
496 r->pos = 0;
497 }
498 r->ridx = -1;
499 r->error = err;
500 }
501
502 char *redisReplyReaderGetError(void *reader) {
503 redisReader *r = reader;
504 return r->error;
505 }
506
507 void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
508 redisReader *r = reader;
509
510 /* Copy the provided buffer. */
511 if (buf != NULL && len >= 1) {
512 r->buf = sdscatlen(r->buf,buf,len);
513 r->len = sdslen(r->buf);
514 }
515 }
516
517 int redisReplyReaderGetReply(void *reader, void **reply) {
518 redisReader *r = reader;
519 if (reply != NULL) *reply = NULL;
520
521 /* When the buffer is empty, there will never be a reply. */
522 if (r->len == 0)
523 return REDIS_OK;
524
525 /* Set first item to process when the stack is empty. */
526 if (r->ridx == -1) {
527 r->rstack[0].type = -1;
528 r->rstack[0].elements = -1;
529 r->rstack[0].idx = -1;
530 r->rstack[0].obj = NULL;
531 r->rstack[0].parent = NULL;
532 r->rstack[0].privdata = r->privdata;
533 r->ridx = 0;
534 }
535
536 /* Process items in reply. */
537 while (r->ridx >= 0)
538 if (processItem(r) < 0)
539 break;
540
541 /* Discard the consumed part of the buffer. */
542 if (r->pos > 0) {
543 if (r->pos == r->len) {
544 /* sdsrange has a quirck on this edge case. */
545 sdsfree(r->buf);
546 r->buf = sdsempty();
547 } else {
548 r->buf = sdsrange(r->buf,r->pos,r->len);
549 }
550 r->pos = 0;
551 r->len = sdslen(r->buf);
552 }
553
554 /* Emit a reply when there is one. */
555 if (r->ridx == -1) {
556 void *aux = r->reply;
557 r->reply = NULL;
558
559 /* Destroy the buffer when it is empty and is quite large. */
560 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
561 sdsfree(r->buf);
562 r->buf = sdsempty();
563 r->pos = 0;
564 }
565
566 /* Check if there actually *is* a reply. */
567 if (r->error != NULL) {
568 return REDIS_ERR;
569 } else {
570 if (reply != NULL) *reply = aux;
571 }
572 }
573 return REDIS_OK;
574 }
575
576 /* Calculate the number of bytes needed to represent an integer as string. */
577 static int intlen(int i) {
578 int len = 0;
579 if (i < 0) {
580 len++;
581 i = -i;
582 }
583 do {
584 len++;
585 i /= 10;
586 } while(i);
587 return len;
588 }
589
590 /* Helper function for redisvFormatCommand(). */
591 static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
592 (*argc)++;
593 if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
594 if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
595 (*argv)[(*argc)-1] = a;
596 }
597
598 int redisvFormatCommand(char **target, const char *format, va_list ap) {
599 size_t size;
600 const char *arg, *c = format;
601 char *cmd = NULL; /* final command */
602 int pos; /* position in final command */
603 sds current; /* current argument */
604 int interpolated = 0; /* did we do interpolation on an argument? */
605 char **argv = NULL;
606 int argc = 0, j;
607 int totlen = 0;
608
609 /* Abort if there is not target to set */
610 if (target == NULL)
611 return -1;
612
613 /* Build the command string accordingly to protocol */
614 current = sdsempty();
615 while(*c != '\0') {
616 if (*c != '%' || c[1] == '\0') {
617 if (*c == ' ') {
618 if (sdslen(current) != 0) {
619 addArgument(current, &argv, &argc, &totlen);
620 current = sdsempty();
621 interpolated = 0;
622 }
623 } else {
624 current = sdscatlen(current,c,1);
625 }
626 } else {
627 switch(c[1]) {
628 case 's':
629 arg = va_arg(ap,char*);
630 size = strlen(arg);
631 if (size > 0)
632 current = sdscatlen(current,arg,size);
633 interpolated = 1;
634 break;
635 case 'b':
636 arg = va_arg(ap,char*);
637 size = va_arg(ap,size_t);
638 if (size > 0)
639 current = sdscatlen(current,arg,size);
640 interpolated = 1;
641 break;
642 case '%':
643 current = sdscat(current,"%");
644 break;
645 default:
646 /* Try to detect printf format */
647 {
648 char _format[16];
649 const char *_p = c+1;
650 size_t _l = 0;
651 va_list _cpy;
652
653 /* Flags */
654 if (*_p != '\0' && *_p == '#') _p++;
655 if (*_p != '\0' && *_p == '0') _p++;
656 if (*_p != '\0' && *_p == '-') _p++;
657 if (*_p != '\0' && *_p == ' ') _p++;
658 if (*_p != '\0' && *_p == '+') _p++;
659
660 /* Field width */
661 while (*_p != '\0' && isdigit(*_p)) _p++;
662
663 /* Precision */
664 if (*_p == '.') {
665 _p++;
666 while (*_p != '\0' && isdigit(*_p)) _p++;
667 }
668
669 /* Modifiers */
670 if (*_p != '\0') {
671 if (*_p == 'h' || *_p == 'l') {
672 /* Allow a single repetition for these modifiers */
673 if (_p[0] == _p[1]) _p++;
674 _p++;
675 }
676 }
677
678 /* Conversion specifier */
679 if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
680 _l = (_p+1)-c;
681 if (_l < sizeof(_format)-2) {
682 memcpy(_format,c,_l);
683 _format[_l] = '\0';
684 va_copy(_cpy,ap);
685 current = sdscatvprintf(current,_format,_cpy);
686 interpolated = 1;
687 va_end(_cpy);
688
689 /* Update current position (note: outer blocks
690 * increment c twice so compensate here) */
691 c = _p-1;
692 }
693 }
694
695 /* Consume and discard vararg */
696 va_arg(ap,void);
697 }
698 }
699 c++;
700 }
701 c++;
702 }
703
704 /* Add the last argument if needed */
705 if (interpolated || sdslen(current) != 0) {
706 addArgument(current, &argv, &argc, &totlen);
707 } else {
708 sdsfree(current);
709 }
710
711 /* Add bytes needed to hold multi bulk count */
712 totlen += 1+intlen(argc)+2;
713
714 /* Build the command at protocol level */
715 cmd = malloc(totlen+1);
716 if (!cmd) redisOOM();
717 pos = sprintf(cmd,"*%d\r\n",argc);
718 for (j = 0; j < argc; j++) {
719 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
720 memcpy(cmd+pos,argv[j],sdslen(argv[j]));
721 pos += sdslen(argv[j]);
722 sdsfree(argv[j]);
723 cmd[pos++] = '\r';
724 cmd[pos++] = '\n';
725 }
726 assert(pos == totlen);
727 free(argv);
728 cmd[totlen] = '\0';
729 *target = cmd;
730 return totlen;
731 }
732
733 /* Format a command according to the Redis protocol. This function
734 * takes a format similar to printf:
735 *
736 * %s represents a C null terminated string you want to interpolate
737 * %b represents a binary safe string
738 *
739 * When using %b you need to provide both the pointer to the string
740 * and the length in bytes. Examples:
741 *
742 * len = redisFormatCommand(target, "GET %s", mykey);
743 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
744 */
745 int redisFormatCommand(char **target, const char *format, ...) {
746 va_list ap;
747 int len;
748 va_start(ap,format);
749 len = redisvFormatCommand(target,format,ap);
750 va_end(ap);
751 return len;
752 }
753
754 /* Format a command according to the Redis protocol. This function takes the
755 * number of arguments, an array with arguments and an array with their
756 * lengths. If the latter is set to NULL, strlen will be used to compute the
757 * argument lengths.
758 */
759 int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
760 char *cmd = NULL; /* final command */
761 int pos; /* position in final command */
762 size_t len;
763 int totlen, j;
764
765 /* Calculate number of bytes needed for the command */
766 totlen = 1+intlen(argc)+2;
767 for (j = 0; j < argc; j++) {
768 len = argvlen ? argvlen[j] : strlen(argv[j]);
769 totlen += 1+intlen(len)+2+len+2;
770 }
771
772 /* Build the command at protocol level */
773 cmd = malloc(totlen+1);
774 if (!cmd) redisOOM();
775 pos = sprintf(cmd,"*%d\r\n",argc);
776 for (j = 0; j < argc; j++) {
777 len = argvlen ? argvlen[j] : strlen(argv[j]);
778 pos += sprintf(cmd+pos,"$%zu\r\n",len);
779 memcpy(cmd+pos,argv[j],len);
780 pos += len;
781 cmd[pos++] = '\r';
782 cmd[pos++] = '\n';
783 }
784 assert(pos == totlen);
785 cmd[totlen] = '\0';
786 *target = cmd;
787 return totlen;
788 }
789
790 void __redisSetError(redisContext *c, int type, const sds errstr) {
791 c->err = type;
792 if (errstr != NULL) {
793 c->errstr = errstr;
794 } else {
795 /* Only REDIS_ERR_IO may lack a description! */
796 assert(type == REDIS_ERR_IO);
797 c->errstr = sdsnew(strerror(errno));
798 }
799 }
800
801 static redisContext *redisContextInit() {
802 redisContext *c = calloc(sizeof(redisContext),1);
803 c->err = 0;
804 c->errstr = NULL;
805 c->obuf = sdsempty();
806 c->fn = &defaultFunctions;
807 c->reader = NULL;
808 return c;
809 }
810
811 void redisFree(redisContext *c) {
812 /* Disconnect before free'ing if not yet disconnected. */
813 if (c->flags & REDIS_CONNECTED)
814 close(c->fd);
815 if (c->errstr != NULL)
816 sdsfree(c->errstr);
817 if (c->obuf != NULL)
818 sdsfree(c->obuf);
819 if (c->reader != NULL)
820 redisReplyReaderFree(c->reader);
821 free(c);
822 }
823
824 /* Connect to a Redis instance. On error the field error in the returned
825 * context will be set to the return value of the error function.
826 * When no set of reply functions is given, the default set will be used. */
827 redisContext *redisConnect(const char *ip, int port) {
828 redisContext *c = redisContextInit();
829 c->flags |= REDIS_BLOCK;
830 redisContextConnectTcp(c,ip,port);
831 return c;
832 }
833
834 redisContext *redisConnectNonBlock(const char *ip, int port) {
835 redisContext *c = redisContextInit();
836 c->flags &= ~REDIS_BLOCK;
837 redisContextConnectTcp(c,ip,port);
838 return c;
839 }
840
841 redisContext *redisConnectUnix(const char *path) {
842 redisContext *c = redisContextInit();
843 c->flags |= REDIS_BLOCK;
844 redisContextConnectUnix(c,path);
845 return c;
846 }
847
848 redisContext *redisConnectUnixNonBlock(const char *path) {
849 redisContext *c = redisContextInit();
850 c->flags &= ~REDIS_BLOCK;
851 redisContextConnectUnix(c,path);
852 return c;
853 }
854
855 /* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
856 * was already initialized and the function set could not be re-set.
857 * Return REDIS_OK when they could be set. */
858 int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
859 if (c->reader != NULL)
860 return REDIS_ERR;
861 c->fn = fn;
862 return REDIS_OK;
863 }
864
865 /* Helper function to lazily create a reply reader. */
866 static void __redisCreateReplyReader(redisContext *c) {
867 if (c->reader == NULL) {
868 c->reader = redisReplyReaderCreate();
869 assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
870 }
871 }
872
873 /* Use this function to handle a read event on the descriptor. It will try
874 * and read some bytes from the socket and feed them to the reply parser.
875 *
876 * After this function is called, you may use redisContextReadReply to
877 * see if there is a reply available. */
878 int redisBufferRead(redisContext *c) {
879 char buf[2048];
880 int nread = read(c->fd,buf,sizeof(buf));
881 if (nread == -1) {
882 if (errno == EAGAIN) {
883 /* Try again later */
884 } else {
885 __redisSetError(c,REDIS_ERR_IO,NULL);
886 return REDIS_ERR;
887 }
888 } else if (nread == 0) {
889 __redisSetError(c,REDIS_ERR_EOF,
890 sdsnew("Server closed the connection"));
891 return REDIS_ERR;
892 } else {
893 __redisCreateReplyReader(c);
894 redisReplyReaderFeed(c->reader,buf,nread);
895 }
896 return REDIS_OK;
897 }
898
899 /* Write the output buffer to the socket.
900 *
901 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
902 * succesfully written to the socket. When the buffer is empty after the
903 * write operation, "wdone" is set to 1 (if given).
904 *
905 * Returns REDIS_ERR if an error occured trying to write and sets
906 * c->error to hold the appropriate error string.
907 */
908 int redisBufferWrite(redisContext *c, int *done) {
909 int nwritten;
910 if (sdslen(c->obuf) > 0) {
911 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
912 if (nwritten == -1) {
913 if (errno == EAGAIN) {
914 /* Try again later */
915 } else {
916 __redisSetError(c,REDIS_ERR_IO,NULL);
917 return REDIS_ERR;
918 }
919 } else if (nwritten > 0) {
920 if (nwritten == (signed)sdslen(c->obuf)) {
921 sdsfree(c->obuf);
922 c->obuf = sdsempty();
923 } else {
924 c->obuf = sdsrange(c->obuf,nwritten,-1);
925 }
926 }
927 }
928 if (done != NULL) *done = (sdslen(c->obuf) == 0);
929 return REDIS_OK;
930 }
931
932 /* Internal helper function to try and get a reply from the reader,
933 * or set an error in the context otherwise. */
934 int redisGetReplyFromReader(redisContext *c, void **reply) {
935 __redisCreateReplyReader(c);
936 if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
937 __redisSetError(c,REDIS_ERR_PROTOCOL,
938 sdsnew(((redisReader*)c->reader)->error));
939 return REDIS_ERR;
940 }
941 return REDIS_OK;
942 }
943
944 int redisGetReply(redisContext *c, void **reply) {
945 int wdone = 0;
946 void *aux = NULL;
947
948 /* Try to read pending replies */
949 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
950 return REDIS_ERR;
951
952 /* For the blocking context, flush output buffer and read reply */
953 if (aux == NULL && c->flags & REDIS_BLOCK) {
954 /* Write until done */
955 do {
956 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
957 return REDIS_ERR;
958 } while (!wdone);
959
960 /* Read until there is a reply */
961 do {
962 if (redisBufferRead(c) == REDIS_ERR)
963 return REDIS_ERR;
964 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
965 return REDIS_ERR;
966 } while (aux == NULL);
967 }
968
969 /* Set reply object */
970 if (reply != NULL) *reply = aux;
971 return REDIS_OK;
972 }
973
974
975 /* Helper function for the redisAppendCommand* family of functions.
976 *
977 * Write a formatted command to the output buffer. When this family
978 * is used, you need to call redisGetReply yourself to retrieve
979 * the reply (or replies in pub/sub).
980 */
981 void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
982 c->obuf = sdscatlen(c->obuf,cmd,len);
983 }
984
985 void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
986 char *cmd;
987 int len;
988 len = redisvFormatCommand(&cmd,format,ap);
989 __redisAppendCommand(c,cmd,len);
990 free(cmd);
991 }
992
993 void redisAppendCommand(redisContext *c, const char *format, ...) {
994 va_list ap;
995 va_start(ap,format);
996 redisvAppendCommand(c,format,ap);
997 va_end(ap);
998 }
999
1000 void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1001 char *cmd;
1002 int len;
1003 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1004 __redisAppendCommand(c,cmd,len);
1005 free(cmd);
1006 }
1007
1008 /* Helper function for the redisCommand* family of functions.
1009 *
1010 * Write a formatted command to the output buffer. If the given context is
1011 * blocking, immediately read the reply into the "reply" pointer. When the
1012 * context is non-blocking, the "reply" pointer will not be used and the
1013 * command is simply appended to the write buffer.
1014 *
1015 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1016 * otherwise. When NULL is returned in a blocking context, the error field
1017 * in the context will be set.
1018 */
1019 static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
1020 void *aux = NULL;
1021 __redisAppendCommand(c,cmd,len);
1022
1023 if (c->flags & REDIS_BLOCK) {
1024 if (redisGetReply(c,&aux) == REDIS_OK)
1025 return aux;
1026 return NULL;
1027 }
1028 return NULL;
1029 }
1030
1031 void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1032 char *cmd;
1033 int len;
1034 void *reply = NULL;
1035 len = redisvFormatCommand(&cmd,format,ap);
1036 reply = __redisCommand(c,cmd,len);
1037 free(cmd);
1038 return reply;
1039 }
1040
1041 void *redisCommand(redisContext *c, const char *format, ...) {
1042 va_list ap;
1043 void *reply = NULL;
1044 va_start(ap,format);
1045 reply = redisvCommand(c,format,ap);
1046 va_end(ap);
1047 return reply;
1048 }
1049
1050 void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1051 char *cmd;
1052 int len;
1053 void *reply = NULL;
1054 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1055 reply = __redisCommand(c,cmd,len);
1056 free(cmd);
1057 return reply;
1058 }