/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 | } |