hiredis-client 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/ext/redis_client/hiredis/export.clang +2 -0
  4. data/ext/redis_client/hiredis/export.gcc +7 -0
  5. data/ext/redis_client/hiredis/extconf.rb +69 -0
  6. data/ext/redis_client/hiredis/hiredis_connection.c +708 -0
  7. data/ext/redis_client/hiredis/vendor/.gitignore +9 -0
  8. data/ext/redis_client/hiredis/vendor/.travis.yml +131 -0
  9. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +364 -0
  10. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +165 -0
  11. data/ext/redis_client/hiredis/vendor/COPYING +29 -0
  12. data/ext/redis_client/hiredis/vendor/Makefile +308 -0
  13. data/ext/redis_client/hiredis/vendor/README.md +664 -0
  14. data/ext/redis_client/hiredis/vendor/adapters/ae.h +130 -0
  15. data/ext/redis_client/hiredis/vendor/adapters/glib.h +156 -0
  16. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +84 -0
  17. data/ext/redis_client/hiredis/vendor/adapters/libev.h +179 -0
  18. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +175 -0
  19. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +117 -0
  20. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +115 -0
  21. data/ext/redis_client/hiredis/vendor/adapters/qt.h +135 -0
  22. data/ext/redis_client/hiredis/vendor/alloc.c +86 -0
  23. data/ext/redis_client/hiredis/vendor/alloc.h +91 -0
  24. data/ext/redis_client/hiredis/vendor/appveyor.yml +24 -0
  25. data/ext/redis_client/hiredis/vendor/async.c +887 -0
  26. data/ext/redis_client/hiredis/vendor/async.h +147 -0
  27. data/ext/redis_client/hiredis/vendor/async_private.h +75 -0
  28. data/ext/redis_client/hiredis/vendor/dict.c +352 -0
  29. data/ext/redis_client/hiredis/vendor/dict.h +126 -0
  30. data/ext/redis_client/hiredis/vendor/fmacros.h +12 -0
  31. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +13 -0
  32. data/ext/redis_client/hiredis/vendor/hiredis.c +1174 -0
  33. data/ext/redis_client/hiredis/vendor/hiredis.h +336 -0
  34. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +12 -0
  35. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +13 -0
  36. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +157 -0
  37. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +12 -0
  38. data/ext/redis_client/hiredis/vendor/net.c +612 -0
  39. data/ext/redis_client/hiredis/vendor/net.h +56 -0
  40. data/ext/redis_client/hiredis/vendor/read.c +739 -0
  41. data/ext/redis_client/hiredis/vendor/read.h +129 -0
  42. data/ext/redis_client/hiredis/vendor/sds.c +1289 -0
  43. data/ext/redis_client/hiredis/vendor/sds.h +278 -0
  44. data/ext/redis_client/hiredis/vendor/sdsalloc.h +44 -0
  45. data/ext/redis_client/hiredis/vendor/sockcompat.c +248 -0
  46. data/ext/redis_client/hiredis/vendor/sockcompat.h +92 -0
  47. data/ext/redis_client/hiredis/vendor/ssl.c +544 -0
  48. data/ext/redis_client/hiredis/vendor/test.c +1401 -0
  49. data/ext/redis_client/hiredis/vendor/test.sh +78 -0
  50. data/ext/redis_client/hiredis/vendor/win32.h +56 -0
  51. data/hiredis-client.gemspec +33 -0
  52. data/lib/hiredis-client.rb +11 -0
  53. data/lib/redis_client/hiredis_connection.rb +94 -0
  54. metadata +114 -0
