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 {
307 d = strtod((char*)buf,&eptr);
308 /* RESP3 only allows "inf", "-inf", and finite values, while
309 * strtod() allows other variations on infinity, NaN,
310 * etc. We explicity handle our two allowed infinite cases
311 * above, so strtod() should only result in finite values. */
312 if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) {
313 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
314 "Bad double value");
315 return REDIS_ERR;
316 }
317 }
318
319 if (r->fn && r->fn->createDouble) {
320 obj = r->fn->createDouble(cur,d,buf,len);
321 } else {
322 obj = (void*)REDIS_REPLY_DOUBLE;
323 }
324 } else if (cur->type == REDIS_REPLY_NIL) {
325 if (len != 0) {
326 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
327 "Bad nil value");
328 return REDIS_ERR;
329 }
330
331 if (r->fn && r->fn->createNil)
332 obj = r->fn->createNil(cur);
333 else
334 obj = (void*)REDIS_REPLY_NIL;
335 } else if (cur->type == REDIS_REPLY_BOOL) {
336 int bval;
337
338 if (len != 1 || !strchr("tTfF", p[0])) {
339 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
340 "Bad bool value");
341 return REDIS_ERR;
342 }
343
344 bval = p[0] == 't' || p[0] == 'T';
345 if (r->fn && r->fn->createBool)
346 obj = r->fn->createBool(cur,bval);
347 else
348 obj = (void*)REDIS_REPLY_BOOL;
349 } else if (cur->type == REDIS_REPLY_BIGNUM) {
350 /* Ensure all characters are decimal digits (with possible leading
351 * minus sign). */
352 for (int i = 0; i < len; i++) {
353 /* XXX Consider: Allow leading '+'? Error on leading '0's? */
354 if (i == 0 && p[0] == '-') continue;
355 if (p[i] < '0' || p[i] > '9') {
356 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
357 "Bad bignum value");
358 return REDIS_ERR;
359 }
360 }
361 if (r->fn && r->fn->createString)
362 obj = r->fn->createString(cur,p,len);
363 else
364 obj = (void*)REDIS_REPLY_BIGNUM;
365 } else {
366 /* Type will be error or status. */
367 for (int i = 0; i < len; i++) {
368 if (p[i] == '\r' || p[i] == '\n') {
369 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
370 "Bad simple string value");
371 return REDIS_ERR;
372 }
373 }
374 if (r->fn && r->fn->createString)
375 obj = r->fn->createString(cur,p,len);
376 else
377 obj = (void*)(size_t)(cur->type);
378 }
379
380 if (obj == NULL) {
381 __redisReaderSetErrorOOM(r);
382 return REDIS_ERR;
383 }
384
385 /* Set reply if this is the root object. */
386 if (r->ridx == 0) r->reply = obj;
387 moveToNextTask(r);
388 return REDIS_OK;
389 }
390
391 return REDIS_ERR;
392}
393
394static int processBulkItem(redisReader *r) {
395 redisReadTask *cur = r->task[r->ridx];
396 void *obj = NULL;
397 char *p, *s;
398 long long len;
399 unsigned long bytelen;
400 int success = 0;
401
402 p = r->buf+r->pos;
403 s = seekNewline(p,r->len-r->pos);
404 if (s != NULL) {
405 p = r->buf+r->pos;
406 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
407
408 if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
409 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
410 "Bad bulk string length");
411 return REDIS_ERR;
412 }
413
414 if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
415 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
416 "Bulk string length out of range");
417 return REDIS_ERR;
418 }
419
420 if (len == -1) {
421 /* The nil object can always be created. */
422 if (r->fn && r->fn->createNil)
423 obj = r->fn->createNil(cur);
424 else
425 obj = (void*)REDIS_REPLY_NIL;
426 success = 1;
427 } else {
428 /* Only continue when the buffer contains the entire bulk item. */
429 bytelen += len+2; /* include \r\n */
430 if (r->pos+bytelen <= r->len) {
431 if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
432 (cur->type == REDIS_REPLY_VERB && s[5] != ':'))
433 {
434 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
435 "Verbatim string 4 bytes of content type are "
436 "missing or incorrectly encoded.");
437 return REDIS_ERR;
438 }
439 if (r->fn && r->fn->createString)
440 obj = r->fn->createString(cur,s+2,len);
441 else
442 obj = (void*)(long)cur->type;
443 success = 1;
444 }
445 }
446
447 /* Proceed when obj was created. */
448 if (success) {
449 if (obj == NULL) {
450 __redisReaderSetErrorOOM(r);
451 return REDIS_ERR;
452 }
453
454 r->pos += bytelen;
455
456 /* Set reply if this is the root object. */
457 if (r->ridx == 0) r->reply = obj;
458 moveToNextTask(r);
459 return REDIS_OK;
460 }
461 }
462
463 return REDIS_ERR;
464}
465
466static int redisReaderGrow(redisReader *r) {
467 redisReadTask **aux;
468 int newlen;
469
470 /* Grow our stack size */
471 newlen = r->tasks + REDIS_READER_STACK_SIZE;
472 aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
473 if (aux == NULL)
474 goto oom;
475
476 r->task = aux;
477
478 /* Allocate new tasks */
479 for (; r->tasks < newlen; r->tasks++) {
480 r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
481 if (r->task[r->tasks] == NULL)
482 goto oom;
483 }
484
485 return REDIS_OK;
486oom:
487 __redisReaderSetErrorOOM(r);
488 return REDIS_ERR;
489}
490
491/* Process the array, map and set types. */
492static int processAggregateItem(redisReader *r) {
493 redisReadTask *cur = r->task[r->ridx];
494 void *obj;
495 char *p;
496 long long elements;
497 int root = 0, len;
498
499 if (r->ridx == r->tasks - 1) {
500 if (redisReaderGrow(r) == REDIS_ERR)
501 return REDIS_ERR;
502 }
503
504 if ((p = readLine(r,&len)) != NULL) {
505 if (string2ll(p, len, &elements) == REDIS_ERR) {
506 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
507 "Bad multi-bulk length");
508 return REDIS_ERR;
509 }
510
511 root = (r->ridx == 0);
512
513 if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
514 (r->maxelements > 0 && elements > r->maxelements))
515 {
516 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
517 "Multi-bulk length out of range");
518 return REDIS_ERR;
519 }
520
521 if (elements == -1) {
522 if (r->fn && r->fn->createNil)
523 obj = r->fn->createNil(cur);
524 else
525 obj = (void*)REDIS_REPLY_NIL;
526
527 if (obj == NULL) {
528 __redisReaderSetErrorOOM(r);
529 return REDIS_ERR;
530 }
531
532 moveToNextTask(r);
533 } else {
534 if (cur->type == REDIS_REPLY_MAP) elements *= 2;
535
536 if (r->fn && r->fn->createArray)
537 obj = r->fn->createArray(cur,elements);
538 else
539 obj = (void*)(long)cur->type;
540
541 if (obj == NULL) {
542 __redisReaderSetErrorOOM(r);
543 return REDIS_ERR;
544 }
545
546 /* Modify task stack when there are more than 0 elements. */
547 if (elements > 0) {
548 cur->elements = elements;
549 cur->obj = obj;
550 r->ridx++;
551 r->task[r->ridx]->type = -1;
552 r->task[r->ridx]->elements = -1;
553 r->task[r->ridx]->idx = 0;
554 r->task[r->ridx]->obj = NULL;
555 r->task[r->ridx]->parent = cur;
556 r->task[r->ridx]->privdata = r->privdata;
557 } else {
558 moveToNextTask(r);
559 }
560 }
561
562 /* Set reply if this is the root object. */
563 if (root) r->reply = obj;
564 return REDIS_OK;
565 }
566
567 return REDIS_ERR;
568}
569
570static int processItem(redisReader *r) {
571 redisReadTask *cur = r->task[r->ridx];
572 char *p;
573
574 /* check if we need to read type */
575 if (cur->type < 0) {
576 if ((p = readBytes(r,1)) != NULL) {
577 switch (p[0]) {
578 case '-':
579 cur->type = REDIS_REPLY_ERROR;
580 break;
581 case '+':
582 cur->type = REDIS_REPLY_STATUS;
583 break;
584 case ':':
585 cur->type = REDIS_REPLY_INTEGER;
586 break;
587 case ',':
588 cur->type = REDIS_REPLY_DOUBLE;
589 break;
590 case '_':
591 cur->type = REDIS_REPLY_NIL;
592 break;
593 case '$':
594 cur->type = REDIS_REPLY_STRING;
595 break;
596 case '*':
597 cur->type = REDIS_REPLY_ARRAY;
598 break;
599 case '%':
600 cur->type = REDIS_REPLY_MAP;
601 break;
602 case '~':
603 cur->type = REDIS_REPLY_SET;
604 break;
605 case '#':
606 cur->type = REDIS_REPLY_BOOL;
607 break;
608 case '=':
609 cur->type = REDIS_REPLY_VERB;
610 break;
611 case '>':
612 cur->type = REDIS_REPLY_PUSH;
613 break;
614 case '(':
615 cur->type = REDIS_REPLY_BIGNUM;
616 break;
617 default:
618 __redisReaderSetErrorProtocolByte(r,*p);
619 return REDIS_ERR;
620 }
621 } else {
622 /* could not consume 1 byte */
623 return REDIS_ERR;
624 }
625 }
626
627 /* process typed item */
628 switch(cur->type) {
629 case REDIS_REPLY_ERROR:
630 case REDIS_REPLY_STATUS:
631 case REDIS_REPLY_INTEGER:
632 case REDIS_REPLY_DOUBLE:
633 case REDIS_REPLY_NIL:
634 case REDIS_REPLY_BOOL:
635 case REDIS_REPLY_BIGNUM:
636 return processLineItem(r);
637 case REDIS_REPLY_STRING:
638 case REDIS_REPLY_VERB:
639 return processBulkItem(r);
640 case REDIS_REPLY_ARRAY:
641 case REDIS_REPLY_MAP:
642 case REDIS_REPLY_SET:
643 case REDIS_REPLY_PUSH:
644 return processAggregateItem(r);
645 default:
646 assert(NULL);
647 return REDIS_ERR; /* Avoid warning. */
648 }
649}
650
651redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
652 redisReader *r;
653
654 r = hi_calloc(1,sizeof(redisReader));
655 if (r == NULL)
656 return NULL;
657
658 r->buf = hi_sdsempty();
659 if (r->buf == NULL)
660 goto oom;
661
662 r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
663 if (r->task == NULL)
664 goto oom;
665
666 for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
667 r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
668 if (r->task[r->tasks] == NULL)
669 goto oom;
670 }
671
672 r->fn = fn;
673 r->maxbuf = REDIS_READER_MAX_BUF;
674 r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
675 r->ridx = -1;
676
677 return r;
678oom:
679 redisReaderFree(r);
680 return NULL;
681}
682
683void redisReaderFree(redisReader *r) {
684 if (r == NULL)
685 return;
686
687 if (r->reply != NULL && r->fn && r->fn->freeObject)
688 r->fn->freeObject(r->reply);
689
690 if (r->task) {
691 /* We know r->task[i] is allocated if i < r->tasks */
692 for (int i = 0; i < r->tasks; i++) {
693 hi_free(r->task[i]);
694 }
695
696 hi_free(r->task);
697 }
698
699 hi_sdsfree(r->buf);
700 hi_free(r);
701}
702
703int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
704 hisds newbuf;
705
706 /* Return early when this reader is in an erroneous state. */
707 if (r->err)
708 return REDIS_ERR;
709
710 /* Copy the provided buffer. */
711 if (buf != NULL && len >= 1) {
712 /* Destroy internal buffer when it is empty and is quite large. */
713 if (r->len == 0 && r->maxbuf != 0 && hi_sdsavail(r->buf) > r->maxbuf) {
714 hi_sdsfree(r->buf);
715 r->buf = hi_sdsempty();
716 if (r->buf == 0) goto oom;
717
718 r->pos = 0;
719 }
720
721 newbuf = hi_sdscatlen(r->buf,buf,len);
722 if (newbuf == NULL) goto oom;
723
724 r->buf = newbuf;
725 r->len = hi_sdslen(r->buf);
726 }
727
728 return REDIS_OK;
729oom:
730 __redisReaderSetErrorOOM(r);
731 return REDIS_ERR;
732}
733
734int redisReaderGetReply(redisReader *r, void **reply) {
735 /* Default target pointer to NULL. */
736 if (reply != NULL)
737 *reply = NULL;
738
739 /* Return early when this reader is in an erroneous state. */
740 if (r->err)
741 return REDIS_ERR;
742
743 /* When the buffer is empty, there will never be a reply. */
744 if (r->len == 0)
745 return REDIS_OK;
746
747 /* Set first item to process when the stack is empty. */
748 if (r->ridx == -1) {
749 r->task[0]->type = -1;
750 r->task[0]->elements = -1;
751 r->task[0]->idx = -1;
752 r->task[0]->obj = NULL;
753 r->task[0]->parent = NULL;
754 r->task[0]->privdata = r->privdata;
755 r->ridx = 0;
756 }
757
758 /* Process items in reply. */
759 while (r->ridx >= 0)
760 if (processItem(r) != REDIS_OK)
761 break;
762
763 /* Return ASAP when an error occurred. */
764 if (r->err)
765 return REDIS_ERR;
766
767 /* Discard part of the buffer when we've consumed at least 1k, to avoid
768 * doing unnecessary calls to memmove() in sds.c. */
769 if (r->pos >= 1024) {
770 if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
771 r->pos = 0;
772 r->len = hi_sdslen(r->buf);
773 }
774
775 /* Emit a reply when there is one. */
776 if (r->ridx == -1) {
777 if (reply != NULL) {
778 *reply = r->reply;
779 } else if (r->reply != NULL && r->fn && r->fn->freeObject) {
780 r->fn->freeObject(r->reply);
781 }
782 r->reply = NULL;
783 }
784 return REDIS_OK;
785}
786