14642e01fSmrg/* x-hash.c - basic hash tables
235c4bbdfSmrg *
335c4bbdfSmrg * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
435c4bbdfSmrg *
535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person
635c4bbdfSmrg * obtaining a copy of this software and associated documentation files
735c4bbdfSmrg * (the "Software"), to deal in the Software without restriction,
835c4bbdfSmrg * including without limitation the rights to use, copy, modify, merge,
935c4bbdfSmrg * publish, distribute, sublicense, and/or sell copies of the Software,
1035c4bbdfSmrg * and to permit persons to whom the Software is furnished to do so,
1135c4bbdfSmrg * subject to the following conditions:
1235c4bbdfSmrg *
1335c4bbdfSmrg * The above copyright notice and this permission notice shall be
1435c4bbdfSmrg * included in all copies or substantial portions of the Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1735c4bbdfSmrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1835c4bbdfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1935c4bbdfSmrg * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
2035c4bbdfSmrg * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2135c4bbdfSmrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2235c4bbdfSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2335c4bbdfSmrg * DEALINGS IN THE SOFTWARE.
2435c4bbdfSmrg *
2535c4bbdfSmrg * Except as contained in this notice, the name(s) of the above
2635c4bbdfSmrg * copyright holders shall not be used in advertising or otherwise to
2735c4bbdfSmrg * promote the sale, use or other dealings in this Software without
2835c4bbdfSmrg * prior written authorization.
2935c4bbdfSmrg */
304642e01fSmrg
314642e01fSmrg#ifdef HAVE_DIX_CONFIG_H
324642e01fSmrg#include <dix-config.h>
334642e01fSmrg#endif
344642e01fSmrg
354642e01fSmrg#include "x-hash.h"
364642e01fSmrg#include "x-list.h"
374642e01fSmrg#include <stdlib.h>
384642e01fSmrg#include <assert.h>
394642e01fSmrg
401b5d61b8Smrg#define ARRAY_SIZE(a)  (sizeof((a)) / sizeof((a)[0]))
411b5d61b8Smrg
424642e01fSmrgstruct x_hash_table_struct {
434642e01fSmrg    unsigned int bucket_index;
444642e01fSmrg    unsigned int total_keys;
454642e01fSmrg    x_list **buckets;
464642e01fSmrg
474642e01fSmrg    x_hash_fun *hash_key;
484642e01fSmrg    x_compare_fun *compare_keys;
494642e01fSmrg    x_destroy_fun *destroy_key;
504642e01fSmrg    x_destroy_fun *destroy_value;
514642e01fSmrg};
524642e01fSmrg
5335c4bbdfSmrg#define ITEM_NEW(k, v) X_PFX(list_prepend) ((x_list *)(k), v)
5435c4bbdfSmrg#define ITEM_FREE(i)   X_PFX(list_free_1) (i)
5535c4bbdfSmrg#define ITEM_KEY(i)    ((void *)(i)->next)
5635c4bbdfSmrg#define ITEM_VALUE(i)  ((i)->data)
574642e01fSmrg
584642e01fSmrg#define SPLIT_THRESHOLD_FACTOR 2
594642e01fSmrg
604642e01fSmrg/* http://planetmath.org/?op=getobj&from=objects&name=GoodHashTablePrimes */
614642e01fSmrgstatic const unsigned int bucket_sizes[] = {
6235c4bbdfSmrg    29,       53,        97,        193,        389,        769,       1543,
6335c4bbdfSmrg    3079,     6151, 12289, 24593, 49157,
6435c4bbdfSmrg    98317,    196613,   393241,    786433,    1572869,   3145739,   6291469,
6535c4bbdfSmrg    12582917,
664642e01fSmrg    25165843, 50331653, 100663319, 201326611, 402653189, 805306457,
674642e01fSmrg    1610612741
684642e01fSmrg};
694642e01fSmrg
704642e01fSmrgstatic inline unsigned int
7135c4bbdfSmrghash_table_total_buckets(x_hash_table *h)
724642e01fSmrg{
734642e01fSmrg    return bucket_sizes[h->bucket_index];
744642e01fSmrg}
754642e01fSmrg
764642e01fSmrgstatic inline void
7735c4bbdfSmrghash_table_destroy_item(x_hash_table *h, void *k, void *v)
784642e01fSmrg{
794642e01fSmrg    if (h->destroy_key != 0)
8035c4bbdfSmrg        (*h->destroy_key)(k);
814642e01fSmrg
824642e01fSmrg    if (h->destroy_value != 0)
8335c4bbdfSmrg        (*h->destroy_value)(v);
844642e01fSmrg}
854642e01fSmrg
864642e01fSmrgstatic inline size_t
8735c4bbdfSmrghash_table_hash_key(x_hash_table *h, void *k)
884642e01fSmrg{
894642e01fSmrg    if (h->hash_key != 0)
9035c4bbdfSmrg        return (*h->hash_key)(k);
914642e01fSmrg    else
9235c4bbdfSmrg        return (size_t)k;
934642e01fSmrg}
944642e01fSmrg
954642e01fSmrgstatic inline int
9635c4bbdfSmrghash_table_compare_keys(x_hash_table *h, void *k1, void *k2)
974642e01fSmrg{
984642e01fSmrg    if (h->compare_keys == 0)
994642e01fSmrg        return k1 == k2;
1004642e01fSmrg    else
10135c4bbdfSmrg        return (*h->compare_keys)(k1, k2) == 0;
1024642e01fSmrg}
1034642e01fSmrg
1044642e01fSmrgstatic void
10535c4bbdfSmrghash_table_split(x_hash_table *h)
1064642e01fSmrg{
1074642e01fSmrg    x_list **new, **old;
1084642e01fSmrg    x_list *node, *item, *next;
1094642e01fSmrg    int new_size, old_size;
1104642e01fSmrg    size_t b;
1114642e01fSmrg    int i;
1124642e01fSmrg
1131b5d61b8Smrg    if (h->bucket_index == ARRAY_SIZE(bucket_sizes) - 1)
1144642e01fSmrg        return;
1154642e01fSmrg
11635c4bbdfSmrg    old_size = hash_table_total_buckets(h);
1174642e01fSmrg    old = h->buckets;
1184642e01fSmrg
1194642e01fSmrg    h->bucket_index++;
1204642e01fSmrg
12135c4bbdfSmrg    new_size = hash_table_total_buckets(h);
12235c4bbdfSmrg    new = calloc(new_size, sizeof(x_list *));
1234642e01fSmrg
12435c4bbdfSmrg    if (new == 0) {
1254642e01fSmrg        h->bucket_index--;
1264642e01fSmrg        return;
1274642e01fSmrg    }
1284642e01fSmrg
12935c4bbdfSmrg    for (i = 0; i < old_size; i++) {
13035c4bbdfSmrg        for (node = old[i]; node != 0; node = next) {
1314642e01fSmrg            next = node->next;
1324642e01fSmrg            item = node->data;
1334642e01fSmrg
13435c4bbdfSmrg            b = hash_table_hash_key(h, ITEM_KEY(item)) % new_size;
1354642e01fSmrg
1364642e01fSmrg            node->next = new[b];
1374642e01fSmrg            new[b] = node;
1384642e01fSmrg        }
1394642e01fSmrg    }
1404642e01fSmrg
1414642e01fSmrg    h->buckets = new;
14235c4bbdfSmrg    free(old);
1434642e01fSmrg}
1444642e01fSmrg
1454642e01fSmrgX_EXTERN x_hash_table *
14635c4bbdfSmrgX_PFX(hash_table_new) (x_hash_fun * hash,
14735c4bbdfSmrg                       x_compare_fun * compare,
14835c4bbdfSmrg                       x_destroy_fun * key_destroy,
14935c4bbdfSmrg                       x_destroy_fun * value_destroy) {
1504642e01fSmrg    x_hash_table *h;
1514642e01fSmrg
15235c4bbdfSmrg    h = calloc(1, sizeof(x_hash_table));
1534642e01fSmrg    if (h == 0)
1544642e01fSmrg        return 0;
1554642e01fSmrg
1564642e01fSmrg    h->bucket_index = 0;
15735c4bbdfSmrg    h->buckets = calloc(hash_table_total_buckets(h), sizeof(x_list *));
1584642e01fSmrg
15935c4bbdfSmrg    if (h->buckets == 0) {
16035c4bbdfSmrg        free(h);
1614642e01fSmrg        return 0;
1624642e01fSmrg    }
16335c4bbdfSmrg
1644642e01fSmrg    h->hash_key = hash;
1654642e01fSmrg    h->compare_keys = compare;
1664642e01fSmrg    h->destroy_key = key_destroy;
1674642e01fSmrg    h->destroy_value = value_destroy;
1684642e01fSmrg
1694642e01fSmrg    return h;
1704642e01fSmrg}
1714642e01fSmrg
1724642e01fSmrgX_EXTERN void
17335c4bbdfSmrgX_PFX(hash_table_free) (x_hash_table * h) {
1744642e01fSmrg    int n, i;
1754642e01fSmrg    x_list *node, *item;
1764642e01fSmrg
17735c4bbdfSmrg    assert(h != NULL);
1784642e01fSmrg
17935c4bbdfSmrg    n = hash_table_total_buckets(h);
1804642e01fSmrg
18135c4bbdfSmrg    for (i = 0; i < n; i++) {
18235c4bbdfSmrg        for (node = h->buckets[i]; node != 0; node = node->next) {
1834642e01fSmrg            item = node->data;
18435c4bbdfSmrg            hash_table_destroy_item(h, ITEM_KEY(item), ITEM_VALUE(item));
18535c4bbdfSmrg            ITEM_FREE(item);
1864642e01fSmrg        }
18735c4bbdfSmrg        X_PFX(list_free) (h->buckets[i]);
1884642e01fSmrg    }
1894642e01fSmrg
19035c4bbdfSmrg    free(h->buckets);
19135c4bbdfSmrg    free(h);
1924642e01fSmrg}
1934642e01fSmrg
1944642e01fSmrgX_EXTERN unsigned int
19535c4bbdfSmrgX_PFX(hash_table_size) (x_hash_table * h) {
19635c4bbdfSmrg    assert(h != NULL);
1974642e01fSmrg
1984642e01fSmrg    return h->total_keys;
1994642e01fSmrg}
2004642e01fSmrg
2014642e01fSmrgstatic void
20235c4bbdfSmrghash_table_modify(x_hash_table *h, void *k, void *v, int replace)
2034642e01fSmrg{
2044642e01fSmrg    size_t hash_value;
2054642e01fSmrg    x_list *node, *item;
2064642e01fSmrg
20735c4bbdfSmrg    assert(h != NULL);
2084642e01fSmrg
20935c4bbdfSmrg    hash_value = hash_table_hash_key(h, k);
2104642e01fSmrg
21135c4bbdfSmrg    for (node = h->buckets[hash_value % hash_table_total_buckets(h)];
21235c4bbdfSmrg         node != 0; node = node->next) {
2134642e01fSmrg        item = node->data;
2144642e01fSmrg
21535c4bbdfSmrg        if (hash_table_compare_keys(h, ITEM_KEY(item), k)) {
21635c4bbdfSmrg            if (replace) {
21735c4bbdfSmrg                hash_table_destroy_item(h, ITEM_KEY(item),
21835c4bbdfSmrg                                        ITEM_VALUE(item));
2194642e01fSmrg                item->next = k;
22035c4bbdfSmrg                ITEM_VALUE(item) = v;
2214642e01fSmrg            }
22235c4bbdfSmrg            else {
22335c4bbdfSmrg                hash_table_destroy_item(h, k, ITEM_VALUE(item));
22435c4bbdfSmrg                ITEM_VALUE(item) = v;
2254642e01fSmrg            }
2264642e01fSmrg            return;
2274642e01fSmrg        }
2284642e01fSmrg    }
2294642e01fSmrg
2304642e01fSmrg    /* Key isn't already in the table. Insert it. */
2314642e01fSmrg
2324642e01fSmrg    if (h->total_keys + 1
23335c4bbdfSmrg        > hash_table_total_buckets(h) * SPLIT_THRESHOLD_FACTOR) {
23435c4bbdfSmrg        hash_table_split(h);
2354642e01fSmrg    }
2364642e01fSmrg
23735c4bbdfSmrg    hash_value = hash_value % hash_table_total_buckets(h);
23835c4bbdfSmrg    h->buckets[hash_value] = X_PFX(list_prepend) (h->buckets[hash_value],
23935c4bbdfSmrg                                                  ITEM_NEW(k, v));
2404642e01fSmrg    h->total_keys++;
2414642e01fSmrg}
2424642e01fSmrg
2434642e01fSmrgX_EXTERN void
24435c4bbdfSmrgX_PFX(hash_table_insert) (x_hash_table * h, void *k, void *v) {
24535c4bbdfSmrg    hash_table_modify(h, k, v, 0);
2464642e01fSmrg}
2474642e01fSmrg
2484642e01fSmrgX_EXTERN void
24935c4bbdfSmrgX_PFX(hash_table_replace) (x_hash_table * h, void *k, void *v) {
25035c4bbdfSmrg    hash_table_modify(h, k, v, 1);
2514642e01fSmrg}
2524642e01fSmrg
2534642e01fSmrgX_EXTERN void
25435c4bbdfSmrgX_PFX(hash_table_remove) (x_hash_table * h, void *k) {
2554642e01fSmrg    size_t hash_value;
2564642e01fSmrg    x_list **ptr, *item;
2574642e01fSmrg
25835c4bbdfSmrg    assert(h != NULL);
2594642e01fSmrg
26035c4bbdfSmrg    hash_value = hash_table_hash_key(h, k);
2614642e01fSmrg
26235c4bbdfSmrg    for (ptr = &h->buckets[hash_value % hash_table_total_buckets(h)];
26335c4bbdfSmrg         *ptr != 0; ptr = &((*ptr)->next)) {
2644642e01fSmrg        item = (*ptr)->data;
2654642e01fSmrg
26635c4bbdfSmrg        if (hash_table_compare_keys(h, ITEM_KEY(item), k)) {
26735c4bbdfSmrg            hash_table_destroy_item(h, ITEM_KEY(item), ITEM_VALUE(item));
26835c4bbdfSmrg            ITEM_FREE(item);
2694642e01fSmrg            item = *ptr;
2704642e01fSmrg            *ptr = item->next;
27135c4bbdfSmrg            X_PFX(list_free_1) (item);
2724642e01fSmrg            h->total_keys--;
2734642e01fSmrg            return;
2744642e01fSmrg        }
2754642e01fSmrg    }
2764642e01fSmrg}
2774642e01fSmrg
2784642e01fSmrgX_EXTERN void *
27935c4bbdfSmrgX_PFX(hash_table_lookup) (x_hash_table * h, void *k, void **k_ret) {
2804642e01fSmrg    size_t hash_value;
2814642e01fSmrg    x_list *node, *item;
2824642e01fSmrg
28335c4bbdfSmrg    assert(h != NULL);
2844642e01fSmrg
28535c4bbdfSmrg    hash_value = hash_table_hash_key(h, k);
2864642e01fSmrg
28735c4bbdfSmrg    for (node = h->buckets[hash_value % hash_table_total_buckets(h)];
28835c4bbdfSmrg         node != 0; node = node->next) {
2894642e01fSmrg        item = node->data;
2904642e01fSmrg
29135c4bbdfSmrg        if (hash_table_compare_keys(h, ITEM_KEY(item), k)) {
2924642e01fSmrg            if (k_ret != 0)
29335c4bbdfSmrg                *k_ret = ITEM_KEY(item);
2944642e01fSmrg
29535c4bbdfSmrg            return ITEM_VALUE(item);
2964642e01fSmrg        }
2974642e01fSmrg    }
2984642e01fSmrg
2994642e01fSmrg    if (k_ret != 0)
3004642e01fSmrg        *k_ret = 0;
3014642e01fSmrg
3024642e01fSmrg    return 0;
3034642e01fSmrg}
3044642e01fSmrg
3054642e01fSmrgX_EXTERN void
30635c4bbdfSmrgX_PFX(hash_table_foreach) (x_hash_table * h,
30735c4bbdfSmrg                           x_hash_foreach_fun * fun, void *data) {
3084642e01fSmrg    int i, n;
3094642e01fSmrg    x_list *node, *item;
3104642e01fSmrg
31135c4bbdfSmrg    assert(h != NULL);
3124642e01fSmrg
31335c4bbdfSmrg    n = hash_table_total_buckets(h);
3144642e01fSmrg
31535c4bbdfSmrg    for (i = 0; i < n; i++) {
31635c4bbdfSmrg        for (node = h->buckets[i]; node != 0; node = node->next) {
3174642e01fSmrg            item = node->data;
31835c4bbdfSmrg            (*fun)(ITEM_KEY(item), ITEM_VALUE(item), data);
3194642e01fSmrg        }
3204642e01fSmrg    }
3214642e01fSmrg}
322