hashtable.c revision 35c4bbdf
1#ifdef HAVE_DIX_CONFIG_H
2#include <dix-config.h>
3#endif
4
5#include <stdlib.h>
6#include "misc.h"
7#include "hashtable.h"
8
9/* HashResourceID */
10#include "resource.h"
11
12#define INITHASHSIZE 6
13#define MAXHASHSIZE 11
14
15struct HashTableRec {
16    int             keySize;
17    int             dataSize;
18
19    int             elements;   /* number of elements inserted */
20    int             bucketBits; /* number of buckets is 1 << bucketBits */
21    struct xorg_list *buckets;  /* array of bucket list heads */
22
23    HashFunc        hash;
24    HashCompareFunc compare;
25
26    void            *cdata;
27};
28
29typedef struct {
30    struct xorg_list l;
31    void *key;
32    void *data;
33} BucketRec, *BucketPtr;
34
35HashTable
36ht_create(int             keySize,
37          int             dataSize,
38          HashFunc        hash,
39          HashCompareFunc compare,
40          void            *cdata)
41{
42    int c;
43    int numBuckets;
44    HashTable ht = malloc(sizeof(struct HashTableRec));
45
46    if (!ht) {
47        return NULL;
48    }
49
50    ht->keySize = keySize;
51    ht->dataSize = dataSize;
52    ht->hash = hash;
53    ht->compare = compare;
54    ht->elements = 0;
55    ht->bucketBits = INITHASHSIZE;
56    numBuckets = 1 << ht->bucketBits;
57    ht->buckets = xallocarray(numBuckets, sizeof(*ht->buckets));
58    ht->cdata = cdata;
59
60    if (ht->buckets) {
61        for (c = 0; c < numBuckets; ++c) {
62            xorg_list_init(&ht->buckets[c]);
63        }
64        return ht;
65    } else {
66        free(ht);
67        return NULL;
68    }
69}
70
71void
72ht_destroy(HashTable ht)
73{
74    int c;
75    BucketPtr it, tmp;
76    int numBuckets = 1 << ht->bucketBits;
77    for (c = 0; c < numBuckets; ++c) {
78        xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
79            xorg_list_del(&it->l);
80            free(it);
81        }
82    }
83    free(ht->buckets);
84}
85
86static Bool
87double_size(HashTable ht)
88{
89    struct xorg_list *newBuckets;
90    int numBuckets = 1 << ht->bucketBits;
91    int newBucketBits = ht->bucketBits + 1;
92    int newNumBuckets = 1 << newBucketBits;
93    int c;
94
95    newBuckets = xallocarray(newNumBuckets, sizeof(*ht->buckets));
96    if (newBuckets) {
97        for (c = 0; c < newNumBuckets; ++c) {
98            xorg_list_init(&newBuckets[c]);
99        }
100
101        for (c = 0; c < numBuckets; ++c) {
102            BucketPtr it, tmp;
103            xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
104                struct xorg_list *newBucket =
105                    &newBuckets[ht->hash(ht->cdata, it->key, newBucketBits)];
106                xorg_list_del(&it->l);
107                xorg_list_add(&it->l, newBucket);
108            }
109        }
110        free(ht->buckets);
111
112        ht->buckets = newBuckets;
113        ht->bucketBits = newBucketBits;
114        return TRUE;
115    } else {
116        return FALSE;
117    }
118}
119
120void *
121ht_add(HashTable ht, const void *key)
122{
123    unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
124    struct xorg_list *bucket = &ht->buckets[index];
125    BucketRec *elem = calloc(1, sizeof(BucketRec));
126    if (!elem) {
127        goto outOfMemory;
128    }
129    elem->key = malloc(ht->keySize);
130    if (!elem->key) {
131        goto outOfMemory;
132    }
133    /* we avoid signaling an out-of-memory error if dataSize is 0 */
134    elem->data = calloc(1, ht->dataSize);
135    if (ht->dataSize && !elem->data) {
136        goto outOfMemory;
137    }
138    xorg_list_add(&elem->l, bucket);
139    ++ht->elements;
140
141    memcpy(elem->key, key, ht->keySize);
142
143    if (ht->elements > 4 * (1 << ht->bucketBits) &&
144        ht->bucketBits < MAXHASHSIZE) {
145        if (!double_size(ht)) {
146            --ht->elements;
147            xorg_list_del(&elem->l);
148            goto outOfMemory;
149        }
150    }
151
152    /* if memory allocation has failed due to dataSize being 0, return
153       a "dummy" pointer pointing at the of the key */
154    return elem->data ? elem->data : ((char*) elem->key + ht->keySize);
155
156 outOfMemory:
157    if (elem) {
158        free(elem->key);
159        free(elem->data);
160        free(elem);
161    }
162
163    return NULL;
164}
165
166void
167ht_remove(HashTable ht, const void *key)
168{
169    unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
170    struct xorg_list *bucket = &ht->buckets[index];
171    BucketPtr it;
172
173    xorg_list_for_each_entry(it, bucket, l) {
174        if (ht->compare(ht->cdata, key, it->key) == 0) {
175            xorg_list_del(&it->l);
176            --ht->elements;
177            free(it->key);
178            free(it->data);
179            free(it);
180            return;
181        }
182    }
183}
184
185void *
186ht_find(HashTable ht, const void *key)
187{
188    unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
189    struct xorg_list *bucket = &ht->buckets[index];
190    BucketPtr it;
191
192    xorg_list_for_each_entry(it, bucket, l) {
193        if (ht->compare(ht->cdata, key, it->key) == 0) {
194            return it->data ? it->data : ((char*) it->key + ht->keySize);
195        }
196    }
197
198    return NULL;
199}
200
201void
202ht_dump_distribution(HashTable ht)
203{
204    int c;
205    int numBuckets = 1 << ht->bucketBits;
206    for (c = 0; c < numBuckets; ++c) {
207        BucketPtr it;
208        int n = 0;
209
210        xorg_list_for_each_entry(it, &ht->buckets[c], l) {
211            ++n;
212        }
213        printf("%d: %d\n", c, n);
214    }
215}
216
217/* Picked the function from http://burtleburtle.net/bob/hash/doobs.html by
218   Bob Jenkins, which is released in public domain */
219static CARD32
220one_at_a_time_hash(const void *data, int len)
221{
222    CARD32 hash;
223    int i;
224    const char *key = data;
225    for (hash=0, i=0; i<len; ++i) {
226        hash += key[i];
227        hash += (hash << 10);
228        hash ^= (hash >> 6);
229    }
230    hash += (hash << 3);
231    hash ^= (hash >> 11);
232    hash += (hash << 15);
233    return hash;
234}
235
236unsigned
237ht_generic_hash(void *cdata, const void *ptr, int numBits)
238{
239    HtGenericHashSetupPtr setup = cdata;
240    return one_at_a_time_hash(ptr, setup->keySize) & ~((~0) << numBits);
241}
242
243int
244ht_generic_compare(void *cdata, const void *l, const void *r)
245{
246    HtGenericHashSetupPtr setup = cdata;
247    return memcmp(l, r, setup->keySize);
248}
249
250unsigned
251ht_resourceid_hash(void * cdata, const void * data, int numBits)
252{
253    const XID* idPtr = data;
254    XID id = *idPtr & RESOURCE_ID_MASK;
255    (void) cdata;
256    return HashResourceID(id, numBits);
257}
258
259int
260ht_resourceid_compare(void* cdata, const void* a, const void* b)
261{
262    const XID* xa = a;
263    const XID* xb = b;
264    (void) cdata;
265    return
266        *xa < *xb ? -1 :
267        *xa > *xb ? 1 :
268        0;
269}
270
271void
272ht_dump_contents(HashTable ht,
273                 void (*print_key)(void *opaque, void *key),
274                 void (*print_value)(void *opaque, void *value),
275                 void* opaque)
276{
277    int c;
278    int numBuckets = 1 << ht->bucketBits;
279    for (c = 0; c < numBuckets; ++c) {
280        BucketPtr it;
281        int n = 0;
282
283        printf("%d: ", c);
284        xorg_list_for_each_entry(it, &ht->buckets[c], l) {
285            if (n > 0) {
286                printf(", ");
287            }
288            print_key(opaque, it->key);
289            printf("->");
290            print_value(opaque, it->data);
291            ++n;
292        }
293        printf("\n");
294    }
295}
296