aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/deps/hiredis/read.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/deps/hiredis/read.c')
-rw-r--r--examples/redis-unstable/deps/hiredis/read.c788
1 files changed, 788 insertions, 0 deletions
diff --git a/examples/redis-unstable/deps/hiredis/read.c b/examples/redis-unstable/deps/hiredis/read.c
new file mode 100644
index 0000000..dd038c5
--- /dev/null
+++ b/examples/redis-unstable/deps/hiredis/read.c
@@ -0,0 +1,788 @@
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#ifndef _MSC_VER
36#include <unistd.h>
37#include <strings.h>
38#endif
39#include <assert.h>
40#include <errno.h>
41#include <ctype.h>
42#include <limits.h>
43#include <math.h>
44
45#include "alloc.h"
46#include "read.h"
47#include "sds.h"
48#include "win32.h"
49
50/* Initial size of our nested reply stack and how much we grow it when needd */
51#define REDIS_READER_STACK_SIZE 9
52
53static void __redisReaderSetError(redisReader *r, int type, const char *str) {
54 size_t len;
55
56 if (r->reply != NULL && r->fn && r->fn->freeObject) {
57 r->fn->freeObject(r->reply);
58 r->reply = NULL;
59 }
60
61 /* Clear input buffer on errors. */
62 hi_sdsfree(r->buf);
63 r->buf = NULL;
64 r->pos = r->len = 0;
65
66 /* Reset task stack. */
67 r->ridx = -1;
68
69 /* Set error. */
70 r->err = type;
71 len = strlen(str);
72 len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
73 memcpy(r->errstr,str,len);
74 r->errstr[len] = '\0';
75}
76
77static size_t chrtos(char *buf, size_t size, char byte) {
78 size_t len = 0;
79
80 switch(byte) {
81 case '\\':
82 case '"':
83 len = snprintf(buf,size,"\"\\%c\"",byte);
84 break;
85 case '\n': len = snprintf(buf,size,"\"\\n\""); break;
86 case '\r': len = snprintf(buf,size,"\"\\r\""); break;
87 case '\t': len = snprintf(buf,size,"\"\\t\""); break;
88 case '\a': len = snprintf(buf,size,"\"\\a\""); break;
89 case '\b': len = snprintf(buf,size,"\"\\b\""); break;
90 default:
91 if (isprint(byte))
92 len = snprintf(buf,size,"\"%c\"",byte);
93 else
94 len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
95 break;
96 }
97
98 return len;
99}
100
101static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
102 char cbuf[8], sbuf[128];
103
104 chrtos(cbuf,sizeof(cbuf),byte);
105 snprintf(sbuf,sizeof(sbuf),
106 "Protocol error, got %s as reply type byte", cbuf);
107 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
108}
109
110static void __redisReaderSetErrorOOM(redisReader *r) {
111 __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
112}
113
114static char *readBytes(redisReader *r, unsigned int bytes) {
115 char *p;
116 if (r->len-r->pos >= bytes) {
117 p = r->buf+r->pos;
118 r->pos += bytes;
119 return p;
120 }
121 return NULL;
122}
123
124/* Find pointer to \r\n. */
125static char *seekNewline(char *s, size_t len) {
126 char *ret;
127
128 /* We cannot match with fewer than 2 bytes */
129 if (len < 2)
130 return NULL;
131
132 /* Search up to len - 1 characters */
133 len--;
134
135 /* Look for the \r */
136 while ((ret = memchr(s, '\r', len)) != NULL) {
137 if (ret[1] == '\n') {
138 /* Found. */
139 break;
140 }
141 /* Continue searching. */
142 ret++;
143 len -= ret - s;
144 s = ret;
145 }
146
147 return ret;
148}
149
150/* Convert a string into a long long. Returns REDIS_OK if the string could be
151 * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
152 * will be set to the parsed value when appropriate.
153 *
154 * Note that this function demands that the string strictly represents
155 * a long long: no spaces or other characters before or after the string
156 * representing the number are accepted, nor zeroes at the start if not
157 * for the string "0" representing the zero number.
158 *
159 * Because of its strictness, it is safe to use this function to check if
160 * you can convert a string into a long long, and obtain back the string
161 * from the number without any loss in the string representation. */
162static int string2ll(const char *s, size_t slen, long long *value) {
163 const char *p = s;
164 size_t plen = 0;
165 int negative = 0;
166 unsigned long long v;
167
168 if (plen == slen)
169 return REDIS_ERR;
170
171 /* Special case: first and only digit is 0. */
172 if (slen == 1 && p[0] == '0') {
173 if (value != NULL) *value = 0;
174 return REDIS_OK;
175 }
176
177 if (p[0] == '-') {
178 negative = 1;
179 p++; plen++;
180
181 /* Abort on only a negative sign. */
182 if (plen == slen)
183 return REDIS_ERR;
184 }
185
186 /* First digit should be 1-9, otherwise the string should just be 0. */
187 if (p[0] >= '1' && p[0] <= '9') {
188 v = p[0]-'0';
189 p++; plen++;
190 } else if (p[0] == '0' && slen == 1) {
191 *value = 0;
192 return REDIS_OK;
193 } else {
194 return REDIS_ERR;
195 }
196
197 while (plen < slen && p[0] >= '0' && p[0] <= '9') {
198 if (v > (ULLONG_MAX / 10)) /* Overflow. */
199 return REDIS_ERR;
200 v *= 10;
201
202 if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
203 return REDIS_ERR;
204 v += p[0]-'0';
205
206 p++; plen++;
207 }
208
209 /* Return if not all bytes were used. */
210 if (plen < slen)
211 return REDIS_ERR;
212
213 if (negative) {
214 if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
215 return REDIS_ERR;
216 if (value != NULL) *value = -v;
217 } else {
218 if (v > LLONG_MAX) /* Overflow. */
219 return REDIS_ERR;
220 if (value != NULL) *value = v;
221 }
222 return REDIS_OK;
223}
224
225static char *readLine(redisReader *r, int *_len) {
226 char *p, *s;
227 int len;
228
229 p = r->buf+r->pos;
230 s = seekNewline(p,(r->len-r->pos));
231 if (s != NULL) {
232 len = s-(r->buf+r->pos);
233 r->pos += len+2; /* skip \r\n */
234 if (_len) *_len = len;
235 return p;
236 }
237 return NULL;
238}
239
240static void moveToNextTask(redisReader *r) {
241 redisReadTask *cur, *prv;
242 while (r->ridx >= 0) {
243 /* Return a.s.a.p. when the stack is now empty. */
244 if (r->ridx == 0) {
245 r->ridx--;
246 return;
247 }
248
249 cur = r->task[r->ridx];
250 prv = r->task[r->ridx-1];
251 assert(prv->type == REDIS_REPLY_ARRAY ||
252 prv->type == REDIS_REPLY_MAP ||
253 prv->type == REDIS_REPLY_SET ||
254 prv->type == REDIS_REPLY_PUSH);
255 if (cur->idx == prv->elements-1) {
256 r->ridx--;
257 } else {
258 /* Reset the type because the next item can be anything */
259 assert(cur->idx < prv->elements);
260 cur->type = -1;
261 cur->elements = -1;
262 cur->idx++;
263 return;
264 }
265 }
266}
267
268static int processLineItem(redisReader *r) {
269 redisReadTask *cur = r->task[r->ridx];
270 void *obj;
271 char *p;
272 int len;
273
274 if ((p = readLine(r,&len)) != NULL) {
275 if (cur->type == REDIS_REPLY_INTEGER) {
276 long long v;
277
278 if (string2ll(p, len, &v) == REDIS_ERR) {
279 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
280 "Bad integer value");
281 return REDIS_ERR;
282 }
283
284 if (r->fn && r->fn->createInteger) {
285 obj = r->fn->createInteger(cur,v);
286 } else {
287 obj = (void*)REDIS_REPLY_INTEGER;
288 }
289 } else if (cur->type == REDIS_REPLY_DOUBLE) {
290 char buf[326], *eptr;
291 double d;
292
293 if ((size_t)len >= sizeof(buf)) {
294 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
295 "Double value is too large");
296 return REDIS_ERR;
297 }
298
299 memcpy(buf,p,len);
300 buf[len] = '\0';
301
302 if (len == 3 && strcasecmp(buf,"inf") == 0) {
303 d = INFINITY; /* Positive infinite. */
304 } else if (len == 4 && strcasecmp(buf,"-inf") == 0) {
305 d = -INFINITY; /* Negative infinite. */
306 } else if ((len == 3 && strcasecmp(buf,"nan") == 0) ||
307 (len == 4 && strcasecmp(buf, "-nan") == 0)) {
308 d = NAN; /* nan. */
309 } else {
310 d = strtod((char*)buf,&eptr);
311 /* RESP3 only allows "inf", "-inf", and finite values, while
312 * strtod() allows other variations on infinity,
313 * etc. We explicity handle our two allowed infinite cases and NaN
314 * above, so strtod() should only result in finite values. */
315 if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) {
316 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
317 "Bad double value");
318 return REDIS_ERR;
319 }
320 }
321
322 if (r->fn && r->fn->createDouble) {
323 obj = r->fn->createDouble(cur,d,buf,len);
324 } else {
325 obj = (void*)REDIS_REPLY_DOUBLE;
326 }
327 } else if (cur->type == REDIS_REPLY_NIL) {
328 if (len != 0) {
329 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
330 "Bad nil value");
331 return REDIS_ERR;
332 }
333
334 if (r->fn && r->fn->createNil)
335 obj = r->fn->createNil(cur);
336 else
337 obj = (void*)REDIS_REPLY_NIL;
338 } else if (cur->type == REDIS_REPLY_BOOL) {
339 int bval;
340
341 if (len != 1 || !strchr("tTfF", p[0])) {
342 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
343 "Bad bool value");
344 return REDIS_ERR;
345 }
346
347 bval = p[0] == 't' || p[0] == 'T';
348 if (r->fn && r->fn->createBool)
349 obj = r->fn->createBool(cur,bval);
350 else
351 obj = (void*)REDIS_REPLY_BOOL;
352 } else if (cur->type == REDIS_REPLY_BIGNUM) {
353 /* Ensure all characters are decimal digits (with possible leading
354 * minus sign). */
355 for (int i = 0; i < len; i++) {
356 /* XXX Consider: Allow leading '+'? Error on leading '0's? */
357 if (i == 0 && p[0] == '-') continue;
358 if (p[i] < '0' || p[i] > '9') {
359 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
360 "Bad bignum value");
361 return REDIS_ERR;
362 }
363 }
364 if (r->fn && r->fn->createString)
365 obj = r->fn->createString(cur,p,len);
366 else
367 obj = (void*)REDIS_REPLY_BIGNUM;
368 } else {
369 /* Type will be error or status. */
370 for (int i = 0; i < len; i++) {
371 if (p[i] == '\r' || p[i] == '\n') {
372 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
373 "Bad simple string value");
374 return REDIS_ERR;
375 }
376 }
377 if (r->fn && r->fn->createString)
378 obj = r->fn->createString(cur,p,len);
379 else
380 obj = (void*)(uintptr_t)(cur->type);
381 }
382
383 if (obj == NULL) {
384 __redisReaderSetErrorOOM(r);
385 return REDIS_ERR;
386 }
387
388 /* Set reply if this is the root object. */
389 if (r->ridx == 0) r->reply = obj;
390 moveToNextTask(r);
391 return REDIS_OK;
392 }
393
394 return REDIS_ERR;
395}
396
397static int processBulkItem(redisReader *r) {
398 redisReadTask *cur = r->task[r->ridx];
399 void *obj = NULL;
400 char *p, *s;
401 long long len;
402 unsigned long bytelen;
403 int success = 0;
404
405 p = r->buf+r->pos;
406 s = seekNewline(p,r->len-r->pos);
407 if (s != NULL) {
408 p = r->buf+r->pos;
409 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
410
411 if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
412 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
413 "Bad bulk string length");
414 return REDIS_ERR;
415 }
416
417 if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
418 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
419 "Bulk string length out of range");
420 return REDIS_ERR;
421 }
422
423 if (len == -1) {
424 /* The nil object can always be created. */
425 if (r->fn && r->fn->createNil)
426 obj = r->fn->createNil(cur);
427 else
428 obj = (void*)REDIS_REPLY_NIL;
429 success = 1;
430 } else {
431 /* Only continue when the buffer contains the entire bulk item. */
432 bytelen += len+2; /* include \r\n */
433 if (r->pos+bytelen <= r->len) {
434 if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
435 (cur->type == REDIS_REPLY_VERB && s[5] != ':'))
436 {
437 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
438 "Verbatim string 4 bytes of content type are "
439 "missing or incorrectly encoded.");
440 return REDIS_ERR;
441 }
442 if (r->fn && r->fn->createString)
443 obj = r->fn->createString(cur,s+2,len);
444 else
445 obj = (void*)(uintptr_t)cur->type;
446 success = 1;
447 }
448 }
449
450 /* Proceed when obj was created. */
451 if (success) {
452 if (obj == NULL) {
453 __redisReaderSetErrorOOM(r);
454 return REDIS_ERR;
455 }
456
457 r->pos += bytelen;
458
459 /* Set reply if this is the root object. */
460 if (r->ridx == 0) r->reply = obj;
461 moveToNextTask(r);
462 return REDIS_OK;
463 }
464 }
465
466 return REDIS_ERR;
467}
468
469static int redisReaderGrow(redisReader *r) {
470 redisReadTask **aux;
471 int newlen;
472
473 /* Grow our stack size */
474 newlen = r->tasks + REDIS_READER_STACK_SIZE;
475 aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
476 if (aux == NULL)
477 goto oom;
478
479 r->task = aux;
480
481 /* Allocate new tasks */
482 for (; r->tasks < newlen; r->tasks++) {
483 r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
484 if (r->task[r->tasks] == NULL)
485 goto oom;
486 }
487
488 return REDIS_OK;
489oom:
490 __redisReaderSetErrorOOM(r);
491 return REDIS_ERR;
492}
493
494/* Process the array, map and set types. */
495static int processAggregateItem(redisReader *r) {
496 redisReadTask *cur = r->task[r->ridx];
497 void *obj;
498 char *p;
499 long long elements;
500 int root = 0, len;
501
502 if (r->ridx == r->tasks - 1) {
503 if (redisReaderGrow(r) == REDIS_ERR)
504 return REDIS_ERR;
505 }
506
507 if ((p = readLine(r,&len)) != NULL) {
508 if (string2ll(p, len, &elements) == REDIS_ERR) {
509 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
510 "Bad multi-bulk length");
511 return REDIS_ERR;
512 }
513
514 root = (r->ridx == 0);
515
516 if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
517 (r->maxelements > 0 && elements > r->maxelements))
518 {
519 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
520 "Multi-bulk length out of range");
521 return REDIS_ERR;
522 }
523
524 if (elements == -1) {
525 if (r->fn && r->fn->createNil)
526 obj = r->fn->createNil(cur);
527 else
528 obj = (void*)REDIS_REPLY_NIL;
529
530 if (obj == NULL) {
531 __redisReaderSetErrorOOM(r);
532 return REDIS_ERR;
533 }
534
535 moveToNextTask(r);
536 } else {
537 if (cur->type == REDIS_REPLY_MAP) elements *= 2;
538
539 if (r->fn && r->fn->createArray)
540 obj = r->fn->createArray(cur,elements);
541 else
542 obj = (void*)(uintptr_t)cur->type;
543
544 if (obj == NULL) {
545 __redisReaderSetErrorOOM(r);
546 return REDIS_ERR;
547 }
548
549 /* Modify task stack when there are more than 0 elements. */
550 if (elements > 0) {
551 cur->elements = elements;
552 cur->obj = obj;
553 r->ridx++;
554 r->task[r->ridx]->type = -1;
555 r->task[r->ridx]->elements = -1;
556 r->task[r->ridx]->idx = 0;
557 r->task[r->ridx]->obj = NULL;
558 r->task[r->ridx]->parent = cur;
559 r->task[r->ridx]->privdata = r->privdata;
560 } else {
561 moveToNextTask(r);
562 }
563 }
564
565 /* Set reply if this is the root object. */
566 if (root) r->reply = obj;
567 return REDIS_OK;
568 }
569
570 return REDIS_ERR;
571}
572
573static int processItem(redisReader *r) {
574 redisReadTask *cur = r->task[r->ridx];
575 char *p;
576
577 /* check if we need to read type */
578 if (cur->type < 0) {
579 if ((p = readBytes(r,1)) != NULL) {
580 switch (p[0]) {
581 case '-':
582 cur->type = REDIS_REPLY_ERROR;
583 break;
584 case '+':
585 cur->type = REDIS_REPLY_STATUS;
586 break;
587 case ':':
588 cur->type = REDIS_REPLY_INTEGER;
589 break;
590 case ',':
591 cur->type = REDIS_REPLY_DOUBLE;
592 break;
593 case '_':
594 cur->type = REDIS_REPLY_NIL;
595 break;
596 case '$':
597 cur->type = REDIS_REPLY_STRING;
598 break;
599 case '*':
600 cur->type = REDIS_REPLY_ARRAY;
601 break;
602 case '%':
603 cur->type = REDIS_REPLY_MAP;
604 break;
605 case '~':
606 cur->type = REDIS_REPLY_SET;
607 break;
608 case '#':
609 cur->type = REDIS_REPLY_BOOL;
610 break;
611 case '=':
612 cur->type = REDIS_REPLY_VERB;
613 break;
614 case '>':
615 cur->type = REDIS_REPLY_PUSH;
616 break;
617 case '(':
618 cur->type = REDIS_REPLY_BIGNUM;
619 break;
620 default:
621 __redisReaderSetErrorProtocolByte(r,*p);
622 return REDIS_ERR;
623 }
624 } else {
625 /* could not consume 1 byte */
626 return REDIS_ERR;
627 }
628 }
629
630 /* process typed item */
631 switch(cur->type) {
632 case REDIS_REPLY_ERROR:
633 case REDIS_REPLY_STATUS:
634 case REDIS_REPLY_INTEGER:
635 case REDIS_REPLY_DOUBLE:
636 case REDIS_REPLY_NIL:
637 case REDIS_REPLY_BOOL:
638 case REDIS_REPLY_BIGNUM:
639 return processLineItem(r);
640 case REDIS_REPLY_STRING:
641 case REDIS_REPLY_VERB:
642 return processBulkItem(r);
643 case REDIS_REPLY_ARRAY:
644 case REDIS_REPLY_MAP:
645 case REDIS_REPLY_SET:
646 case REDIS_REPLY_PUSH:
647 return processAggregateItem(r);
648 default:
649 assert(NULL);
650 return REDIS_ERR; /* Avoid warning. */
651 }
652}
653
654redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
655 redisReader *r;
656
657 r = hi_calloc(1,sizeof(redisReader));
658 if (r == NULL)
659 return NULL;
660
661 r->buf = hi_sdsempty();
662 if (r->buf == NULL)
663 goto oom;
664
665 r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
666 if (r->task == NULL)
667 goto oom;
668
669 for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
670 r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
671 if (r->task[r->tasks] == NULL)
672 goto oom;
673 }
674
675 r->fn = fn;
676 r->maxbuf = REDIS_READER_MAX_BUF;
677 r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
678 r->ridx = -1;
679
680 return r;
681oom:
682 redisReaderFree(r);
683 return NULL;
684}
685
686void redisReaderFree(redisReader *r) {
687 if (r == NULL)
688 return;
689
690 if (r->reply != NULL && r->fn && r->fn->freeObject)
691 r->fn->freeObject(r->reply);
692
693 if (r->task) {
694 /* We know r->task[i] is allocated if i < r->tasks */
695 for (int i = 0; i < r->tasks; i++) {
696 hi_free(r->task[i]);
697 }
698
699 hi_free(r->task);
700 }
701
702 hi_sdsfree(r->buf);
703 hi_free(r);
704}
705
706int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
707 hisds newbuf;
708
709 /* Return early when this reader is in an erroneous state. */
710 if (r->err)
711 return REDIS_ERR;
712
713 /* Copy the provided buffer. */
714 if (buf != NULL && len >= 1) {
715 /* Destroy internal buffer when it is empty and is quite large. */
716 if (r->len == 0 && r->maxbuf != 0 && hi_sdsavail(r->buf) > r->maxbuf) {
717 hi_sdsfree(r->buf);
718 r->buf = hi_sdsempty();
719 if (r->buf == 0) goto oom;
720
721 r->pos = 0;
722 }
723
724 newbuf = hi_sdscatlen(r->buf,buf,len);
725 if (newbuf == NULL) goto oom;
726
727 r->buf = newbuf;
728 r->len = hi_sdslen(r->buf);
729 }
730
731 return REDIS_OK;
732oom:
733 __redisReaderSetErrorOOM(r);
734 return REDIS_ERR;
735}
736
737int redisReaderGetReply(redisReader *r, void **reply) {
738 /* Default target pointer to NULL. */
739 if (reply != NULL)
740 *reply = NULL;
741
742 /* Return early when this reader is in an erroneous state. */
743 if (r->err)
744 return REDIS_ERR;
745
746 /* When the buffer is empty, there will never be a reply. */
747 if (r->len == 0)
748 return REDIS_OK;
749
750 /* Set first item to process when the stack is empty. */
751 if (r->ridx == -1) {
752 r->task[0]->type = -1;
753 r->task[0]->elements = -1;
754 r->task[0]->idx = -1;
755 r->task[0]->obj = NULL;
756 r->task[0]->parent = NULL;
757 r->task[0]->privdata = r->privdata;
758 r->ridx = 0;
759 }
760
761 /* Process items in reply. */
762 while (r->ridx >= 0)
763 if (processItem(r) != REDIS_OK)
764 break;
765
766 /* Return ASAP when an error occurred. */
767 if (r->err)
768 return REDIS_ERR;
769
770 /* Discard part of the buffer when we've consumed at least 1k, to avoid
771 * doing unnecessary calls to memmove() in sds.c. */
772 if (r->pos >= 1024) {
773 if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
774 r->pos = 0;
775 r->len = hi_sdslen(r->buf);
776 }
777
778 /* Emit a reply when there is one. */
779 if (r->ridx == -1) {
780 if (reply != NULL) {
781 *reply = r->reply;
782 } else if (r->reply != NULL && r->fn && r->fn->freeObject) {
783 r->fn->freeObject(r->reply);
784 }
785 r->reply = NULL;
786 }
787 return REDIS_OK;
788}