hiredis 0.5.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Rakefile +8 -6
- data/ext/hiredis_ext/connection.c +4 -3
- data/ext/hiredis_ext/extconf.rb +25 -10
- data/ext/hiredis_ext/reader.c +12 -6
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +119 -54
- data/vendor/hiredis/async.c +121 -27
- data/vendor/hiredis/async.h +5 -0
- data/vendor/hiredis/dict.c +2 -2
- data/vendor/hiredis/fmacros.h +5 -9
- data/vendor/hiredis/hiredis.c +275 -554
- data/vendor/hiredis/hiredis.h +64 -75
- data/vendor/hiredis/net.c +242 -56
- data/vendor/hiredis/net.h +12 -10
- data/vendor/hiredis/read.c +598 -0
- data/vendor/hiredis/read.h +111 -0
- data/vendor/hiredis/sds.c +847 -180
- data/vendor/hiredis/sds.h +203 -18
- data/vendor/hiredis/sdsalloc.h +42 -0
- data/vendor/hiredis/test.c +286 -19
- data/vendor/hiredis/win32.h +42 -0
- metadata +46 -28
- data/lib/hiredis/errors.rb +0 -5
data/vendor/hiredis/async.c
CHANGED
@@ -58,11 +58,12 @@
|
|
58
58
|
} while(0);
|
59
59
|
|
60
60
|
/* Forward declaration of function in hiredis.c */
|
61
|
-
|
61
|
+
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
62
62
|
|
63
63
|
/* Functions managing dictionary of callbacks for pub/sub. */
|
64
64
|
static unsigned int callbackHash(const void *key) {
|
65
|
-
return dictGenHashFunction((unsigned char*)key,
|
65
|
+
return dictGenHashFunction((const unsigned char *)key,
|
66
|
+
sdslen((const sds)key));
|
66
67
|
}
|
67
68
|
|
68
69
|
static void *callbackValDup(void *privdata, const void *src) {
|
@@ -76,8 +77,8 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
|
|
76
77
|
int l1, l2;
|
77
78
|
((void) privdata);
|
78
79
|
|
79
|
-
l1 = sdslen((sds)key1);
|
80
|
-
l2 = sdslen((sds)key2);
|
80
|
+
l1 = sdslen((const sds)key1);
|
81
|
+
l2 = sdslen((const sds)key2);
|
81
82
|
if (l1 != l2) return 0;
|
82
83
|
return memcmp(key1,key2,l1) == 0;
|
83
84
|
}
|
@@ -102,7 +103,12 @@ static dictType callbackDict = {
|
|
102
103
|
};
|
103
104
|
|
104
105
|
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
105
|
-
redisAsyncContext *ac
|
106
|
+
redisAsyncContext *ac;
|
107
|
+
|
108
|
+
ac = realloc(c,sizeof(redisAsyncContext));
|
109
|
+
if (ac == NULL)
|
110
|
+
return NULL;
|
111
|
+
|
106
112
|
c = &(ac->c);
|
107
113
|
|
108
114
|
/* The regular connect functions will always set the flag REDIS_CONNECTED.
|
@@ -136,25 +142,66 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
|
136
142
|
/* We want the error field to be accessible directly instead of requiring
|
137
143
|
* an indirection to the redisContext struct. */
|
138
144
|
static void __redisAsyncCopyError(redisAsyncContext *ac) {
|
145
|
+
if (!ac)
|
146
|
+
return;
|
147
|
+
|
139
148
|
redisContext *c = &(ac->c);
|
140
149
|
ac->err = c->err;
|
141
150
|
ac->errstr = c->errstr;
|
142
151
|
}
|
143
152
|
|
144
153
|
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
145
|
-
redisContext *c
|
154
|
+
redisContext *c;
|
155
|
+
redisAsyncContext *ac;
|
156
|
+
|
157
|
+
c = redisConnectNonBlock(ip,port);
|
158
|
+
if (c == NULL)
|
159
|
+
return NULL;
|
160
|
+
|
161
|
+
ac = redisAsyncInitialize(c);
|
162
|
+
if (ac == NULL) {
|
163
|
+
redisFree(c);
|
164
|
+
return NULL;
|
165
|
+
}
|
166
|
+
|
167
|
+
__redisAsyncCopyError(ac);
|
168
|
+
return ac;
|
169
|
+
}
|
170
|
+
|
171
|
+
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
|
172
|
+
const char *source_addr) {
|
173
|
+
redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
|
146
174
|
redisAsyncContext *ac = redisAsyncInitialize(c);
|
147
175
|
__redisAsyncCopyError(ac);
|
148
176
|
return ac;
|
149
177
|
}
|
150
178
|
|
151
|
-
redisAsyncContext *
|
152
|
-
|
179
|
+
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
180
|
+
const char *source_addr) {
|
181
|
+
redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
|
153
182
|
redisAsyncContext *ac = redisAsyncInitialize(c);
|
154
183
|
__redisAsyncCopyError(ac);
|
155
184
|
return ac;
|
156
185
|
}
|
157
186
|
|
187
|
+
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
188
|
+
redisContext *c;
|
189
|
+
redisAsyncContext *ac;
|
190
|
+
|
191
|
+
c = redisConnectUnixNonBlock(path);
|
192
|
+
if (c == NULL)
|
193
|
+
return NULL;
|
194
|
+
|
195
|
+
ac = redisAsyncInitialize(c);
|
196
|
+
if (ac == NULL) {
|
197
|
+
redisFree(c);
|
198
|
+
return NULL;
|
199
|
+
}
|
200
|
+
|
201
|
+
__redisAsyncCopyError(ac);
|
202
|
+
return ac;
|
203
|
+
}
|
204
|
+
|
158
205
|
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
159
206
|
if (ac->onConnect == NULL) {
|
160
207
|
ac->onConnect = fn;
|
@@ -182,6 +229,9 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
|
182
229
|
|
183
230
|
/* Copy callback from stack to heap */
|
184
231
|
cb = malloc(sizeof(*cb));
|
232
|
+
if (cb == NULL)
|
233
|
+
return REDIS_ERR_OOM;
|
234
|
+
|
185
235
|
if (source != NULL) {
|
186
236
|
memcpy(cb,source,sizeof(*cb));
|
187
237
|
cb->next = NULL;
|
@@ -286,7 +336,8 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
|
286
336
|
|
287
337
|
if (ac->err == 0) {
|
288
338
|
/* For clean disconnects, there should be no pending callbacks. */
|
289
|
-
|
339
|
+
int ret = __redisShiftCallback(&ac->replies,NULL);
|
340
|
+
assert(ret == REDIS_ERR);
|
290
341
|
} else {
|
291
342
|
/* Disconnection is caused by an error, make sure that pending
|
292
343
|
* callbacks cannot call new commands. */
|
@@ -314,6 +365,7 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
|
|
314
365
|
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
|
315
366
|
redisContext *c = &(ac->c);
|
316
367
|
dict *callbacks;
|
368
|
+
redisCallback *cb;
|
317
369
|
dictEntry *de;
|
318
370
|
int pvariant;
|
319
371
|
char *stype;
|
@@ -337,16 +389,28 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
|
337
389
|
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
338
390
|
de = dictFind(callbacks,sname);
|
339
391
|
if (de != NULL) {
|
340
|
-
|
392
|
+
cb = dictGetEntryVal(de);
|
393
|
+
|
394
|
+
/* If this is an subscribe reply decrease pending counter. */
|
395
|
+
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
396
|
+
cb->pending_subs -= 1;
|
397
|
+
}
|
398
|
+
|
399
|
+
memcpy(dstcb,cb,sizeof(*dstcb));
|
341
400
|
|
342
401
|
/* If this is an unsubscribe message, remove it. */
|
343
402
|
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
344
|
-
|
403
|
+
if (cb->pending_subs == 0)
|
404
|
+
dictDelete(callbacks,sname);
|
345
405
|
|
346
406
|
/* If this was the last unsubscribe message, revert to
|
347
407
|
* non-subscribe mode. */
|
348
408
|
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
349
|
-
|
409
|
+
|
410
|
+
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
411
|
+
if (reply->element[2]->integer == 0
|
412
|
+
&& dictSize(ac->sub.channels) == 0
|
413
|
+
&& dictSize(ac->sub.patterns) == 0)
|
350
414
|
c->flags &= ~REDIS_SUBSCRIBED;
|
351
415
|
}
|
352
416
|
}
|
@@ -360,7 +424,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
|
360
424
|
|
361
425
|
void redisProcessCallbacks(redisAsyncContext *ac) {
|
362
426
|
redisContext *c = &(ac->c);
|
363
|
-
redisCallback cb;
|
427
|
+
redisCallback cb = {NULL, NULL, 0, NULL};
|
364
428
|
void *reply = NULL;
|
365
429
|
int status;
|
366
430
|
|
@@ -368,11 +432,12 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
|
368
432
|
if (reply == NULL) {
|
369
433
|
/* When the connection is being disconnected and there are
|
370
434
|
* no more replies, this is the cue to really disconnect. */
|
371
|
-
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
|
435
|
+
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
|
436
|
+
&& ac->replies.head == NULL) {
|
372
437
|
__redisAsyncDisconnect(ac);
|
373
438
|
return;
|
374
439
|
}
|
375
|
-
|
440
|
+
|
376
441
|
/* If monitor mode, repush callback */
|
377
442
|
if(c->flags & REDIS_MONITORING) {
|
378
443
|
__redisPushCallback(&ac->replies,&cb);
|
@@ -404,6 +469,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
|
404
469
|
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
|
405
470
|
c->err = REDIS_ERR_OTHER;
|
406
471
|
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
|
472
|
+
c->reader->fn->freeObject(reply);
|
407
473
|
__redisAsyncDisconnect(ac);
|
408
474
|
return;
|
409
475
|
}
|
@@ -437,12 +503,12 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
|
437
503
|
}
|
438
504
|
|
439
505
|
/* Internal helper function to detect socket status the first time a read or
|
440
|
-
* write event fires. When connecting was not
|
506
|
+
* write event fires. When connecting was not successful, the connect callback
|
441
507
|
* is called with a REDIS_ERR status and the context is free'd. */
|
442
508
|
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
443
509
|
redisContext *c = &(ac->c);
|
444
510
|
|
445
|
-
if (redisCheckSocketError(c
|
511
|
+
if (redisCheckSocketError(c) == REDIS_ERR) {
|
446
512
|
/* Try again later when connect(2) is still in progress. */
|
447
513
|
if (errno == EINPROGRESS)
|
448
514
|
return REDIS_OK;
|
@@ -511,8 +577,8 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
|
511
577
|
|
512
578
|
/* Sets a pointer to the first argument and its length starting at p. Returns
|
513
579
|
* the number of bytes to skip to get to the following argument. */
|
514
|
-
static char *nextArgument(char *start, char **str, size_t *len) {
|
515
|
-
char *p = start;
|
580
|
+
static const char *nextArgument(const char *start, const char **str, size_t *len) {
|
581
|
+
const char *p = start;
|
516
582
|
if (p[0] != '$') {
|
517
583
|
p = strchr(p,'$');
|
518
584
|
if (p == NULL) return NULL;
|
@@ -528,14 +594,18 @@ static char *nextArgument(char *start, char **str, size_t *len) {
|
|
528
594
|
/* Helper function for the redisAsyncCommand* family of functions. Writes a
|
529
595
|
* formatted command to the output buffer and registers the provided callback
|
530
596
|
* function with the context. */
|
531
|
-
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
|
597
|
+
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
532
598
|
redisContext *c = &(ac->c);
|
533
599
|
redisCallback cb;
|
600
|
+
struct dict *cbdict;
|
601
|
+
dictEntry *de;
|
602
|
+
redisCallback *existcb;
|
534
603
|
int pvariant, hasnext;
|
535
|
-
char *cstr, *astr;
|
604
|
+
const char *cstr, *astr;
|
536
605
|
size_t clen, alen;
|
537
|
-
char *p;
|
606
|
+
const char *p;
|
538
607
|
sds sname;
|
608
|
+
int ret;
|
539
609
|
|
540
610
|
/* Don't accept new commands when the connection is about to be closed. */
|
541
611
|
if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
|
@@ -543,6 +613,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
|
543
613
|
/* Setup callback */
|
544
614
|
cb.fn = fn;
|
545
615
|
cb.privdata = privdata;
|
616
|
+
cb.pending_subs = 1;
|
546
617
|
|
547
618
|
/* Find out which command will be appended. */
|
548
619
|
p = nextArgument(cmd,&cstr,&clen);
|
@@ -559,9 +630,20 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
|
559
630
|
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
560
631
|
sname = sdsnewlen(astr,alen);
|
561
632
|
if (pvariant)
|
562
|
-
|
633
|
+
cbdict = ac->sub.patterns;
|
563
634
|
else
|
564
|
-
|
635
|
+
cbdict = ac->sub.channels;
|
636
|
+
|
637
|
+
de = dictFind(cbdict,sname);
|
638
|
+
|
639
|
+
if (de != NULL) {
|
640
|
+
existcb = dictGetEntryVal(de);
|
641
|
+
cb.pending_subs = existcb->pending_subs + 1;
|
642
|
+
}
|
643
|
+
|
644
|
+
ret = dictReplace(cbdict,sname,&cb);
|
645
|
+
|
646
|
+
if (ret == 0) sdsfree(sname);
|
565
647
|
}
|
566
648
|
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
567
649
|
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
@@ -597,6 +679,11 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
|
|
597
679
|
int len;
|
598
680
|
int status;
|
599
681
|
len = redisvFormatCommand(&cmd,format,ap);
|
682
|
+
|
683
|
+
/* We don't want to pass -1 or -2 to future functions as a length. */
|
684
|
+
if (len < 0)
|
685
|
+
return REDIS_ERR;
|
686
|
+
|
600
687
|
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
601
688
|
free(cmd);
|
602
689
|
return status;
|
@@ -612,11 +699,18 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata
|
|
612
699
|
}
|
613
700
|
|
614
701
|
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
|
615
|
-
|
702
|
+
sds cmd;
|
616
703
|
int len;
|
617
704
|
int status;
|
618
|
-
len =
|
705
|
+
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
706
|
+
if (len < 0)
|
707
|
+
return REDIS_ERR;
|
619
708
|
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
620
|
-
|
709
|
+
sdsfree(cmd);
|
710
|
+
return status;
|
711
|
+
}
|
712
|
+
|
713
|
+
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
714
|
+
int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
621
715
|
return status;
|
622
716
|
}
|
data/vendor/hiredis/async.h
CHANGED
@@ -45,6 +45,7 @@ typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
|
|
45
45
|
typedef struct redisCallback {
|
46
46
|
struct redisCallback *next; /* simple singly linked list */
|
47
47
|
redisCallbackFn *fn;
|
48
|
+
int pending_subs;
|
48
49
|
void *privdata;
|
49
50
|
} redisCallback;
|
50
51
|
|
@@ -102,6 +103,9 @@ typedef struct redisAsyncContext {
|
|
102
103
|
|
103
104
|
/* Functions that proxy to hiredis */
|
104
105
|
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
|
106
|
+
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
|
107
|
+
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
108
|
+
const char *source_addr);
|
105
109
|
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
106
110
|
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
107
111
|
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
@@ -117,6 +121,7 @@ void redisAsyncHandleWrite(redisAsyncContext *ac);
|
|
117
121
|
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
|
118
122
|
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
|
119
123
|
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
|
124
|
+
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
|
120
125
|
|
121
126
|
#ifdef __cplusplus
|
122
127
|
}
|
data/vendor/hiredis/dict.c
CHANGED
@@ -161,7 +161,7 @@ static int dictReplace(dict *ht, void *key, void *val) {
|
|
161
161
|
dictEntry *entry, auxentry;
|
162
162
|
|
163
163
|
/* Try to add the element. If the key
|
164
|
-
* does not exists dictAdd will
|
164
|
+
* does not exists dictAdd will succeed. */
|
165
165
|
if (dictAdd(ht, key, val) == DICT_OK)
|
166
166
|
return 1;
|
167
167
|
/* It already exists, get the entry */
|
@@ -293,7 +293,7 @@ static void dictReleaseIterator(dictIterator *iter) {
|
|
293
293
|
|
294
294
|
/* Expand the hash table if needed */
|
295
295
|
static int _dictExpandIfNeeded(dict *ht) {
|
296
|
-
/* If the hash table is empty expand it to the
|
296
|
+
/* If the hash table is empty expand it to the initial size,
|
297
297
|
* if the table is "full" dobule its size. */
|
298
298
|
if (ht->size == 0)
|
299
299
|
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
data/vendor/hiredis/fmacros.h
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
#ifndef __HIREDIS_FMACRO_H
|
2
2
|
#define __HIREDIS_FMACRO_H
|
3
3
|
|
4
|
-
#if !defined(_BSD_SOURCE)
|
5
|
-
#define _BSD_SOURCE
|
6
|
-
#endif
|
7
|
-
|
8
|
-
#if defined(__sun__)
|
9
|
-
#define _POSIX_C_SOURCE 200112L
|
10
|
-
#elif defined(__linux__)
|
11
4
|
#define _XOPEN_SOURCE 600
|
12
|
-
#
|
13
|
-
|
5
|
+
#define _POSIX_C_SOURCE 200112L
|
6
|
+
|
7
|
+
#if defined(__APPLE__) && defined(__MACH__)
|
8
|
+
/* Enable TCP_KEEPALIVE */
|
9
|
+
#define _DARWIN_C_SOURCE
|
14
10
|
#endif
|
15
11
|
|
16
12
|
#endif
|