Coverage Report

Created: 2025-07-11 06:24

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