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