hiredis 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  /*
2
2
  * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
+ *
3
5
  * All rights reserved.
4
6
  *
5
7
  * Redistribution and use in source and binary forms, with or without
@@ -30,6 +32,7 @@
30
32
  #ifndef __HIREDIS_ASYNC_H
31
33
  #define __HIREDIS_ASYNC_H
32
34
  #include "hiredis.h"
35
+ #include "dict.h"
33
36
 
34
37
  #ifdef __cplusplus
35
38
  extern "C" {
@@ -50,8 +53,9 @@ typedef struct redisCallbackList {
50
53
  redisCallback *head, *tail;
51
54
  } redisCallbackList;
52
55
 
53
- /* Disconnect callback prototype */
56
+ /* Connection callback prototypes */
54
57
  typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
58
+ typedef void (redisConnectCallback)(const struct redisAsyncContext*);
55
59
 
56
60
  /* Context for an async connection to Redis */
57
61
  typedef struct redisAsyncContext {
@@ -65,30 +69,45 @@ typedef struct redisAsyncContext {
65
69
  /* Not used by hiredis */
66
70
  void *data;
67
71
 
68
- /* Used by the different event lib adapters to store their private data */
69
- void *_adapter_data;
72
+ /* Event library data and hooks */
73
+ struct {
74
+ void *data;
70
75
 
71
- /* Called when the library expects to start reading/writing.
72
- * The supplied functions should be idempotent. */
73
- void (*evAddRead)(void *privdata);
74
- void (*evDelRead)(void *privdata);
75
- void (*evAddWrite)(void *privdata);
76
- void (*evDelWrite)(void *privdata);
77
- void (*evCleanup)(void *privdata);
76
+ /* Hooks that are called when the library expects to start
77
+ * reading/writing. These functions should be idempotent. */
78
+ void (*addRead)(void *privdata);
79
+ void (*delRead)(void *privdata);
80
+ void (*addWrite)(void *privdata);
81
+ void (*delWrite)(void *privdata);
82
+ void (*cleanup)(void *privdata);
83
+ } ev;
78
84
 
79
85
  /* Called when either the connection is terminated due to an error or per
80
86
  * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
81
87
  redisDisconnectCallback *onDisconnect;
82
88
 
83
- /* Reply callbacks */
89
+ /* Called when the first write event was received. */
90
+ redisConnectCallback *onConnect;
91
+
92
+ /* Regular command callbacks */
84
93
  redisCallbackList replies;
94
+
95
+ /* Subscription callbacks */
96
+ struct {
97
+ redisCallbackList invalid;
98
+ dict *channels;
99
+ dict *patterns;
100
+ } sub;
85
101
  } redisAsyncContext;
86
102
 
87
103
  /* Functions that proxy to hiredis */
88
104
  redisAsyncContext *redisAsyncConnect(const char *ip, int port);
105
+ redisAsyncContext *redisAsyncConnectUnix(const char *path);
89
106
  int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
107
+ int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
90
108
  int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
91
109
  void redisAsyncDisconnect(redisAsyncContext *ac);
110
+ void redisAsyncFree(redisAsyncContext *ac);
92
111
 
93
112
  /* Handle read/write events */
94
113
  void redisAsyncHandleRead(redisAsyncContext *ac);
@@ -0,0 +1,388 @@
1
+ /* Hash table implementation.
2
+ *
3
+ * This file implements in memory hash tables with insert/del/replace/find/
4
+ * get-random-element operations. Hash tables will auto resize if needed
5
+ * tables of power of two in size are used, collisions are handled by
6
+ * chaining. See the source code for more information... :)
7
+ *
8
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
9
+ * All rights reserved.
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without
12
+ * modification, are permitted provided that the following conditions are met:
13
+ *
14
+ * * Redistributions of source code must retain the above copyright notice,
15
+ * this list of conditions and the following disclaimer.
16
+ * * Redistributions in binary form must reproduce the above copyright
17
+ * notice, this list of conditions and the following disclaimer in the
18
+ * documentation and/or other materials provided with the distribution.
19
+ * * Neither the name of Redis nor the names of its contributors may be used
20
+ * to endorse or promote products derived from this software without
21
+ * specific prior written permission.
22
+ *
23
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ * POSSIBILITY OF SUCH DAMAGE.
34
+ */
35
+
36
+ #include "fmacros.h"
37
+ #include <stdlib.h>
38
+ #include <assert.h>
39
+ #include <limits.h>
40
+ #include "dict.h"
41
+
42
+ /* -------------------------- private prototypes ---------------------------- */
43
+
44
+ static int _dictExpandIfNeeded(dict *ht);
45
+ static unsigned long _dictNextPower(unsigned long size);
46
+ static int _dictKeyIndex(dict *ht, const void *key);
47
+ static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
48
+
49
+ /* -------------------------- hash functions -------------------------------- */
50
+
51
+ /* Generic hash function (a popular one from Bernstein).
52
+ * I tested a few and this was the best. */
53
+ unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
54
+ unsigned int hash = 5381;
55
+
56
+ while (len--)
57
+ hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
58
+ return hash;
59
+ }
60
+
61
+ /* ----------------------------- API implementation ------------------------- */
62
+
63
+ /* Reset an hashtable already initialized with ht_init().
64
+ * NOTE: This function should only called by ht_destroy(). */
65
+ static void _dictReset(dict *ht) {
66
+ ht->table = NULL;
67
+ ht->size = 0;
68
+ ht->sizemask = 0;
69
+ ht->used = 0;
70
+ }
71
+
72
+ /* Create a new hash table */
73
+ dict *dictCreate(dictType *type, void *privDataPtr) {
74
+ dict *ht = malloc(sizeof(*ht));
75
+ _dictInit(ht,type,privDataPtr);
76
+ return ht;
77
+ }
78
+
79
+ /* Initialize the hash table */
80
+ int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
81
+ _dictReset(ht);
82
+ ht->type = type;
83
+ ht->privdata = privDataPtr;
84
+ return DICT_OK;
85
+ }
86
+
87
+ /* Resize the table to the minimal size that contains all the elements,
88
+ * but with the invariant of a USER/BUCKETS ration near to <= 1 */
89
+ int dictResize(dict *ht) {
90
+ int minimal = ht->used;
91
+ if (minimal < DICT_HT_INITIAL_SIZE)
92
+ minimal = DICT_HT_INITIAL_SIZE;
93
+ return dictExpand(ht, minimal);
94
+ }
95
+
96
+ /* Expand or create the hashtable */
97
+ int dictExpand(dict *ht, unsigned long size) {
98
+ dict n; /* the new hashtable */
99
+ unsigned long realsize = _dictNextPower(size), i;
100
+
101
+ /* the size is invalid if it is smaller than the number of
102
+ * elements already inside the hashtable */
103
+ if (ht->used > size)
104
+ return DICT_ERR;
105
+
106
+ _dictInit(&n, ht->type, ht->privdata);
107
+ n.size = realsize;
108
+ n.sizemask = realsize-1;
109
+ n.table = calloc(realsize,sizeof(dictEntry*));
110
+
111
+ /* Copy all the elements from the old to the new table:
112
+ * note that if the old hash table is empty ht->size is zero,
113
+ * so dictExpand just creates an hash table. */
114
+ n.used = ht->used;
115
+ for (i = 0; i < ht->size && ht->used > 0; i++) {
116
+ dictEntry *he, *nextHe;
117
+
118
+ if (ht->table[i] == NULL) continue;
119
+
120
+ /* For each hash entry on this slot... */
121
+ he = ht->table[i];
122
+ while(he) {
123
+ unsigned int h;
124
+
125
+ nextHe = he->next;
126
+ /* Get the new element index */
127
+ h = dictHashKey(ht, he->key) & n.sizemask;
128
+ he->next = n.table[h];
129
+ n.table[h] = he;
130
+ ht->used--;
131
+ /* Pass to the next element */
132
+ he = nextHe;
133
+ }
134
+ }
135
+ assert(ht->used == 0);
136
+ free(ht->table);
137
+
138
+ /* Remap the new hashtable in the old */
139
+ *ht = n;
140
+ return DICT_OK;
141
+ }
142
+
143
+ /* Add an element to the target hash table */
144
+ int dictAdd(dict *ht, void *key, void *val) {
145
+ int index;
146
+ dictEntry *entry;
147
+
148
+ /* Get the index of the new element, or -1 if
149
+ * the element already exists. */
150
+ if ((index = _dictKeyIndex(ht, key)) == -1)
151
+ return DICT_ERR;
152
+
153
+ /* Allocates the memory and stores key */
154
+ entry = malloc(sizeof(*entry));
155
+ entry->next = ht->table[index];
156
+ ht->table[index] = entry;
157
+
158
+ /* Set the hash entry fields. */
159
+ dictSetHashKey(ht, entry, key);
160
+ dictSetHashVal(ht, entry, val);
161
+ ht->used++;
162
+ return DICT_OK;
163
+ }
164
+
165
+ /* Add an element, discarding the old if the key already exists.
166
+ * Return 1 if the key was added from scratch, 0 if there was already an
167
+ * element with such key and dictReplace() just performed a value update
168
+ * operation. */
169
+ int dictReplace(dict *ht, void *key, void *val) {
170
+ dictEntry *entry, auxentry;
171
+
172
+ /* Try to add the element. If the key
173
+ * does not exists dictAdd will suceed. */
174
+ if (dictAdd(ht, key, val) == DICT_OK)
175
+ return 1;
176
+ /* It already exists, get the entry */
177
+ entry = dictFind(ht, key);
178
+ /* Free the old value and set the new one */
179
+ /* Set the new value and free the old one. Note that it is important
180
+ * to do that in this order, as the value may just be exactly the same
181
+ * as the previous one. In this context, think to reference counting,
182
+ * you want to increment (set), and then decrement (free), and not the
183
+ * reverse. */
184
+ auxentry = *entry;
185
+ dictSetHashVal(ht, entry, val);
186
+ dictFreeEntryVal(ht, &auxentry);
187
+ return 0;
188
+ }
189
+
190
+ /* Search and remove an element */
191
+ static int dictGenericDelete(dict *ht, const void *key, int nofree) {
192
+ unsigned int h;
193
+ dictEntry *he, *prevHe;
194
+
195
+ if (ht->size == 0)
196
+ return DICT_ERR;
197
+ h = dictHashKey(ht, key) & ht->sizemask;
198
+ he = ht->table[h];
199
+
200
+ prevHe = NULL;
201
+ while(he) {
202
+ if (dictCompareHashKeys(ht, key, he->key)) {
203
+ /* Unlink the element from the list */
204
+ if (prevHe)
205
+ prevHe->next = he->next;
206
+ else
207
+ ht->table[h] = he->next;
208
+ if (!nofree) {
209
+ dictFreeEntryKey(ht, he);
210
+ dictFreeEntryVal(ht, he);
211
+ }
212
+ free(he);
213
+ ht->used--;
214
+ return DICT_OK;
215
+ }
216
+ prevHe = he;
217
+ he = he->next;
218
+ }
219
+ return DICT_ERR; /* not found */
220
+ }
221
+
222
+ int dictDelete(dict *ht, const void *key) {
223
+ return dictGenericDelete(ht,key,0);
224
+ }
225
+
226
+ int dictDeleteNoFree(dict *ht, const void *key) {
227
+ return dictGenericDelete(ht,key,1);
228
+ }
229
+
230
+ /* Destroy an entire hash table */
231
+ int _dictClear(dict *ht) {
232
+ unsigned long i;
233
+
234
+ /* Free all the elements */
235
+ for (i = 0; i < ht->size && ht->used > 0; i++) {
236
+ dictEntry *he, *nextHe;
237
+
238
+ if ((he = ht->table[i]) == NULL) continue;
239
+ while(he) {
240
+ nextHe = he->next;
241
+ dictFreeEntryKey(ht, he);
242
+ dictFreeEntryVal(ht, he);
243
+ free(he);
244
+ ht->used--;
245
+ he = nextHe;
246
+ }
247
+ }
248
+ /* Free the table and the allocated cache structure */
249
+ free(ht->table);
250
+ /* Re-initialize the table */
251
+ _dictReset(ht);
252
+ return DICT_OK; /* never fails */
253
+ }
254
+
255
+ /* Clear & Release the hash table */
256
+ void dictRelease(dict *ht) {
257
+ _dictClear(ht);
258
+ free(ht);
259
+ }
260
+
261
+ dictEntry *dictFind(dict *ht, const void *key) {
262
+ dictEntry *he;
263
+ unsigned int h;
264
+
265
+ if (ht->size == 0) return NULL;
266
+ h = dictHashKey(ht, key) & ht->sizemask;
267
+ he = ht->table[h];
268
+ while(he) {
269
+ if (dictCompareHashKeys(ht, key, he->key))
270
+ return he;
271
+ he = he->next;
272
+ }
273
+ return NULL;
274
+ }
275
+
276
+ dictIterator *dictGetIterator(dict *ht) {
277
+ dictIterator *iter = malloc(sizeof(*iter));
278
+
279
+ iter->ht = ht;
280
+ iter->index = -1;
281
+ iter->entry = NULL;
282
+ iter->nextEntry = NULL;
283
+ return iter;
284
+ }
285
+
286
+ dictEntry *dictNext(dictIterator *iter) {
287
+ while (1) {
288
+ if (iter->entry == NULL) {
289
+ iter->index++;
290
+ if (iter->index >=
291
+ (signed)iter->ht->size) break;
292
+ iter->entry = iter->ht->table[iter->index];
293
+ } else {
294
+ iter->entry = iter->nextEntry;
295
+ }
296
+ if (iter->entry) {
297
+ /* We need to save the 'next' here, the iterator user
298
+ * may delete the entry we are returning. */
299
+ iter->nextEntry = iter->entry->next;
300
+ return iter->entry;
301
+ }
302
+ }
303
+ return NULL;
304
+ }
305
+
306
+ void dictReleaseIterator(dictIterator *iter) {
307
+ free(iter);
308
+ }
309
+
310
+ /* Return a random entry from the hash table. Useful to
311
+ * implement randomized algorithms */
312
+ dictEntry *dictGetRandomKey(dict *ht) {
313
+ dictEntry *he;
314
+ unsigned int h;
315
+ int listlen, listele;
316
+
317
+ if (ht->used == 0) return NULL;
318
+ do {
319
+ h = random() & ht->sizemask;
320
+ he = ht->table[h];
321
+ } while(he == NULL);
322
+
323
+ /* Now we found a non empty bucket, but it is a linked
324
+ * list and we need to get a random element from the list.
325
+ * The only sane way to do so is to count the element and
326
+ * select a random index. */
327
+ listlen = 0;
328
+ while(he) {
329
+ he = he->next;
330
+ listlen++;
331
+ }
332
+ listele = random() % listlen;
333
+ he = ht->table[h];
334
+ while(listele--) he = he->next;
335
+ return he;
336
+ }
337
+
338
+ /* ------------------------- private functions ------------------------------ */
339
+
340
+ /* Expand the hash table if needed */
341
+ static int _dictExpandIfNeeded(dict *ht) {
342
+ /* If the hash table is empty expand it to the intial size,
343
+ * if the table is "full" dobule its size. */
344
+ if (ht->size == 0)
345
+ return dictExpand(ht, DICT_HT_INITIAL_SIZE);
346
+ if (ht->used == ht->size)
347
+ return dictExpand(ht, ht->size*2);
348
+ return DICT_OK;
349
+ }
350
+
351
+ /* Our hash table capability is a power of two */
352
+ static unsigned long _dictNextPower(unsigned long size) {
353
+ unsigned long i = DICT_HT_INITIAL_SIZE;
354
+
355
+ if (size >= LONG_MAX) return LONG_MAX;
356
+ while(1) {
357
+ if (i >= size)
358
+ return i;
359
+ i *= 2;
360
+ }
361
+ }
362
+
363
+ /* Returns the index of a free slot that can be populated with
364
+ * an hash entry for the given 'key'.
365
+ * If the key already exists, -1 is returned. */
366
+ static int _dictKeyIndex(dict *ht, const void *key) {
367
+ unsigned int h;
368
+ dictEntry *he;
369
+
370
+ /* Expand the hashtable if needed */
371
+ if (_dictExpandIfNeeded(ht) == DICT_ERR)
372
+ return -1;
373
+ /* Compute the key hash value */
374
+ h = dictHashKey(ht, key) & ht->sizemask;
375
+ /* Search if this slot does not already contain the given key */
376
+ he = ht->table[h];
377
+ while(he) {
378
+ if (dictCompareHashKeys(ht, key, he->key))
379
+ return -1;
380
+ he = he->next;
381
+ }
382
+ return h;
383
+ }
384
+
385
+ void dictEmpty(dict *ht) {
386
+ _dictClear(ht);
387
+ }
388
+