@@ -0,0 +1,739 @@
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
+ 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
+ int pos = 0;
127
+ int _len = len-1;
128
+
129
+ /* Position should be < len-1 because the character at "pos" should be
130
+ * followed by a \n. Note that strchr cannot be used because it doesn't
131
+ * allow to search a limited length and the buffer that is being searched
132
+ * might not have a trailing NULL character. */
133
+ while (pos < _len) {
134
+ while(pos < _len && s[pos] != '\r') pos++;
135
+ if (pos==_len) {
136
+ /* Not found. */
137
+ return NULL;
138
+ } else {
139
+ if (s[pos+1] == '\n') {
140
+ /* Found. */
141
+ return s+pos;
142
+ } else {
143
+ /* Continue searching. */
144
+ pos++;
145
+ }
146
+ }
147
+ }
148
+ return NULL;
149
+ }
150
+
151
+ /* Convert a string into a long long. Returns REDIS_OK if the string could be
152
+ * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
153
+ * will be set to the parsed value when appropriate.
154
+ *
155
+ * Note that this function demands that the string strictly represents
156
+ * a long long: no spaces or other characters before or after the string
157
+ * representing the number are accepted, nor zeroes at the start if not
158
+ * for the string "0" representing the zero number.
159
+ *
160
+ * Because of its strictness, it is safe to use this function to check if
161
+ * you can convert a string into a long long, and obtain back the string
162
+ * from the number without any loss in the string representation. */
163
+ static int string2ll(const char *s, size_t slen, long long *value) {
164
+ const char *p = s;
165
+ size_t plen = 0;
166
+ int negative = 0;
167
+ unsigned long long v;
168
+
169
+ if (plen == slen)
170
+ return REDIS_ERR;
171
+
172
+ /* Special case: first and only digit is 0. */
173
+ if (slen == 1 && p[0] == '0') {
174
+ if (value != NULL) *value = 0;
175
+ return REDIS_OK;
176
+ }
177
+
178
+ if (p[0] == '-') {
179
+ negative = 1;
180
+ p++; plen++;
181
+
182
+ /* Abort on only a negative sign. */
183
+ if (plen == slen)
184
+ return REDIS_ERR;
185
+ }
186
+
187
+ /* First digit should be 1-9, otherwise the string should just be 0. */
188
+ if (p[0] >= '1' && p[0] <= '9') {
189
+ v = p[0]-'0';
190
+ p++; plen++;
191
+ } else if (p[0] == '0' && slen == 1) {
192
+ *value = 0;
193
+ return REDIS_OK;
194
+ } else {
195
+ return REDIS_ERR;
196
+ }
197
+
198
+ while (plen < slen && p[0] >= '0' && p[0] <= '9') {
199
+ if (v > (ULLONG_MAX / 10)) /* Overflow. */
200
+ return REDIS_ERR;
201
+ v *= 10;
202
+
203
+ if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
204
+ return REDIS_ERR;
205
+ v += p[0]-'0';
206
+
207
+ p++; plen++;
208
+ }
209
+
210
+ /* Return if not all bytes were used. */
211
+ if (plen < slen)
212
+ return REDIS_ERR;
213
+
214
+ if (negative) {
215
+ if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
216
+ return REDIS_ERR;
217
+ if (value != NULL) *value = -v;
218
+ } else {
219
+ if (v > LLONG_MAX) /* Overflow. */
220
+ return REDIS_ERR;
221
+ if (value != NULL) *value = v;
222
+ }
223
+ return REDIS_OK;
224
+ }
225
+
226
+ static char *readLine(redisReader *r, int *_len) {
227
+ char *p, *s;
228
+ int len;
229
+
230
+ p = r->buf+r->pos;
231
+ s = seekNewline(p,(r->len-r->pos));
232
+ if (s != NULL) {
233
+ len = s-(r->buf+r->pos);
234
+ r->pos += len+2; /* skip \r\n */
235
+ if (_len) *_len = len;
236
+ return p;
237
+ }
238
+ return NULL;
239
+ }
240
+
241
+ static void moveToNextTask(redisReader *r) {
242
+ redisReadTask *cur, *prv;
243
+ while (r->ridx >= 0) {
244
+ /* Return a.s.a.p. when the stack is now empty. */
245
+ if (r->ridx == 0) {
246
+ r->ridx--;
247
+ return;
248
+ }
249
+
250
+ cur = r->task[r->ridx];
251
+ prv = r->task[r->ridx-1];
252
+ assert(prv->type == REDIS_REPLY_ARRAY ||
253
+ prv->type == REDIS_REPLY_MAP ||
254
+ prv->type == REDIS_REPLY_SET ||
255
+ prv->type == REDIS_REPLY_PUSH);
256
+ if (cur->idx == prv->elements-1) {
257
+ r->ridx--;
258
+ } else {
259
+ /* Reset the type because the next item can be anything */
260
+ assert(cur->idx < prv->elements);
261
+ cur->type = -1;
262
+ cur->elements = -1;
263
+ cur->idx++;
264
+ return;
265
+ }
266
+ }
267
+ }
268
+
269
+ static int processLineItem(redisReader *r) {
270
+ redisReadTask *cur = r->task[r->ridx];
271
+ void *obj;
272
+ char *p;
273
+ int len;
274
+
275
+ if ((p = readLine(r,&len)) != NULL) {
276
+ if (cur->type == REDIS_REPLY_INTEGER) {
277
+ if (r->fn && r->fn->createInteger) {
278
+ long long v;
279
+ if (string2ll(p, len, &v) == REDIS_ERR) {
280
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
281
+ "Bad integer value");
282
+ return REDIS_ERR;
283
+ }
284
+ obj = r->fn->createInteger(cur,v);
285
+ } else {
286
+ obj = (void*)REDIS_REPLY_INTEGER;
287
+ }
288
+ } else if (cur->type == REDIS_REPLY_DOUBLE) {
289
+ if (r->fn && r->fn->createDouble) {
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 (strcasecmp(buf,",inf") == 0) {
303
+ d = INFINITY; /* Positive infinite. */
304
+ } else if (strcasecmp(buf,",-inf") == 0) {
305
+ d = -INFINITY; /* Negative infinite. */
306
+ } else {
307
+ d = strtod((char*)buf,&eptr);
308
+ if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
309
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
310
+ "Bad double value");
311
+ return REDIS_ERR;
312
+ }
313
+ }
314
+ obj = r->fn->createDouble(cur,d,buf,len);
315
+ } else {
316
+ obj = (void*)REDIS_REPLY_DOUBLE;
317
+ }
318
+ } else if (cur->type == REDIS_REPLY_NIL) {
319
+ if (r->fn && r->fn->createNil)
320
+ obj = r->fn->createNil(cur);
321
+ else
322
+ obj = (void*)REDIS_REPLY_NIL;
323
+ } else if (cur->type == REDIS_REPLY_BOOL) {
324
+ int bval = p[0] == 't' || p[0] == 'T';
325
+ if (r->fn && r->fn->createBool)
326
+ obj = r->fn->createBool(cur,bval);
327
+ else
328
+ obj = (void*)REDIS_REPLY_BOOL;
329
+ } else {
330
+ /* Type will be error or status. */
331
+ if (r->fn && r->fn->createString)
332
+ obj = r->fn->createString(cur,p,len);
333
+ else
334
+ obj = (void*)(size_t)(cur->type);
335
+ }
336
+
337
+ if (obj == NULL) {
338
+ __redisReaderSetErrorOOM(r);
339
+ return REDIS_ERR;
340
+ }
341
+
342
+ /* Set reply if this is the root object. */
343
+ if (r->ridx == 0) r->reply = obj;
344
+ moveToNextTask(r);
345
+ return REDIS_OK;
346
+ }
347
+
348
+ return REDIS_ERR;
349
+ }
350
+
351
+ static int processBulkItem(redisReader *r) {
352
+ redisReadTask *cur = r->task[r->ridx];
353
+ void *obj = NULL;
354
+ char *p, *s;
355
+ long long len;
356
+ unsigned long bytelen;
357
+ int success = 0;
358
+
359
+ p = r->buf+r->pos;
360
+ s = seekNewline(p,r->len-r->pos);
361
+ if (s != NULL) {
362
+ p = r->buf+r->pos;
363
+ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
364
+
365
+ if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
366
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
367
+ "Bad bulk string length");
368
+ return REDIS_ERR;
369
+ }
370
+
371
+ if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
372
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
373
+ "Bulk string length out of range");
374
+ return REDIS_ERR;
375
+ }
376
+
377
+ if (len == -1) {
378
+ /* The nil object can always be created. */
379
+ if (r->fn && r->fn->createNil)
380
+ obj = r->fn->createNil(cur);
381
+ else
382
+ obj = (void*)REDIS_REPLY_NIL;
383
+ success = 1;
384
+ } else {
385
+ /* Only continue when the buffer contains the entire bulk item. */
386
+ bytelen += len+2; /* include \r\n */
387
+ if (r->pos+bytelen <= r->len) {
388
+ if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
389
+ (cur->type == REDIS_REPLY_VERB && s[5] != ':'))
390
+ {
391
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
392
+ "Verbatim string 4 bytes of content type are "
393
+ "missing or incorrectly encoded.");
394
+ return REDIS_ERR;
395
+ }
396
+ if (r->fn && r->fn->createString)
397
+ obj = r->fn->createString(cur,s+2,len);
398
+ else
399
+ obj = (void*)(long)cur->type;
400
+ success = 1;
401
+ }
402
+ }
403
+
404
+ /* Proceed when obj was created. */
405
+ if (success) {
406
+ if (obj == NULL) {
407
+ __redisReaderSetErrorOOM(r);
408
+ return REDIS_ERR;
409
+ }
410
+
411
+ r->pos += bytelen;
412
+
413
+ /* Set reply if this is the root object. */
414
+ if (r->ridx == 0) r->reply = obj;
415
+ moveToNextTask(r);
416
+ return REDIS_OK;
417
+ }
418
+ }
419
+
420
+ return REDIS_ERR;
421
+ }
422
+
423
+ static int redisReaderGrow(redisReader *r) {
424
+ redisReadTask **aux;
425
+ int newlen;
426
+
427
+ /* Grow our stack size */
428
+ newlen = r->tasks + REDIS_READER_STACK_SIZE;
429
+ aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
430
+ if (aux == NULL)
431
+ goto oom;
432
+
433
+ r->task = aux;
434
+
435
+ /* Allocate new tasks */
436
+ for (; r->tasks < newlen; r->tasks++) {
437
+ r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
438
+ if (r->task[r->tasks] == NULL)
439
+ goto oom;
440
+ }
441
+
442
+ return REDIS_OK;
443
+ oom:
444
+ __redisReaderSetErrorOOM(r);
445
+ return REDIS_ERR;
446
+ }
447
+
448
+ /* Process the array, map and set types. */
449
+ static int processAggregateItem(redisReader *r) {
450
+ redisReadTask *cur = r->task[r->ridx];
451
+ void *obj;
452
+ char *p;
453
+ long long elements;
454
+ int root = 0, len;
455
+
456
+ /* Set error for nested multi bulks with depth > 7 */
457
+ if (r->ridx == r->tasks - 1) {
458
+ if (redisReaderGrow(r) == REDIS_ERR)
459
+ return REDIS_ERR;
460
+ }
461
+
462
+ if ((p = readLine(r,&len)) != NULL) {
463
+ if (string2ll(p, len, &elements) == REDIS_ERR) {
464
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
465
+ "Bad multi-bulk length");
466
+ return REDIS_ERR;
467
+ }
468
+
469
+ root = (r->ridx == 0);
470
+
471
+ if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
472
+ (r->maxelements > 0 && elements > r->maxelements))
473
+ {
474
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
475
+ "Multi-bulk length out of range");
476
+ return REDIS_ERR;
477
+ }
478
+
479
+ if (elements == -1) {
480
+ if (r->fn && r->fn->createNil)
481
+ obj = r->fn->createNil(cur);
482
+ else
483
+ obj = (void*)REDIS_REPLY_NIL;
484
+
485
+ if (obj == NULL) {
486
+ __redisReaderSetErrorOOM(r);
487
+ return REDIS_ERR;
488
+ }
489
+
490
+ moveToNextTask(r);
491
+ } else {
492
+ if (cur->type == REDIS_REPLY_MAP) elements *= 2;
493
+
494
+ if (r->fn && r->fn->createArray)
495
+ obj = r->fn->createArray(cur,elements);
496
+ else
497
+ obj = (void*)(long)cur->type;
498
+
499
+ if (obj == NULL) {
500
+ __redisReaderSetErrorOOM(r);
501
+ return REDIS_ERR;
502
+ }
503
+
504
+ /* Modify task stack when there are more than 0 elements. */
505
+ if (elements > 0) {
506
+ cur->elements = elements;
507
+ cur->obj = obj;
508
+ r->ridx++;
509
+ r->task[r->ridx]->type = -1;
510
+ r->task[r->ridx]->elements = -1;
511
+ r->task[r->ridx]->idx = 0;
512
+ r->task[r->ridx]->obj = NULL;
513
+ r->task[r->ridx]->parent = cur;
514
+ r->task[r->ridx]->privdata = r->privdata;
515
+ } else {
516
+ moveToNextTask(r);
517
+ }
518
+ }
519
+
520
+ /* Set reply if this is the root object. */
521
+ if (root) r->reply = obj;
522
+ return REDIS_OK;
523
+ }
524
+
525
+ return REDIS_ERR;
526
+ }
527
+
528
+ static int processItem(redisReader *r) {
529
+ redisReadTask *cur = r->task[r->ridx];
530
+ char *p;
531
+
532
+ /* check if we need to read type */
533
+ if (cur->type < 0) {
534
+ if ((p = readBytes(r,1)) != NULL) {
535
+ switch (p[0]) {
536
+ case '-':
537
+ cur->type = REDIS_REPLY_ERROR;
538
+ break;
539
+ case '+':
540
+ cur->type = REDIS_REPLY_STATUS;
541
+ break;
542
+ case ':':
543
+ cur->type = REDIS_REPLY_INTEGER;
544
+ break;
545
+ case ',':
546
+ cur->type = REDIS_REPLY_DOUBLE;
547
+ break;
548
+ case '_':
549
+ cur->type = REDIS_REPLY_NIL;
550
+ break;
551
+ case '$':
552
+ cur->type = REDIS_REPLY_STRING;
553
+ break;
554
+ case '*':
555
+ cur->type = REDIS_REPLY_ARRAY;
556
+ break;
557
+ case '%':
558
+ cur->type = REDIS_REPLY_MAP;
559
+ break;
560
+ case '~':
561
+ cur->type = REDIS_REPLY_SET;
562
+ break;
563
+ case '#':
564
+ cur->type = REDIS_REPLY_BOOL;
565
+ break;
566
+ case '=':
567
+ cur->type = REDIS_REPLY_VERB;
568
+ break;
569
+ case '>':
570
+ cur->type = REDIS_REPLY_PUSH;
571
+ break;
572
+ default:
573
+ __redisReaderSetErrorProtocolByte(r,*p);
574
+ return REDIS_ERR;
575
+ }
576
+ } else {
577
+ /* could not consume 1 byte */
578
+ return REDIS_ERR;
579
+ }
580
+ }
581
+
582
+ /* process typed item */
583
+ switch(cur->type) {
584
+ case REDIS_REPLY_ERROR:
585
+ case REDIS_REPLY_STATUS:
586
+ case REDIS_REPLY_INTEGER:
587
+ case REDIS_REPLY_DOUBLE:
588
+ case REDIS_REPLY_NIL:
589
+ case REDIS_REPLY_BOOL:
590
+ return processLineItem(r);
591
+ case REDIS_REPLY_STRING:
592
+ case REDIS_REPLY_VERB:
593
+ return processBulkItem(r);
594
+ case REDIS_REPLY_ARRAY:
595
+ case REDIS_REPLY_MAP:
596
+ case REDIS_REPLY_SET:
597
+ case REDIS_REPLY_PUSH:
598
+ return processAggregateItem(r);
599
+ default:
600
+ assert(NULL);
601
+ return REDIS_ERR; /* Avoid warning. */
602
+ }
603
+ }
604
+
605
+ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
606
+ redisReader *r;
607
+
608
+ r = hi_calloc(1,sizeof(redisReader));
609
+ if (r == NULL)
610
+ return NULL;
611
+
612
+ r->buf = sdsempty();
613
+ if (r->buf == NULL)
614
+ goto oom;
615
+
616
+ r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
617
+ if (r->task == NULL)
618
+ goto oom;
619
+
620
+ for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
621
+ r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
622
+ if (r->task[r->tasks] == NULL)
623
+ goto oom;
624
+ }
625
+
626
+ r->fn = fn;
627
+ r->maxbuf = REDIS_READER_MAX_BUF;
628
+ r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
629
+ r->ridx = -1;
630
+
631
+ return r;
632
+ oom:
633
+ redisReaderFree(r);
634
+ return NULL;
635
+ }
636
+
637
+ void redisReaderFree(redisReader *r) {
638
+ if (r == NULL)
639
+ return;
640
+
641
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
642
+ r->fn->freeObject(r->reply);
643
+
644
+ if (r->task) {
645
+ /* We know r->task[i] is allocated if i < r->tasks */
646
+ for (int i = 0; i < r->tasks; i++) {
647
+ hi_free(r->task[i]);
648
+ }
649
+
650
+ hi_free(r->task);
651
+ }
652
+
653
+ sdsfree(r->buf);
654
+ hi_free(r);
655
+ }
656
+
657
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
658
+ sds newbuf;
659
+
660
+ /* Return early when this reader is in an erroneous state. */
661
+ if (r->err)
662
+ return REDIS_ERR;
663
+
664
+ /* Copy the provided buffer. */
665
+ if (buf != NULL && len >= 1) {
666
+ /* Destroy internal buffer when it is empty and is quite large. */
667
+ if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
668
+ sdsfree(r->buf);
669
+ r->buf = sdsempty();
670
+ if (r->buf == 0) goto oom;
671
+
672
+ r->pos = 0;
673
+ }
674
+
675
+ newbuf = sdscatlen(r->buf,buf,len);
676
+ if (newbuf == NULL) goto oom;
677
+
678
+ r->buf = newbuf;
679
+ r->len = sdslen(r->buf);
680
+ }
681
+
682
+ return REDIS_OK;
683
+ oom:
684
+ __redisReaderSetErrorOOM(r);
685
+ return REDIS_ERR;
686
+ }
687
+
688
+ int redisReaderGetReply(redisReader *r, void **reply) {
689
+ /* Default target pointer to NULL. */
690
+ if (reply != NULL)
691
+ *reply = NULL;
692
+
693
+ /* Return early when this reader is in an erroneous state. */
694
+ if (r->err)
695
+ return REDIS_ERR;
696
+
697
+ /* When the buffer is empty, there will never be a reply. */
698
+ if (r->len == 0)
699
+ return REDIS_OK;
700
+
701
+ /* Set first item to process when the stack is empty. */
702
+ if (r->ridx == -1) {
703
+ r->task[0]->type = -1;
704
+ r->task[0]->elements = -1;
705
+ r->task[0]->idx = -1;
706
+ r->task[0]->obj = NULL;
707
+ r->task[0]->parent = NULL;
708
+ r->task[0]->privdata = r->privdata;
709
+ r->ridx = 0;
710
+ }
711
+
712
+ /* Process items in reply. */
713
+ while (r->ridx >= 0)
714
+ if (processItem(r) != REDIS_OK)
715
+ break;
716
+
717
+ /* Return ASAP when an error occurred. */
718
+ if (r->err)
719
+ return REDIS_ERR;
720
+
721
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
722
+ * doing unnecessary calls to memmove() in sds.c. */
723
+ if (r->pos >= 1024) {
724
+ if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
725
+ r->pos = 0;
726
+ r->len = sdslen(r->buf);
727
+ }
728
+
729
+ /* Emit a reply when there is one. */
730
+ if (r->ridx == -1) {
731
+ if (reply != NULL) {
732
+ *reply = r->reply;
733
+ } else if (r->reply != NULL && r->fn && r->fn->freeObject) {
734
+ r->fn->freeObject(r->reply);
735
+ }
736
+ r->reply = NULL;
737
+ }
738
+ return REDIS_OK;
739
+ }