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