1706f2543Smrg/* x-hash.c - basic hash tables
2706f2543Smrg
3706f2543Smrg   Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4706f2543Smrg
5706f2543Smrg   Permission is hereby granted, free of charge, to any person
6706f2543Smrg   obtaining a copy of this software and associated documentation files
7706f2543Smrg   (the "Software"), to deal in the Software without restriction,
8706f2543Smrg   including without limitation the rights to use, copy, modify, merge,
9706f2543Smrg   publish, distribute, sublicense, and/or sell copies of the Software,
10706f2543Smrg   and to permit persons to whom the Software is furnished to do so,
11706f2543Smrg   subject to the following conditions:
12706f2543Smrg
13706f2543Smrg   The above copyright notice and this permission notice shall be
14706f2543Smrg   included in all copies or substantial portions of the Software.
15706f2543Smrg
16706f2543Smrg   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17706f2543Smrg   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18706f2543Smrg   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19706f2543Smrg   NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
20706f2543Smrg   HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21706f2543Smrg   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22706f2543Smrg   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23706f2543Smrg   DEALINGS IN THE SOFTWARE.
24706f2543Smrg
25706f2543Smrg   Except as contained in this notice, the name(s) of the above
26706f2543Smrg   copyright holders shall not be used in advertising or otherwise to
27706f2543Smrg   promote the sale, use or other dealings in this Software without
28706f2543Smrg   prior written authorization. */
29706f2543Smrg
30706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
31706f2543Smrg#include <dix-config.h>
32706f2543Smrg#endif
33706f2543Smrg
34706f2543Smrg#include "x-hash.h"
35706f2543Smrg#include "x-list.h"
36706f2543Smrg#include <stdlib.h>
37706f2543Smrg#include <assert.h>
38706f2543Smrg
39706f2543Smrgstruct x_hash_table_struct {
40706f2543Smrg    unsigned int bucket_index;
41706f2543Smrg    unsigned int total_keys;
42706f2543Smrg    x_list **buckets;
43706f2543Smrg
44706f2543Smrg    x_hash_fun *hash_key;
45706f2543Smrg    x_compare_fun *compare_keys;
46706f2543Smrg    x_destroy_fun *destroy_key;
47706f2543Smrg    x_destroy_fun *destroy_value;
48706f2543Smrg};
49706f2543Smrg
50706f2543Smrg#define ITEM_NEW(k, v) X_PFX (list_prepend) ((x_list *) (k), v)
51706f2543Smrg#define ITEM_FREE(i) X_PFX (list_free_1) (i)
52706f2543Smrg#define ITEM_KEY(i) ((void *) (i)->next)
53706f2543Smrg#define ITEM_VALUE(i) ((i)->data)
54706f2543Smrg
55706f2543Smrg#define SPLIT_THRESHOLD_FACTOR 2
56706f2543Smrg
57706f2543Smrg/* http://planetmath.org/?op=getobj&from=objects&name=GoodHashTablePrimes */
58706f2543Smrgstatic const unsigned int bucket_sizes[] = {
59706f2543Smrg    29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157,
60706f2543Smrg    98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917,
61706f2543Smrg    25165843, 50331653, 100663319, 201326611, 402653189, 805306457,
62706f2543Smrg    1610612741
63706f2543Smrg};
64706f2543Smrg
65706f2543Smrg#define N_BUCKET_SIZES (sizeof (bucket_sizes) / sizeof (bucket_sizes[0]))
66706f2543Smrg
67706f2543Smrgstatic inline unsigned int
68706f2543Smrghash_table_total_buckets (x_hash_table *h)
69706f2543Smrg{
70706f2543Smrg    return bucket_sizes[h->bucket_index];
71706f2543Smrg}
72706f2543Smrg
73706f2543Smrgstatic inline void
74706f2543Smrghash_table_destroy_item (x_hash_table *h, void *k, void *v)
75706f2543Smrg{
76706f2543Smrg    if (h->destroy_key != 0)
77706f2543Smrg        (*h->destroy_key) (k);
78706f2543Smrg
79706f2543Smrg    if (h->destroy_value != 0)
80706f2543Smrg        (*h->destroy_value) (v);
81706f2543Smrg}
82706f2543Smrg
83706f2543Smrgstatic inline size_t
84706f2543Smrghash_table_hash_key (x_hash_table *h, void *k)
85706f2543Smrg{
86706f2543Smrg    if (h->hash_key != 0)
87706f2543Smrg        return (*h->hash_key) (k);
88706f2543Smrg    else
89706f2543Smrg        return (size_t) k;
90706f2543Smrg}
91706f2543Smrg
92706f2543Smrgstatic inline int
93706f2543Smrghash_table_compare_keys (x_hash_table *h, void *k1, void *k2)
94706f2543Smrg{
95706f2543Smrg    if (h->compare_keys == 0)
96706f2543Smrg        return k1 == k2;
97706f2543Smrg    else
98706f2543Smrg        return (*h->compare_keys) (k1, k2) == 0;
99706f2543Smrg}
100706f2543Smrg
101706f2543Smrgstatic void
102706f2543Smrghash_table_split (x_hash_table *h)
103706f2543Smrg{
104706f2543Smrg    x_list **new, **old;
105706f2543Smrg    x_list *node, *item, *next;
106706f2543Smrg    int new_size, old_size;
107706f2543Smrg    size_t b;
108706f2543Smrg    int i;
109706f2543Smrg
110706f2543Smrg    if (h->bucket_index == N_BUCKET_SIZES - 1)
111706f2543Smrg        return;
112706f2543Smrg
113706f2543Smrg    old_size = hash_table_total_buckets (h);
114706f2543Smrg    old = h->buckets;
115706f2543Smrg
116706f2543Smrg    h->bucket_index++;
117706f2543Smrg
118706f2543Smrg    new_size = hash_table_total_buckets (h);
119706f2543Smrg    new = calloc (new_size, sizeof (x_list *));
120706f2543Smrg
121706f2543Smrg    if (new == 0)
122706f2543Smrg    {
123706f2543Smrg        h->bucket_index--;
124706f2543Smrg        return;
125706f2543Smrg    }
126706f2543Smrg
127706f2543Smrg    for (i = 0; i < old_size; i++)
128706f2543Smrg    {
129706f2543Smrg        for (node = old[i]; node != 0; node = next)
130706f2543Smrg        {
131706f2543Smrg            next = node->next;
132706f2543Smrg            item = node->data;
133706f2543Smrg
134706f2543Smrg            b = hash_table_hash_key (h, ITEM_KEY (item)) % new_size;
135706f2543Smrg
136706f2543Smrg            node->next = new[b];
137706f2543Smrg            new[b] = node;
138706f2543Smrg        }
139706f2543Smrg    }
140706f2543Smrg
141706f2543Smrg    h->buckets = new;
142706f2543Smrg    free (old);
143706f2543Smrg}
144706f2543Smrg
145706f2543SmrgX_EXTERN x_hash_table *
146706f2543SmrgX_PFX (hash_table_new) (x_hash_fun *hash,
147706f2543Smrg                        x_compare_fun *compare,
148706f2543Smrg                        x_destroy_fun *key_destroy,
149706f2543Smrg                        x_destroy_fun *value_destroy)
150706f2543Smrg{
151706f2543Smrg    x_hash_table *h;
152706f2543Smrg
153706f2543Smrg    h = calloc (1, sizeof (x_hash_table));
154706f2543Smrg    if (h == 0)
155706f2543Smrg        return 0;
156706f2543Smrg
157706f2543Smrg    h->bucket_index = 0;
158706f2543Smrg    h->buckets = calloc (hash_table_total_buckets (h), sizeof (x_list *));
159706f2543Smrg
160706f2543Smrg    if (h->buckets == 0)
161706f2543Smrg    {
162706f2543Smrg        free (h);
163706f2543Smrg        return 0;
164706f2543Smrg    }
165706f2543Smrg
166706f2543Smrg    h->hash_key = hash;
167706f2543Smrg    h->compare_keys = compare;
168706f2543Smrg    h->destroy_key = key_destroy;
169706f2543Smrg    h->destroy_value = value_destroy;
170706f2543Smrg
171706f2543Smrg    return h;
172706f2543Smrg}
173706f2543Smrg
174706f2543SmrgX_EXTERN void
175706f2543SmrgX_PFX (hash_table_free) (x_hash_table *h)
176706f2543Smrg{
177706f2543Smrg    int n, i;
178706f2543Smrg    x_list *node, *item;
179706f2543Smrg
180706f2543Smrg    assert (h != NULL);
181706f2543Smrg
182706f2543Smrg    n = hash_table_total_buckets (h);
183706f2543Smrg
184706f2543Smrg    for (i = 0; i < n; i++)
185706f2543Smrg    {
186706f2543Smrg        for (node = h->buckets[i]; node != 0; node = node->next)
187706f2543Smrg        {
188706f2543Smrg            item = node->data;
189706f2543Smrg            hash_table_destroy_item (h, ITEM_KEY (item), ITEM_VALUE (item));
190706f2543Smrg            ITEM_FREE (item);
191706f2543Smrg        }
192706f2543Smrg        X_PFX (list_free) (h->buckets[i]);
193706f2543Smrg    }
194706f2543Smrg
195706f2543Smrg    free (h->buckets);
196706f2543Smrg    free (h);
197706f2543Smrg}
198706f2543Smrg
199706f2543SmrgX_EXTERN unsigned int
200706f2543SmrgX_PFX (hash_table_size) (x_hash_table *h)
201706f2543Smrg{
202706f2543Smrg    assert (h != NULL);
203706f2543Smrg
204706f2543Smrg    return h->total_keys;
205706f2543Smrg}
206706f2543Smrg
207706f2543Smrgstatic void
208706f2543Smrghash_table_modify (x_hash_table *h, void *k, void *v, int replace)
209706f2543Smrg{
210706f2543Smrg    size_t hash_value;
211706f2543Smrg    x_list *node, *item;
212706f2543Smrg
213706f2543Smrg    assert (h != NULL);
214706f2543Smrg
215706f2543Smrg    hash_value = hash_table_hash_key (h, k);
216706f2543Smrg
217706f2543Smrg    for (node = h->buckets[hash_value % hash_table_total_buckets (h)];
218706f2543Smrg         node != 0; node = node->next)
219706f2543Smrg    {
220706f2543Smrg        item = node->data;
221706f2543Smrg
222706f2543Smrg        if (hash_table_compare_keys (h, ITEM_KEY (item), k))
223706f2543Smrg        {
224706f2543Smrg            if (replace)
225706f2543Smrg            {
226706f2543Smrg                hash_table_destroy_item (h, ITEM_KEY (item),
227706f2543Smrg                                         ITEM_VALUE (item));
228706f2543Smrg                item->next = k;
229706f2543Smrg                ITEM_VALUE (item) = v;
230706f2543Smrg            }
231706f2543Smrg            else
232706f2543Smrg            {
233706f2543Smrg                hash_table_destroy_item (h, k, ITEM_VALUE (item));
234706f2543Smrg                ITEM_VALUE (item) = v;
235706f2543Smrg            }
236706f2543Smrg            return;
237706f2543Smrg        }
238706f2543Smrg    }
239706f2543Smrg
240706f2543Smrg    /* Key isn't already in the table. Insert it. */
241706f2543Smrg
242706f2543Smrg    if (h->total_keys + 1
243706f2543Smrg        > hash_table_total_buckets (h) * SPLIT_THRESHOLD_FACTOR)
244706f2543Smrg    {
245706f2543Smrg        hash_table_split (h);
246706f2543Smrg    }
247706f2543Smrg
248706f2543Smrg    hash_value = hash_value % hash_table_total_buckets (h);
249706f2543Smrg    h->buckets[hash_value] = X_PFX (list_prepend) (h->buckets[hash_value],
250706f2543Smrg                                                   ITEM_NEW (k, v));
251706f2543Smrg    h->total_keys++;
252706f2543Smrg}
253706f2543Smrg
254706f2543SmrgX_EXTERN void
255706f2543SmrgX_PFX (hash_table_insert) (x_hash_table *h, void *k, void *v)
256706f2543Smrg{
257706f2543Smrg    hash_table_modify (h, k, v, 0);
258706f2543Smrg}
259706f2543Smrg
260706f2543SmrgX_EXTERN void
261706f2543SmrgX_PFX (hash_table_replace) (x_hash_table *h, void *k, void *v)
262706f2543Smrg{
263706f2543Smrg    hash_table_modify (h, k, v, 1);
264706f2543Smrg}
265706f2543Smrg
266706f2543SmrgX_EXTERN void
267706f2543SmrgX_PFX (hash_table_remove) (x_hash_table *h, void *k)
268706f2543Smrg{
269706f2543Smrg    size_t hash_value;
270706f2543Smrg    x_list **ptr, *item;
271706f2543Smrg
272706f2543Smrg    assert (h != NULL);
273706f2543Smrg
274706f2543Smrg    hash_value = hash_table_hash_key (h, k);
275706f2543Smrg
276706f2543Smrg    for (ptr = &h->buckets[hash_value % hash_table_total_buckets (h)];
277706f2543Smrg         *ptr != 0; ptr = &((*ptr)->next))
278706f2543Smrg    {
279706f2543Smrg        item = (*ptr)->data;
280706f2543Smrg
281706f2543Smrg        if (hash_table_compare_keys (h, ITEM_KEY (item), k))
282706f2543Smrg        {
283706f2543Smrg            hash_table_destroy_item (h, ITEM_KEY (item), ITEM_VALUE (item));
284706f2543Smrg            ITEM_FREE (item);
285706f2543Smrg            item = *ptr;
286706f2543Smrg            *ptr = item->next;
287706f2543Smrg            X_PFX (list_free_1) (item);
288706f2543Smrg            h->total_keys--;
289706f2543Smrg            return;
290706f2543Smrg        }
291706f2543Smrg    }
292706f2543Smrg}
293706f2543Smrg
294706f2543SmrgX_EXTERN void *
295706f2543SmrgX_PFX (hash_table_lookup) (x_hash_table *h, void *k, void **k_ret)
296706f2543Smrg{
297706f2543Smrg    size_t hash_value;
298706f2543Smrg    x_list *node, *item;
299706f2543Smrg
300706f2543Smrg    assert (h != NULL);
301706f2543Smrg
302706f2543Smrg    hash_value = hash_table_hash_key (h, k);
303706f2543Smrg
304706f2543Smrg    for (node = h->buckets[hash_value % hash_table_total_buckets (h)];
305706f2543Smrg         node != 0; node = node->next)
306706f2543Smrg    {
307706f2543Smrg        item = node->data;
308706f2543Smrg
309706f2543Smrg        if (hash_table_compare_keys (h, ITEM_KEY (item), k))
310706f2543Smrg        {
311706f2543Smrg            if (k_ret != 0)
312706f2543Smrg            *k_ret = ITEM_KEY (item);
313706f2543Smrg
314706f2543Smrg            return ITEM_VALUE (item);
315706f2543Smrg        }
316706f2543Smrg    }
317706f2543Smrg
318706f2543Smrg    if (k_ret != 0)
319706f2543Smrg        *k_ret = 0;
320706f2543Smrg
321706f2543Smrg    return 0;
322706f2543Smrg}
323706f2543Smrg
324706f2543SmrgX_EXTERN void
325706f2543SmrgX_PFX (hash_table_foreach) (x_hash_table *h,
326706f2543Smrg                            x_hash_foreach_fun *fun, void *data)
327706f2543Smrg{
328706f2543Smrg    int i, n;
329706f2543Smrg    x_list *node, *item;
330706f2543Smrg
331706f2543Smrg    assert (h != NULL);
332706f2543Smrg
333706f2543Smrg    n = hash_table_total_buckets (h);
334706f2543Smrg
335706f2543Smrg    for (i = 0; i < n; i++)
336706f2543Smrg    {
337706f2543Smrg        for (node = h->buckets[i]; node != 0; node = node->next)
338706f2543Smrg        {
339706f2543Smrg            item = node->data;
340706f2543Smrg            (*fun) (ITEM_KEY (item), ITEM_VALUE (item), data);
341706f2543Smrg        }
342706f2543Smrg    }
343706f2543Smrg}
344