rugged-redis 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
3
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
6
+ * Jan-Erik Rediger <janerik at fnordig dot com>
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions are met:
12
+ *
13
+ * * Redistributions of source code must retain the above copyright notice,
14
+ * this list of conditions and the following disclaimer.
15
+ * * Redistributions in binary form must reproduce the above copyright
16
+ * notice, this list of conditions and the following disclaimer in the
17
+ * documentation and/or other materials provided with the distribution.
18
+ * * Neither the name of Redis nor the names of its contributors may be used
19
+ * to endorse or promote products derived from this software without
20
+ * specific prior written permission.
21
+ *
22
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ * POSSIBILITY OF SUCH DAMAGE.
33
+ */
34
+
35
+ #ifndef __NET_H
36
+ #define __NET_H
37
+
38
+ #include "hiredis.h"
39
+
40
+ #if defined(__sun)
41
+ #define AF_LOCAL AF_UNIX
42
+ #endif
43
+
44
+ int redisCheckSocketError(redisContext *c);
45
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv);
46
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
47
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
48
+ const struct timeval *timeout,
49
+ const char *source_addr);
50
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
51
+ int redisKeepAlive(redisContext *c, int interval);
52
+
53
+ #endif
@@ -0,0 +1,525 @@
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
+
33
+ #include "fmacros.h"
34
+ #include <string.h>
35
+ #include <stdlib.h>
36
+ #ifndef _MSC_VER
37
+ #include <unistd.h>
38
+ #endif
39
+ #include <assert.h>
40
+ #include <errno.h>
41
+ #include <ctype.h>
42
+
43
+ #include "read.h"
44
+ #include "sds.h"
45
+
46
+ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
47
+ size_t len;
48
+
49
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
50
+ r->fn->freeObject(r->reply);
51
+ r->reply = NULL;
52
+ }
53
+
54
+ /* Clear input buffer on errors. */
55
+ if (r->buf != NULL) {
56
+ sdsfree(r->buf);
57
+ r->buf = NULL;
58
+ r->pos = r->len = 0;
59
+ }
60
+
61
+ /* Reset task stack. */
62
+ r->ridx = -1;
63
+
64
+ /* Set error. */
65
+ r->err = type;
66
+ len = strlen(str);
67
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
68
+ memcpy(r->errstr,str,len);
69
+ r->errstr[len] = '\0';
70
+ }
71
+
72
+ static size_t chrtos(char *buf, size_t size, char byte) {
73
+ size_t len = 0;
74
+
75
+ switch(byte) {
76
+ case '\\':
77
+ case '"':
78
+ len = snprintf(buf,size,"\"\\%c\"",byte);
79
+ break;
80
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
81
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
82
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
83
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
84
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
85
+ default:
86
+ if (isprint(byte))
87
+ len = snprintf(buf,size,"\"%c\"",byte);
88
+ else
89
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
90
+ break;
91
+ }
92
+
93
+ return len;
94
+ }
95
+
96
+ static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
97
+ char cbuf[8], sbuf[128];
98
+
99
+ chrtos(cbuf,sizeof(cbuf),byte);
100
+ snprintf(sbuf,sizeof(sbuf),
101
+ "Protocol error, got %s as reply type byte", cbuf);
102
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
103
+ }
104
+
105
+ static void __redisReaderSetErrorOOM(redisReader *r) {
106
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
107
+ }
108
+
109
+ static char *readBytes(redisReader *r, unsigned int bytes) {
110
+ char *p;
111
+ if (r->len-r->pos >= bytes) {
112
+ p = r->buf+r->pos;
113
+ r->pos += bytes;
114
+ return p;
115
+ }
116
+ return NULL;
117
+ }
118
+
119
+ /* Find pointer to \r\n. */
120
+ static char *seekNewline(char *s, size_t len) {
121
+ int pos = 0;
122
+ int _len = len-1;
123
+
124
+ /* Position should be < len-1 because the character at "pos" should be
125
+ * followed by a \n. Note that strchr cannot be used because it doesn't
126
+ * allow to search a limited length and the buffer that is being searched
127
+ * might not have a trailing NULL character. */
128
+ while (pos < _len) {
129
+ while(pos < _len && s[pos] != '\r') pos++;
130
+ if (pos==_len) {
131
+ /* Not found. */
132
+ return NULL;
133
+ } else {
134
+ if (s[pos+1] == '\n') {
135
+ /* Found. */
136
+ return s+pos;
137
+ } else {
138
+ /* Continue searching. */
139
+ pos++;
140
+ }
141
+ }
142
+ }
143
+ return NULL;
144
+ }
145
+
146
+ /* Read a long long value starting at *s, under the assumption that it will be
147
+ * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
148
+ static long long readLongLong(char *s) {
149
+ long long v = 0;
150
+ int dec, mult = 1;
151
+ char c;
152
+
153
+ if (*s == '-') {
154
+ mult = -1;
155
+ s++;
156
+ } else if (*s == '+') {
157
+ mult = 1;
158
+ s++;
159
+ }
160
+
161
+ while ((c = *(s++)) != '\r') {
162
+ dec = c - '0';
163
+ if (dec >= 0 && dec < 10) {
164
+ v *= 10;
165
+ v += dec;
166
+ } else {
167
+ /* Should not happen... */
168
+ return -1;
169
+ }
170
+ }
171
+
172
+ return mult*v;
173
+ }
174
+
175
+ static char *readLine(redisReader *r, int *_len) {
176
+ char *p, *s;
177
+ int len;
178
+
179
+ p = r->buf+r->pos;
180
+ s = seekNewline(p,(r->len-r->pos));
181
+ if (s != NULL) {
182
+ len = s-(r->buf+r->pos);
183
+ r->pos += len+2; /* skip \r\n */
184
+ if (_len) *_len = len;
185
+ return p;
186
+ }
187
+ return NULL;
188
+ }
189
+
190
+ static void moveToNextTask(redisReader *r) {
191
+ redisReadTask *cur, *prv;
192
+ while (r->ridx >= 0) {
193
+ /* Return a.s.a.p. when the stack is now empty. */
194
+ if (r->ridx == 0) {
195
+ r->ridx--;
196
+ return;
197
+ }
198
+
199
+ cur = &(r->rstack[r->ridx]);
200
+ prv = &(r->rstack[r->ridx-1]);
201
+ assert(prv->type == REDIS_REPLY_ARRAY);
202
+ if (cur->idx == prv->elements-1) {
203
+ r->ridx--;
204
+ } else {
205
+ /* Reset the type because the next item can be anything */
206
+ assert(cur->idx < prv->elements);
207
+ cur->type = -1;
208
+ cur->elements = -1;
209
+ cur->idx++;
210
+ return;
211
+ }
212
+ }
213
+ }
214
+
215
+ static int processLineItem(redisReader *r) {
216
+ redisReadTask *cur = &(r->rstack[r->ridx]);
217
+ void *obj;
218
+ char *p;
219
+ int len;
220
+
221
+ if ((p = readLine(r,&len)) != NULL) {
222
+ if (cur->type == REDIS_REPLY_INTEGER) {
223
+ if (r->fn && r->fn->createInteger)
224
+ obj = r->fn->createInteger(cur,readLongLong(p));
225
+ else
226
+ obj = (void*)REDIS_REPLY_INTEGER;
227
+ } else {
228
+ /* Type will be error or status. */
229
+ if (r->fn && r->fn->createString)
230
+ obj = r->fn->createString(cur,p,len);
231
+ else
232
+ obj = (void*)(size_t)(cur->type);
233
+ }
234
+
235
+ if (obj == NULL) {
236
+ __redisReaderSetErrorOOM(r);
237
+ return REDIS_ERR;
238
+ }
239
+
240
+ /* Set reply if this is the root object. */
241
+ if (r->ridx == 0) r->reply = obj;
242
+ moveToNextTask(r);
243
+ return REDIS_OK;
244
+ }
245
+
246
+ return REDIS_ERR;
247
+ }
248
+
249
+ static int processBulkItem(redisReader *r) {
250
+ redisReadTask *cur = &(r->rstack[r->ridx]);
251
+ void *obj = NULL;
252
+ char *p, *s;
253
+ long len;
254
+ unsigned long bytelen;
255
+ int success = 0;
256
+
257
+ p = r->buf+r->pos;
258
+ s = seekNewline(p,r->len-r->pos);
259
+ if (s != NULL) {
260
+ p = r->buf+r->pos;
261
+ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
262
+ len = readLongLong(p);
263
+
264
+ if (len < 0) {
265
+ /* The nil object can always be created. */
266
+ if (r->fn && r->fn->createNil)
267
+ obj = r->fn->createNil(cur);
268
+ else
269
+ obj = (void*)REDIS_REPLY_NIL;
270
+ success = 1;
271
+ } else {
272
+ /* Only continue when the buffer contains the entire bulk item. */
273
+ bytelen += len+2; /* include \r\n */
274
+ if (r->pos+bytelen <= r->len) {
275
+ if (r->fn && r->fn->createString)
276
+ obj = r->fn->createString(cur,s+2,len);
277
+ else
278
+ obj = (void*)REDIS_REPLY_STRING;
279
+ success = 1;
280
+ }
281
+ }
282
+
283
+ /* Proceed when obj was created. */
284
+ if (success) {
285
+ if (obj == NULL) {
286
+ __redisReaderSetErrorOOM(r);
287
+ return REDIS_ERR;
288
+ }
289
+
290
+ r->pos += bytelen;
291
+
292
+ /* Set reply if this is the root object. */
293
+ if (r->ridx == 0) r->reply = obj;
294
+ moveToNextTask(r);
295
+ return REDIS_OK;
296
+ }
297
+ }
298
+
299
+ return REDIS_ERR;
300
+ }
301
+
302
+ static int processMultiBulkItem(redisReader *r) {
303
+ redisReadTask *cur = &(r->rstack[r->ridx]);
304
+ void *obj;
305
+ char *p;
306
+ long elements;
307
+ int root = 0;
308
+
309
+ /* Set error for nested multi bulks with depth > 7 */
310
+ if (r->ridx == 8) {
311
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
312
+ "No support for nested multi bulk replies with depth > 7");
313
+ return REDIS_ERR;
314
+ }
315
+
316
+ if ((p = readLine(r,NULL)) != NULL) {
317
+ elements = readLongLong(p);
318
+ root = (r->ridx == 0);
319
+
320
+ if (elements == -1) {
321
+ if (r->fn && r->fn->createNil)
322
+ obj = r->fn->createNil(cur);
323
+ else
324
+ obj = (void*)REDIS_REPLY_NIL;
325
+
326
+ if (obj == NULL) {
327
+ __redisReaderSetErrorOOM(r);
328
+ return REDIS_ERR;
329
+ }
330
+
331
+ moveToNextTask(r);
332
+ } else {
333
+ if (r->fn && r->fn->createArray)
334
+ obj = r->fn->createArray(cur,elements);
335
+ else
336
+ obj = (void*)REDIS_REPLY_ARRAY;
337
+
338
+ if (obj == NULL) {
339
+ __redisReaderSetErrorOOM(r);
340
+ return REDIS_ERR;
341
+ }
342
+
343
+ /* Modify task stack when there are more than 0 elements. */
344
+ if (elements > 0) {
345
+ cur->elements = elements;
346
+ cur->obj = obj;
347
+ r->ridx++;
348
+ r->rstack[r->ridx].type = -1;
349
+ r->rstack[r->ridx].elements = -1;
350
+ r->rstack[r->ridx].idx = 0;
351
+ r->rstack[r->ridx].obj = NULL;
352
+ r->rstack[r->ridx].parent = cur;
353
+ r->rstack[r->ridx].privdata = r->privdata;
354
+ } else {
355
+ moveToNextTask(r);
356
+ }
357
+ }
358
+
359
+ /* Set reply if this is the root object. */
360
+ if (root) r->reply = obj;
361
+ return REDIS_OK;
362
+ }
363
+
364
+ return REDIS_ERR;
365
+ }
366
+
367
+ static int processItem(redisReader *r) {
368
+ redisReadTask *cur = &(r->rstack[r->ridx]);
369
+ char *p;
370
+
371
+ /* check if we need to read type */
372
+ if (cur->type < 0) {
373
+ if ((p = readBytes(r,1)) != NULL) {
374
+ switch (p[0]) {
375
+ case '-':
376
+ cur->type = REDIS_REPLY_ERROR;
377
+ break;
378
+ case '+':
379
+ cur->type = REDIS_REPLY_STATUS;
380
+ break;
381
+ case ':':
382
+ cur->type = REDIS_REPLY_INTEGER;
383
+ break;
384
+ case '$':
385
+ cur->type = REDIS_REPLY_STRING;
386
+ break;
387
+ case '*':
388
+ cur->type = REDIS_REPLY_ARRAY;
389
+ break;
390
+ default:
391
+ __redisReaderSetErrorProtocolByte(r,*p);
392
+ return REDIS_ERR;
393
+ }
394
+ } else {
395
+ /* could not consume 1 byte */
396
+ return REDIS_ERR;
397
+ }
398
+ }
399
+
400
+ /* process typed item */
401
+ switch(cur->type) {
402
+ case REDIS_REPLY_ERROR:
403
+ case REDIS_REPLY_STATUS:
404
+ case REDIS_REPLY_INTEGER:
405
+ return processLineItem(r);
406
+ case REDIS_REPLY_STRING:
407
+ return processBulkItem(r);
408
+ case REDIS_REPLY_ARRAY:
409
+ return processMultiBulkItem(r);
410
+ default:
411
+ assert(NULL);
412
+ return REDIS_ERR; /* Avoid warning. */
413
+ }
414
+ }
415
+
416
+ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
417
+ redisReader *r;
418
+
419
+ r = calloc(sizeof(redisReader),1);
420
+ if (r == NULL)
421
+ return NULL;
422
+
423
+ r->err = 0;
424
+ r->errstr[0] = '\0';
425
+ r->fn = fn;
426
+ r->buf = sdsempty();
427
+ r->maxbuf = REDIS_READER_MAX_BUF;
428
+ if (r->buf == NULL) {
429
+ free(r);
430
+ return NULL;
431
+ }
432
+
433
+ r->ridx = -1;
434
+ return r;
435
+ }
436
+
437
+ void redisReaderFree(redisReader *r) {
438
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
439
+ r->fn->freeObject(r->reply);
440
+ if (r->buf != NULL)
441
+ sdsfree(r->buf);
442
+ free(r);
443
+ }
444
+
445
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
446
+ sds newbuf;
447
+
448
+ /* Return early when this reader is in an erroneous state. */
449
+ if (r->err)
450
+ return REDIS_ERR;
451
+
452
+ /* Copy the provided buffer. */
453
+ if (buf != NULL && len >= 1) {
454
+ /* Destroy internal buffer when it is empty and is quite large. */
455
+ if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
456
+ sdsfree(r->buf);
457
+ r->buf = sdsempty();
458
+ r->pos = 0;
459
+
460
+ /* r->buf should not be NULL since we just free'd a larger one. */
461
+ assert(r->buf != NULL);
462
+ }
463
+
464
+ newbuf = sdscatlen(r->buf,buf,len);
465
+ if (newbuf == NULL) {
466
+ __redisReaderSetErrorOOM(r);
467
+ return REDIS_ERR;
468
+ }
469
+
470
+ r->buf = newbuf;
471
+ r->len = sdslen(r->buf);
472
+ }
473
+
474
+ return REDIS_OK;
475
+ }
476
+
477
+ int redisReaderGetReply(redisReader *r, void **reply) {
478
+ /* Default target pointer to NULL. */
479
+ if (reply != NULL)
480
+ *reply = NULL;
481
+
482
+ /* Return early when this reader is in an erroneous state. */
483
+ if (r->err)
484
+ return REDIS_ERR;
485
+
486
+ /* When the buffer is empty, there will never be a reply. */
487
+ if (r->len == 0)
488
+ return REDIS_OK;
489
+
490
+ /* Set first item to process when the stack is empty. */
491
+ if (r->ridx == -1) {
492
+ r->rstack[0].type = -1;
493
+ r->rstack[0].elements = -1;
494
+ r->rstack[0].idx = -1;
495
+ r->rstack[0].obj = NULL;
496
+ r->rstack[0].parent = NULL;
497
+ r->rstack[0].privdata = r->privdata;
498
+ r->ridx = 0;
499
+ }
500
+
501
+ /* Process items in reply. */
502
+ while (r->ridx >= 0)
503
+ if (processItem(r) != REDIS_OK)
504
+ break;
505
+
506
+ /* Return ASAP when an error occurred. */
507
+ if (r->err)
508
+ return REDIS_ERR;
509
+
510
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
511
+ * doing unnecessary calls to memmove() in sds.c. */
512
+ if (r->pos >= 1024) {
513
+ sdsrange(r->buf,r->pos,-1);
514
+ r->pos = 0;
515
+ r->len = sdslen(r->buf);
516
+ }
517
+
518
+ /* Emit a reply when there is one. */
519
+ if (r->ridx == -1) {
520
+ if (reply != NULL)
521
+ *reply = r->reply;
522
+ r->reply = NULL;
523
+ }
524
+ return REDIS_OK;
525
+ }
@@ -0,0 +1,111 @@
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
+
33
+ #ifndef __HIREDIS_READ_H
34
+ #define __HIREDIS_READ_H
35
+ #include <stdio.h> /* for size_t */
36
+
37
+ #define REDIS_ERR -1
38
+ #define REDIS_OK 0
39
+
40
+ /* When an error occurs, the err flag in a context is set to hold the type of
41
+ * error that occurred. REDIS_ERR_IO means there was an I/O error and you
42
+ * should use the "errno" variable to find out what is wrong.
43
+ * For other values, the "errstr" field will hold a description. */
44
+ #define REDIS_ERR_IO 1 /* Error in read or write */
45
+ #define REDIS_ERR_EOF 3 /* End of file */
46
+ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */
47
+ #define REDIS_ERR_OOM 5 /* Out of memory */
48
+ #define REDIS_ERR_OTHER 2 /* Everything else... */
49
+
50
+ #define REDIS_REPLY_STRING 1
51
+ #define REDIS_REPLY_ARRAY 2
52
+ #define REDIS_REPLY_INTEGER 3
53
+ #define REDIS_REPLY_NIL 4
54
+ #define REDIS_REPLY_STATUS 5
55
+ #define REDIS_REPLY_ERROR 6
56
+
57
+ #define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
58
+
59
+ #ifdef __cplusplus
60
+ extern "C" {
61
+ #endif
62
+
63
+ typedef struct redisReadTask {
64
+ int type;
65
+ int elements; /* number of elements in multibulk container */
66
+ int idx; /* index in parent (array) object */
67
+ void *obj; /* holds user-generated value for a read task */
68
+ struct redisReadTask *parent; /* parent task */
69
+ void *privdata; /* user-settable arbitrary field */
70
+ } redisReadTask;
71
+
72
+ typedef struct redisReplyObjectFunctions {
73
+ void *(*createString)(const redisReadTask*, char*, size_t);
74
+ void *(*createArray)(const redisReadTask*, int);
75
+ void *(*createInteger)(const redisReadTask*, long long);
76
+ void *(*createNil)(const redisReadTask*);
77
+ void (*freeObject)(void*);
78
+ } redisReplyObjectFunctions;
79
+
80
+ typedef struct redisReader {
81
+ int err; /* Error flags, 0 when there is no error */
82
+ char errstr[128]; /* String representation of error when applicable */
83
+
84
+ char *buf; /* Read buffer */
85
+ size_t pos; /* Buffer cursor */
86
+ size_t len; /* Buffer length */
87
+ size_t maxbuf; /* Max length of unused buffer */
88
+
89
+ redisReadTask rstack[9];
90
+ int ridx; /* Index of current read task */
91
+ void *reply; /* Temporary reply pointer */
92
+
93
+ redisReplyObjectFunctions *fn;
94
+ void *privdata;
95
+ } redisReader;
96
+
97
+ /* Public API for the protocol parser. */
98
+ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
99
+ void redisReaderFree(redisReader *r);
100
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len);
101
+ int redisReaderGetReply(redisReader *r, void **reply);
102
+
103
+ #define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
104
+ #define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
105
+ #define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
106
+
107
+ #ifdef __cplusplus
108
+ }
109
+ #endif
110
+
111
+ #endif