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 | |
53 | static 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 | |
77 | static 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 | |
101 | static 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 | |
110 | static void __redisReaderSetErrorOOM(redisReader *r) { |
111 | __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory" ); |
112 | } |
113 | |
114 | static 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. */ |
125 | static 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. */ |
162 | static 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 | |
225 | static 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 | |
240 | static 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 | |
268 | static 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 | |
394 | static 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 | |
466 | static 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; |
486 | oom: |
487 | __redisReaderSetErrorOOM(r); |
488 | return REDIS_ERR; |
489 | } |
490 | |
491 | /* Process the array, map and set types. */ |
492 | static 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 | |
570 | static 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 | |
651 | redisReader *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; |
678 | oom: |
679 | redisReaderFree(r); |
680 | return NULL; |
681 | } |
682 | |
683 | void 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 | |
703 | int 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; |
729 | oom: |
730 | __redisReaderSetErrorOOM(r); |
731 | return REDIS_ERR; |
732 | } |
733 | |
734 | int 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 | |