1848b8605Smrg/* glxhash.c -- Small hash table support for integer -> integer mapping 2848b8605Smrg * Taken from libdrm. 3848b8605Smrg * 4848b8605Smrg * Created: Sun Apr 18 09:35:45 1999 by faith@precisioninsight.com 5848b8605Smrg * 6848b8605Smrg * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 7848b8605Smrg * All Rights Reserved. 8848b8605Smrg * 9848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a 10848b8605Smrg * copy of this software and associated documentation files (the "Software"), 11848b8605Smrg * to deal in the Software without restriction, including without limitation 12848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the 14848b8605Smrg * Software is furnished to do so, subject to the following conditions: 15848b8605Smrg * 16848b8605Smrg * The above copyright notice and this permission notice (including the next 17848b8605Smrg * paragraph) shall be included in all copies or substantial portions of the 18848b8605Smrg * Software. 19848b8605Smrg * 20848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21848b8605Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23848b8605Smrg * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26848b8605Smrg * DEALINGS IN THE SOFTWARE. 27848b8605Smrg * 28848b8605Smrg * Authors: Rickard E. (Rik) Faith <faith@valinux.com> 29848b8605Smrg * 30848b8605Smrg * DESCRIPTION 31848b8605Smrg * 32848b8605Smrg * This file contains a straightforward implementation of a fixed-sized 33848b8605Smrg * hash table using self-organizing linked lists [Knuth73, pp. 398-399] for 34848b8605Smrg * collision resolution. There are two potentially interesting things 35848b8605Smrg * about this implementation: 36848b8605Smrg * 37848b8605Smrg * 1) The table is power-of-two sized. Prime sized tables are more 38848b8605Smrg * traditional, but do not have a significant advantage over power-of-two 39848b8605Smrg * sized table, especially when double hashing is not used for collision 40848b8605Smrg * resolution. 41848b8605Smrg * 42848b8605Smrg * 2) The hash computation uses a table of random integers [Hanson97, 43848b8605Smrg * pp. 39-41]. 44848b8605Smrg * 45848b8605Smrg * FUTURE ENHANCEMENTS 46848b8605Smrg * 47848b8605Smrg * With a table size of 512, the current implementation is sufficient for a 48848b8605Smrg * few hundred keys. Since this is well above the expected size of the 49848b8605Smrg * tables for which this implementation was designed, the implementation of 50848b8605Smrg * dynamic hash tables was postponed until the need arises. A common (and 51848b8605Smrg * naive) approach to dynamic hash table implementation simply creates a 52848b8605Smrg * new hash table when necessary, rehashes all the data into the new table, 53848b8605Smrg * and destroys the old table. The approach in [Larson88] is superior in 54848b8605Smrg * two ways: 1) only a portion of the table is expanded when needed, 55848b8605Smrg * distributing the expansion cost over several insertions, and 2) portions 56848b8605Smrg * of the table can be locked, enabling a scalable thread-safe 57848b8605Smrg * implementation. 58848b8605Smrg * 59848b8605Smrg * REFERENCES 60848b8605Smrg * 61848b8605Smrg * [Hanson97] David R. Hanson. C Interfaces and Implementations: 62848b8605Smrg * Techniques for Creating Reusable Software. Reading, Massachusetts: 63848b8605Smrg * Addison-Wesley, 1997. 64848b8605Smrg * 65848b8605Smrg * [Knuth73] Donald E. Knuth. The Art of Computer Programming. Volume 3: 66848b8605Smrg * Sorting and Searching. Reading, Massachusetts: Addison-Wesley, 1973. 67848b8605Smrg * 68848b8605Smrg * [Larson88] Per-Ake Larson. "Dynamic Hash Tables". CACM 31(4), April 69848b8605Smrg * 1988, pp. 446-457. 70848b8605Smrg * 71848b8605Smrg */ 72848b8605Smrg 73848b8605Smrg#include "glxhash.h" 74848b8605Smrg#include <X11/Xfuncproto.h> 75848b8605Smrg 76848b8605Smrg#define HASH_MAIN 0 77848b8605Smrg 78848b8605Smrg#include <stdio.h> 79848b8605Smrg#include <stdlib.h> 80848b8605Smrg#include <string.h> 81848b8605Smrg 82848b8605Smrg#define HASH_MAGIC 0xdeadbeef 83848b8605Smrg#define HASH_DEBUG 0 84848b8605Smrg#define HASH_SIZE 512 /* Good for about 100 entries */ 85848b8605Smrg /* If you change this value, you probably 86848b8605Smrg have to change the HashHash hashing 87848b8605Smrg function! */ 88848b8605Smrg 89848b8605Smrg#define HASH_ALLOC malloc 90848b8605Smrg#define HASH_FREE free 91848b8605Smrg#ifndef __GLIBC__ 92848b8605Smrg#define HASH_RANDOM_DECL char *ps, rs[256] 93848b8605Smrg#define HASH_RANDOM_INIT(seed) ps = initstate(seed, rs, sizeof(rs)) 94848b8605Smrg#define HASH_RANDOM random() 95848b8605Smrg#define HASH_RANDOM_DESTROY setstate(ps) 96848b8605Smrg#else 97848b8605Smrg#define HASH_RANDOM_DECL struct random_data rd; int32_t rv; char rs[256] 98848b8605Smrg#define HASH_RANDOM_INIT(seed) \ 99848b8605Smrg do { \ 100848b8605Smrg (void) memset(&rd, 0, sizeof(rd)); \ 101848b8605Smrg (void) initstate_r(seed, rs, sizeof(rs), &rd); \ 102848b8605Smrg } while(0) 103848b8605Smrg#define HASH_RANDOM ((void) random_r(&rd, &rv), rv) 104848b8605Smrg#define HASH_RANDOM_DESTROY 105848b8605Smrg#endif 106848b8605Smrg 107848b8605Smrgtypedef struct __glxHashBucket 108848b8605Smrg{ 109848b8605Smrg unsigned long key; 110848b8605Smrg void *value; 111848b8605Smrg struct __glxHashBucket *next; 112848b8605Smrg} __glxHashBucket, *__glxHashBucketPtr; 113848b8605Smrg 114848b8605Smrgtypedef struct __glxHashTable *__glxHashTablePtr; 115848b8605Smrgstruct __glxHashTable 116848b8605Smrg{ 117848b8605Smrg unsigned long magic; 118848b8605Smrg unsigned long hits; /* At top of linked list */ 119848b8605Smrg unsigned long partials; /* Not at top of linked list */ 120848b8605Smrg unsigned long misses; /* Not in table */ 121848b8605Smrg __glxHashBucketPtr buckets[HASH_SIZE]; 122848b8605Smrg int p0; 123848b8605Smrg __glxHashBucketPtr p1; 124848b8605Smrg}; 125848b8605Smrg 126848b8605Smrgstatic unsigned long 127848b8605SmrgHashHash(unsigned long key) 128848b8605Smrg{ 129848b8605Smrg unsigned long hash = 0; 130848b8605Smrg unsigned long tmp = key; 131848b8605Smrg static int init = 0; 132848b8605Smrg static unsigned long scatter[256]; 133848b8605Smrg int i; 134848b8605Smrg 135848b8605Smrg if (!init) { 136848b8605Smrg HASH_RANDOM_DECL; 137848b8605Smrg HASH_RANDOM_INIT(37); 138848b8605Smrg for (i = 0; i < 256; i++) 139848b8605Smrg scatter[i] = HASH_RANDOM; 140848b8605Smrg HASH_RANDOM_DESTROY; 141848b8605Smrg ++init; 142848b8605Smrg } 143848b8605Smrg 144848b8605Smrg while (tmp) { 145848b8605Smrg hash = (hash << 1) + scatter[tmp & 0xff]; 146848b8605Smrg tmp >>= 8; 147848b8605Smrg } 148848b8605Smrg 149848b8605Smrg hash %= HASH_SIZE; 150848b8605Smrg#if HASH_DEBUG 151848b8605Smrg printf("Hash(%d) = %d\n", key, hash); 152848b8605Smrg#endif 153848b8605Smrg return hash; 154848b8605Smrg} 155848b8605Smrg 156848b8605Smrg_X_HIDDEN __glxHashTable * 157848b8605Smrg__glxHashCreate(void) 158848b8605Smrg{ 159848b8605Smrg __glxHashTablePtr table; 160848b8605Smrg int i; 161848b8605Smrg 162848b8605Smrg table = HASH_ALLOC(sizeof(*table)); 163848b8605Smrg if (!table) 164848b8605Smrg return NULL; 165848b8605Smrg table->magic = HASH_MAGIC; 166848b8605Smrg table->hits = 0; 167848b8605Smrg table->partials = 0; 168848b8605Smrg table->misses = 0; 169848b8605Smrg 170848b8605Smrg for (i = 0; i < HASH_SIZE; i++) 171848b8605Smrg table->buckets[i] = NULL; 172848b8605Smrg return table; 173848b8605Smrg} 174848b8605Smrg 175848b8605Smrg_X_HIDDEN int 176848b8605Smrg__glxHashDestroy(__glxHashTable * t) 177848b8605Smrg{ 178848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 179848b8605Smrg __glxHashBucketPtr bucket; 180848b8605Smrg __glxHashBucketPtr next; 181848b8605Smrg int i; 182848b8605Smrg 183848b8605Smrg if (table->magic != HASH_MAGIC) 184848b8605Smrg return -1; /* Bad magic */ 185848b8605Smrg 186848b8605Smrg for (i = 0; i < HASH_SIZE; i++) { 187848b8605Smrg for (bucket = table->buckets[i]; bucket;) { 188848b8605Smrg next = bucket->next; 189848b8605Smrg HASH_FREE(bucket); 190848b8605Smrg bucket = next; 191848b8605Smrg } 192848b8605Smrg } 193848b8605Smrg HASH_FREE(table); 194848b8605Smrg return 0; 195848b8605Smrg} 196848b8605Smrg 197848b8605Smrg/* Find the bucket and organize the list so that this bucket is at the 198848b8605Smrg top. */ 199848b8605Smrg 200848b8605Smrgstatic __glxHashBucketPtr 201848b8605SmrgHashFind(__glxHashTablePtr table, unsigned long key, unsigned long *h) 202848b8605Smrg{ 203848b8605Smrg unsigned long hash = HashHash(key); 204848b8605Smrg __glxHashBucketPtr prev = NULL; 205848b8605Smrg __glxHashBucketPtr bucket; 206848b8605Smrg 207848b8605Smrg if (h) 208848b8605Smrg *h = hash; 209848b8605Smrg 210848b8605Smrg for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) { 211848b8605Smrg if (bucket->key == key) { 212848b8605Smrg if (prev) { 213848b8605Smrg /* Organize */ 214848b8605Smrg prev->next = bucket->next; 215848b8605Smrg bucket->next = table->buckets[hash]; 216848b8605Smrg table->buckets[hash] = bucket; 217848b8605Smrg ++table->partials; 218848b8605Smrg } 219848b8605Smrg else { 220848b8605Smrg ++table->hits; 221848b8605Smrg } 222848b8605Smrg return bucket; 223848b8605Smrg } 224848b8605Smrg prev = bucket; 225848b8605Smrg } 226848b8605Smrg ++table->misses; 227848b8605Smrg return NULL; 228848b8605Smrg} 229848b8605Smrg 230848b8605Smrg_X_HIDDEN int 231848b8605Smrg__glxHashLookup(__glxHashTable * t, unsigned long key, void **value) 232848b8605Smrg{ 233848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 234848b8605Smrg __glxHashBucketPtr bucket; 235848b8605Smrg 236848b8605Smrg if (!table || table->magic != HASH_MAGIC) 237848b8605Smrg return -1; /* Bad magic */ 238848b8605Smrg 239848b8605Smrg bucket = HashFind(table, key, NULL); 240848b8605Smrg if (!bucket) 241848b8605Smrg return 1; /* Not found */ 242848b8605Smrg *value = bucket->value; 243848b8605Smrg return 0; /* Found */ 244848b8605Smrg} 245848b8605Smrg 246848b8605Smrg_X_HIDDEN int 247848b8605Smrg__glxHashInsert(__glxHashTable * t, unsigned long key, void *value) 248848b8605Smrg{ 249848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 250848b8605Smrg __glxHashBucketPtr bucket; 251848b8605Smrg unsigned long hash; 252848b8605Smrg 253848b8605Smrg if (table->magic != HASH_MAGIC) 254848b8605Smrg return -1; /* Bad magic */ 255848b8605Smrg 256848b8605Smrg if (HashFind(table, key, &hash)) 257848b8605Smrg return 1; /* Already in table */ 258848b8605Smrg 259848b8605Smrg bucket = HASH_ALLOC(sizeof(*bucket)); 260848b8605Smrg if (!bucket) 261848b8605Smrg return -1; /* Error */ 262848b8605Smrg bucket->key = key; 263848b8605Smrg bucket->value = value; 264848b8605Smrg bucket->next = table->buckets[hash]; 265848b8605Smrg table->buckets[hash] = bucket; 266848b8605Smrg#if HASH_DEBUG 267848b8605Smrg printf("Inserted %d at %d/%p\n", key, hash, bucket); 268848b8605Smrg#endif 269848b8605Smrg return 0; /* Added to table */ 270848b8605Smrg} 271848b8605Smrg 272848b8605Smrg_X_HIDDEN int 273848b8605Smrg__glxHashDelete(__glxHashTable * t, unsigned long key) 274848b8605Smrg{ 275848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 276848b8605Smrg unsigned long hash; 277848b8605Smrg __glxHashBucketPtr bucket; 278848b8605Smrg 279848b8605Smrg if (table->magic != HASH_MAGIC) 280848b8605Smrg return -1; /* Bad magic */ 281848b8605Smrg 282848b8605Smrg bucket = HashFind(table, key, &hash); 283848b8605Smrg 284848b8605Smrg if (!bucket) 285848b8605Smrg return 1; /* Not found */ 286848b8605Smrg 287848b8605Smrg table->buckets[hash] = bucket->next; 288848b8605Smrg HASH_FREE(bucket); 289848b8605Smrg return 0; 290848b8605Smrg} 291848b8605Smrg 292848b8605Smrg_X_HIDDEN int 293848b8605Smrg__glxHashNext(__glxHashTable * t, unsigned long *key, void **value) 294848b8605Smrg{ 295848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 296848b8605Smrg 297848b8605Smrg while (table->p0 < HASH_SIZE) { 298848b8605Smrg if (table->p1) { 299848b8605Smrg *key = table->p1->key; 300848b8605Smrg *value = table->p1->value; 301848b8605Smrg table->p1 = table->p1->next; 302848b8605Smrg return 1; 303848b8605Smrg } 304848b8605Smrg table->p1 = table->buckets[table->p0]; 305848b8605Smrg ++table->p0; 306848b8605Smrg } 307848b8605Smrg return 0; 308848b8605Smrg} 309848b8605Smrg 310848b8605Smrg_X_HIDDEN int 311848b8605Smrg__glxHashFirst(__glxHashTable * t, unsigned long *key, void **value) 312848b8605Smrg{ 313848b8605Smrg __glxHashTablePtr table = (__glxHashTablePtr) t; 314848b8605Smrg 315848b8605Smrg if (table->magic != HASH_MAGIC) 316848b8605Smrg return -1; /* Bad magic */ 317848b8605Smrg 318848b8605Smrg table->p0 = 0; 319848b8605Smrg table->p1 = table->buckets[0]; 320848b8605Smrg return __glxHashNext(table, key, value); 321848b8605Smrg} 322848b8605Smrg 323848b8605Smrg#if HASH_MAIN 324848b8605Smrg#define DIST_LIMIT 10 325848b8605Smrgstatic int dist[DIST_LIMIT]; 326848b8605Smrg 327848b8605Smrgstatic void 328848b8605Smrgclear_dist(void) 329848b8605Smrg{ 330848b8605Smrg int i; 331848b8605Smrg 332848b8605Smrg for (i = 0; i < DIST_LIMIT; i++) 333848b8605Smrg dist[i] = 0; 334848b8605Smrg} 335848b8605Smrg 336848b8605Smrgstatic int 337848b8605Smrgcount_entries(__glxHashBucketPtr bucket) 338848b8605Smrg{ 339848b8605Smrg int count = 0; 340848b8605Smrg 341848b8605Smrg for (; bucket; bucket = bucket->next) 342848b8605Smrg ++count; 343848b8605Smrg return count; 344848b8605Smrg} 345848b8605Smrg 346848b8605Smrgstatic void 347848b8605Smrgupdate_dist(int count) 348848b8605Smrg{ 349848b8605Smrg if (count >= DIST_LIMIT) 350848b8605Smrg ++dist[DIST_LIMIT - 1]; 351848b8605Smrg else 352848b8605Smrg ++dist[count]; 353848b8605Smrg} 354848b8605Smrg 355848b8605Smrgstatic void 356848b8605Smrgcompute_dist(__glxHashTablePtr table) 357848b8605Smrg{ 358848b8605Smrg int i; 359848b8605Smrg __glxHashBucketPtr bucket; 360848b8605Smrg 361848b8605Smrg printf("Hits = %ld, partials = %ld, misses = %ld\n", 362848b8605Smrg table->hits, table->partials, table->misses); 363848b8605Smrg clear_dist(); 364848b8605Smrg for (i = 0; i < HASH_SIZE; i++) { 365848b8605Smrg bucket = table->buckets[i]; 366848b8605Smrg update_dist(count_entries(bucket)); 367848b8605Smrg } 368848b8605Smrg for (i = 0; i < DIST_LIMIT; i++) { 369848b8605Smrg if (i != DIST_LIMIT - 1) 370848b8605Smrg printf("%5d %10d\n", i, dist[i]); 371848b8605Smrg else 372848b8605Smrg printf("other %10d\n", dist[i]); 373848b8605Smrg } 374848b8605Smrg} 375848b8605Smrg 376848b8605Smrgstatic void 377848b8605Smrgcheck_table(__glxHashTablePtr table, unsigned long key, unsigned long value) 378848b8605Smrg{ 379848b8605Smrg unsigned long retval = 0; 380848b8605Smrg int retcode = __glxHashLookup(table, key, &retval); 381848b8605Smrg 382848b8605Smrg switch (retcode) { 383848b8605Smrg case -1: 384848b8605Smrg printf("Bad magic = 0x%08lx:" 385848b8605Smrg " key = %lu, expected = %lu, returned = %lu\n", 386848b8605Smrg table->magic, key, value, retval); 387848b8605Smrg break; 388848b8605Smrg case 1: 389848b8605Smrg printf("Not found: key = %lu, expected = %lu returned = %lu\n", 390848b8605Smrg key, value, retval); 391848b8605Smrg break; 392848b8605Smrg case 0: 393848b8605Smrg if (value != retval) 394848b8605Smrg printf("Bad value: key = %lu, expected = %lu, returned = %lu\n", 395848b8605Smrg key, value, retval); 396848b8605Smrg break; 397848b8605Smrg default: 398848b8605Smrg printf("Bad retcode = %d: key = %lu, expected = %lu, returned = %lu\n", 399848b8605Smrg retcode, key, value, retval); 400848b8605Smrg break; 401848b8605Smrg } 402848b8605Smrg} 403848b8605Smrg 404848b8605Smrgint 405848b8605Smrgmain(void) 406848b8605Smrg{ 407848b8605Smrg __glxHashTablePtr table; 408848b8605Smrg int i; 409848b8605Smrg 410848b8605Smrg printf("\n***** 256 consecutive integers ****\n"); 411848b8605Smrg table = __glxHashCreate(); 412848b8605Smrg for (i = 0; i < 256; i++) 413848b8605Smrg __glxHashInsert(table, i, i); 414848b8605Smrg for (i = 0; i < 256; i++) 415848b8605Smrg check_table(table, i, i); 416848b8605Smrg for (i = 256; i >= 0; i--) 417848b8605Smrg check_table(table, i, i); 418848b8605Smrg compute_dist(table); 419848b8605Smrg __glxHashDestroy(table); 420848b8605Smrg 421848b8605Smrg printf("\n***** 1024 consecutive integers ****\n"); 422848b8605Smrg table = __glxHashCreate(); 423848b8605Smrg for (i = 0; i < 1024; i++) 424848b8605Smrg __glxHashInsert(table, i, i); 425848b8605Smrg for (i = 0; i < 1024; i++) 426848b8605Smrg check_table(table, i, i); 427848b8605Smrg for (i = 1024; i >= 0; i--) 428848b8605Smrg check_table(table, i, i); 429848b8605Smrg compute_dist(table); 430848b8605Smrg __glxHashDestroy(table); 431848b8605Smrg 432848b8605Smrg printf("\n***** 1024 consecutive page addresses (4k pages) ****\n"); 433848b8605Smrg table = __glxHashCreate(); 434848b8605Smrg for (i = 0; i < 1024; i++) 435848b8605Smrg __glxHashInsert(table, i * 4096, i); 436848b8605Smrg for (i = 0; i < 1024; i++) 437848b8605Smrg check_table(table, i * 4096, i); 438848b8605Smrg for (i = 1024; i >= 0; i--) 439848b8605Smrg check_table(table, i * 4096, i); 440848b8605Smrg compute_dist(table); 441848b8605Smrg __glxHashDestroy(table); 442848b8605Smrg 443848b8605Smrg printf("\n***** 1024 random integers ****\n"); 444848b8605Smrg table = __glxHashCreate(); 445848b8605Smrg srandom(0xbeefbeef); 446848b8605Smrg for (i = 0; i < 1024; i++) 447848b8605Smrg __glxHashInsert(table, random(), i); 448848b8605Smrg srandom(0xbeefbeef); 449848b8605Smrg for (i = 0; i < 1024; i++) 450848b8605Smrg check_table(table, random(), i); 451848b8605Smrg srandom(0xbeefbeef); 452848b8605Smrg for (i = 0; i < 1024; i++) 453848b8605Smrg check_table(table, random(), i); 454848b8605Smrg compute_dist(table); 455848b8605Smrg __glxHashDestroy(table); 456848b8605Smrg 457848b8605Smrg printf("\n***** 5000 random integers ****\n"); 458848b8605Smrg table = __glxHashCreate(); 459848b8605Smrg srandom(0xbeefbeef); 460848b8605Smrg for (i = 0; i < 5000; i++) 461848b8605Smrg __glxHashInsert(table, random(), i); 462848b8605Smrg srandom(0xbeefbeef); 463848b8605Smrg for (i = 0; i < 5000; i++) 464848b8605Smrg check_table(table, random(), i); 465848b8605Smrg srandom(0xbeefbeef); 466848b8605Smrg for (i = 0; i < 5000; i++) 467848b8605Smrg check_table(table, random(), i); 468848b8605Smrg compute_dist(table); 469848b8605Smrg __glxHashDestroy(table); 470848b8605Smrg 471848b8605Smrg return 0; 472848b8605Smrg} 473848b8605Smrg#endif 474