Home | History | Annotate | Download | only in glx
      1 /* glxhash.c -- Small hash table support for integer -> integer mapping
      2  * Taken from libdrm.
      3  *
      4  * Created: Sun Apr 18 09:35:45 1999 by faith (at) precisioninsight.com
      5  *
      6  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
      7  * All Rights Reserved.
      8  *
      9  * Permission is hereby granted, free of charge, to any person obtaining a
     10  * copy of this software and associated documentation files (the "Software"),
     11  * to deal in the Software without restriction, including without limitation
     12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     13  * and/or sell copies of the Software, and to permit persons to whom the
     14  * Software is furnished to do so, subject to the following conditions:
     15  *
     16  * The above copyright notice and this permission notice (including the next
     17  * paragraph) shall be included in all copies or substantial portions of the
     18  * Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     23  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     26  * DEALINGS IN THE SOFTWARE.
     27  *
     28  * Authors: Rickard E. (Rik) Faith <faith (at) valinux.com>
     29  *
     30  * DESCRIPTION
     31  *
     32  * This file contains a straightforward implementation of a fixed-sized
     33  * hash table using self-organizing linked lists [Knuth73, pp. 398-399] for
     34  * collision resolution.  There are two potentially interesting things
     35  * about this implementation:
     36  *
     37  * 1) The table is power-of-two sized.  Prime sized tables are more
     38  * traditional, but do not have a significant advantage over power-of-two
     39  * sized table, especially when double hashing is not used for collision
     40  * resolution.
     41  *
     42  * 2) The hash computation uses a table of random integers [Hanson97,
     43  * pp. 39-41].
     44  *
     45  * FUTURE ENHANCEMENTS
     46  *
     47  * With a table size of 512, the current implementation is sufficient for a
     48  * few hundred keys.  Since this is well above the expected size of the
     49  * tables for which this implementation was designed, the implementation of
     50  * dynamic hash tables was postponed until the need arises.  A common (and
     51  * naive) approach to dynamic hash table implementation simply creates a
     52  * new hash table when necessary, rehashes all the data into the new table,
     53  * and destroys the old table.  The approach in [Larson88] is superior in
     54  * two ways: 1) only a portion of the table is expanded when needed,
     55  * distributing the expansion cost over several insertions, and 2) portions
     56  * of the table can be locked, enabling a scalable thread-safe
     57  * implementation.
     58  *
     59  * REFERENCES
     60  *
     61  * [Hanson97] David R. Hanson.  C Interfaces and Implementations:
     62  * Techniques for Creating Reusable Software.  Reading, Massachusetts:
     63  * Addison-Wesley, 1997.
     64  *
     65  * [Knuth73] Donald E. Knuth. The Art of Computer Programming.  Volume 3:
     66  * Sorting and Searching.  Reading, Massachusetts: Addison-Wesley, 1973.
     67  *
     68  * [Larson88] Per-Ake Larson. "Dynamic Hash Tables".  CACM 31(4), April
     69  * 1988, pp. 446-457.
     70  *
     71  */
     72 
     73 #include "glxhash.h"
     74 #include <X11/Xfuncproto.h>
     75 
     76 #define HASH_MAIN 0
     77 
     78 #include <stdio.h>
     79 #include <stdlib.h>
     80 #include <string.h>
     81 
     82 #define HASH_MAGIC 0xdeadbeef
     83 #define HASH_DEBUG 0
     84 #define HASH_SIZE  512          /* Good for about 100 entries */
     85                                 /* If you change this value, you probably
     86                                    have to change the HashHash hashing
     87                                    function! */
     88 
     89 #define HASH_ALLOC malloc
     90 #define HASH_FREE  free
     91 #ifndef __GLIBC__
     92 #define HASH_RANDOM_DECL	char *ps, rs[256]
     93 #define HASH_RANDOM_INIT(seed)	ps = initstate(seed, rs, sizeof(rs))
     94 #define HASH_RANDOM		random()
     95 #define HASH_RANDOM_DESTROY	setstate(ps)
     96 #else
     97 #define HASH_RANDOM_DECL	struct random_data rd; int32_t rv; char rs[256]
     98 #define HASH_RANDOM_INIT(seed)					\
     99    do {								\
    100       (void) memset(&rd, 0, sizeof(rd));			\
    101       (void) initstate_r(seed, rs, sizeof(rs), &rd);		\
    102    } while(0)
    103 #define HASH_RANDOM             ((void) random_r(&rd, &rv), rv)
    104 #define HASH_RANDOM_DESTROY
    105 #endif
    106 
    107 typedef struct __glxHashBucket
    108 {
    109    unsigned long key;
    110    void *value;
    111    struct __glxHashBucket *next;
    112 } __glxHashBucket, *__glxHashBucketPtr;
    113 
    114 typedef struct __glxHashTable *__glxHashTablePtr;
    115 struct __glxHashTable
    116 {
    117    unsigned long magic;
    118    unsigned long hits;          /* At top of linked list */
    119    unsigned long partials;      /* Not at top of linked list */
    120    unsigned long misses;        /* Not in table */
    121    __glxHashBucketPtr buckets[HASH_SIZE];
    122    int p0;
    123    __glxHashBucketPtr p1;
    124 };
    125 
    126 static unsigned long
    127 HashHash(unsigned long key)
    128 {
    129    unsigned long hash = 0;
    130    unsigned long tmp = key;
    131    static int init = 0;
    132    static unsigned long scatter[256];
    133    int i;
    134 
    135    if (!init) {
    136       HASH_RANDOM_DECL;
    137       HASH_RANDOM_INIT(37);
    138       for (i = 0; i < 256; i++)
    139          scatter[i] = HASH_RANDOM;
    140       HASH_RANDOM_DESTROY;
    141       ++init;
    142    }
    143 
    144    while (tmp) {
    145       hash = (hash << 1) + scatter[tmp & 0xff];
    146       tmp >>= 8;
    147    }
    148 
    149    hash %= HASH_SIZE;
    150 #if HASH_DEBUG
    151    printf("Hash(%d) = %d\n", key, hash);
    152 #endif
    153    return hash;
    154 }
    155 
    156 _X_HIDDEN __glxHashTable *
    157 __glxHashCreate(void)
    158 {
    159    __glxHashTablePtr table;
    160    int i;
    161 
    162    table = HASH_ALLOC(sizeof(*table));
    163    if (!table)
    164       return NULL;
    165    table->magic = HASH_MAGIC;
    166    table->hits = 0;
    167    table->partials = 0;
    168    table->misses = 0;
    169 
    170    for (i = 0; i < HASH_SIZE; i++)
    171       table->buckets[i] = NULL;
    172    return table;
    173 }
    174 
    175 _X_HIDDEN int
    176 __glxHashDestroy(__glxHashTable * t)
    177 {
    178    __glxHashTablePtr table = (__glxHashTablePtr) t;
    179    __glxHashBucketPtr bucket;
    180    __glxHashBucketPtr next;
    181    int i;
    182 
    183    if (table->magic != HASH_MAGIC)
    184       return -1;                /* Bad magic */
    185 
    186    for (i = 0; i < HASH_SIZE; i++) {
    187       for (bucket = table->buckets[i]; bucket;) {
    188          next = bucket->next;
    189          HASH_FREE(bucket);
    190          bucket = next;
    191       }
    192    }
    193    HASH_FREE(table);
    194    return 0;
    195 }
    196 
    197 /* Find the bucket and organize the list so that this bucket is at the
    198    top. */
    199 
    200 static __glxHashBucketPtr
    201 HashFind(__glxHashTablePtr table, unsigned long key, unsigned long *h)
    202 {
    203    unsigned long hash = HashHash(key);
    204    __glxHashBucketPtr prev = NULL;
    205    __glxHashBucketPtr bucket;
    206 
    207    if (h)
    208       *h = hash;
    209 
    210    for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
    211       if (bucket->key == key) {
    212          if (prev) {
    213             /* Organize */
    214             prev->next = bucket->next;
    215             bucket->next = table->buckets[hash];
    216             table->buckets[hash] = bucket;
    217             ++table->partials;
    218          }
    219          else {
    220             ++table->hits;
    221          }
    222          return bucket;
    223       }
    224       prev = bucket;
    225    }
    226    ++table->misses;
    227    return NULL;
    228 }
    229 
    230 _X_HIDDEN int
    231 __glxHashLookup(__glxHashTable * t, unsigned long key, void **value)
    232 {
    233    __glxHashTablePtr table = (__glxHashTablePtr) t;
    234    __glxHashBucketPtr bucket;
    235 
    236    if (!table || table->magic != HASH_MAGIC)
    237       return -1;                /* Bad magic */
    238 
    239    bucket = HashFind(table, key, NULL);
    240    if (!bucket)
    241       return 1;                 /* Not found */
    242    *value = bucket->value;
    243    return 0;                    /* Found */
    244 }
    245 
    246 _X_HIDDEN int
    247 __glxHashInsert(__glxHashTable * t, unsigned long key, void *value)
    248 {
    249    __glxHashTablePtr table = (__glxHashTablePtr) t;
    250    __glxHashBucketPtr bucket;
    251    unsigned long hash;
    252 
    253    if (table->magic != HASH_MAGIC)
    254       return -1;                /* Bad magic */
    255 
    256    if (HashFind(table, key, &hash))
    257       return 1;                 /* Already in table */
    258 
    259    bucket = HASH_ALLOC(sizeof(*bucket));
    260    if (!bucket)
    261       return -1;                /* Error */
    262    bucket->key = key;
    263    bucket->value = value;
    264    bucket->next = table->buckets[hash];
    265    table->buckets[hash] = bucket;
    266 #if HASH_DEBUG
    267    printf("Inserted %d at %d/%p\n", key, hash, bucket);
    268 #endif
    269    return 0;                    /* Added to table */
    270 }
    271 
    272 _X_HIDDEN int
    273 __glxHashDelete(__glxHashTable * t, unsigned long key)
    274 {
    275    __glxHashTablePtr table = (__glxHashTablePtr) t;
    276    unsigned long hash;
    277    __glxHashBucketPtr bucket;
    278 
    279    if (table->magic != HASH_MAGIC)
    280       return -1;                /* Bad magic */
    281 
    282    bucket = HashFind(table, key, &hash);
    283 
    284    if (!bucket)
    285       return 1;                 /* Not found */
    286 
    287    table->buckets[hash] = bucket->next;
    288    HASH_FREE(bucket);
    289    return 0;
    290 }
    291 
    292 _X_HIDDEN int
    293 __glxHashNext(__glxHashTable * t, unsigned long *key, void **value)
    294 {
    295    __glxHashTablePtr table = (__glxHashTablePtr) t;
    296 
    297    while (table->p0 < HASH_SIZE) {
    298       if (table->p1) {
    299          *key = table->p1->key;
    300          *value = table->p1->value;
    301          table->p1 = table->p1->next;
    302          return 1;
    303       }
    304       table->p1 = table->buckets[table->p0];
    305       ++table->p0;
    306    }
    307    return 0;
    308 }
    309 
    310 _X_HIDDEN int
    311 __glxHashFirst(__glxHashTable * t, unsigned long *key, void **value)
    312 {
    313    __glxHashTablePtr table = (__glxHashTablePtr) t;
    314 
    315    if (table->magic != HASH_MAGIC)
    316       return -1;                /* Bad magic */
    317 
    318    table->p0 = 0;
    319    table->p1 = table->buckets[0];
    320    return __glxHashNext(table, key, value);
    321 }
    322 
    323 #if HASH_MAIN
    324 #define DIST_LIMIT 10
    325 static int dist[DIST_LIMIT];
    326 
    327 static void
    328 clear_dist(void)
    329 {
    330    int i;
    331 
    332    for (i = 0; i < DIST_LIMIT; i++)
    333       dist[i] = 0;
    334 }
    335 
    336 static int
    337 count_entries(__glxHashBucketPtr bucket)
    338 {
    339    int count = 0;
    340 
    341    for (; bucket; bucket = bucket->next)
    342       ++count;
    343    return count;
    344 }
    345 
    346 static void
    347 update_dist(int count)
    348 {
    349    if (count >= DIST_LIMIT)
    350       ++dist[DIST_LIMIT - 1];
    351    else
    352       ++dist[count];
    353 }
    354 
    355 static void
    356 compute_dist(__glxHashTablePtr table)
    357 {
    358    int i;
    359    __glxHashBucketPtr bucket;
    360 
    361    printf("Hits = %ld, partials = %ld, misses = %ld\n",
    362           table->hits, table->partials, table->misses);
    363    clear_dist();
    364    for (i = 0; i < HASH_SIZE; i++) {
    365       bucket = table->buckets[i];
    366       update_dist(count_entries(bucket));
    367    }
    368    for (i = 0; i < DIST_LIMIT; i++) {
    369       if (i != DIST_LIMIT - 1)
    370          printf("%5d %10d\n", i, dist[i]);
    371       else
    372          printf("other %10d\n", dist[i]);
    373    }
    374 }
    375 
    376 static void
    377 check_table(__glxHashTablePtr table, unsigned long key, unsigned long value)
    378 {
    379    unsigned long retval = 0;
    380    int retcode = __glxHashLookup(table, key, &retval);
    381 
    382    switch (retcode) {
    383    case -1:
    384       printf("Bad magic = 0x%08lx:"
    385              " key = %lu, expected = %lu, returned = %lu\n",
    386              table->magic, key, value, retval);
    387       break;
    388    case 1:
    389       printf("Not found: key = %lu, expected = %lu returned = %lu\n",
    390              key, value, retval);
    391       break;
    392    case 0:
    393       if (value != retval)
    394          printf("Bad value: key = %lu, expected = %lu, returned = %lu\n",
    395                 key, value, retval);
    396       break;
    397    default:
    398       printf("Bad retcode = %d: key = %lu, expected = %lu, returned = %lu\n",
    399              retcode, key, value, retval);
    400       break;
    401    }
    402 }
    403 
    404 int
    405 main(void)
    406 {
    407    __glxHashTablePtr table;
    408    int i;
    409 
    410    printf("\n***** 256 consecutive integers ****\n");
    411    table = __glxHashCreate();
    412    for (i = 0; i < 256; i++)
    413       __glxHashInsert(table, i, i);
    414    for (i = 0; i < 256; i++)
    415       check_table(table, i, i);
    416    for (i = 256; i >= 0; i--)
    417       check_table(table, i, i);
    418    compute_dist(table);
    419    __glxHashDestroy(table);
    420 
    421    printf("\n***** 1024 consecutive integers ****\n");
    422    table = __glxHashCreate();
    423    for (i = 0; i < 1024; i++)
    424       __glxHashInsert(table, i, i);
    425    for (i = 0; i < 1024; i++)
    426       check_table(table, i, i);
    427    for (i = 1024; i >= 0; i--)
    428       check_table(table, i, i);
    429    compute_dist(table);
    430    __glxHashDestroy(table);
    431 
    432    printf("\n***** 1024 consecutive page addresses (4k pages) ****\n");
    433    table = __glxHashCreate();
    434    for (i = 0; i < 1024; i++)
    435       __glxHashInsert(table, i * 4096, i);
    436    for (i = 0; i < 1024; i++)
    437       check_table(table, i * 4096, i);
    438    for (i = 1024; i >= 0; i--)
    439       check_table(table, i * 4096, i);
    440    compute_dist(table);
    441    __glxHashDestroy(table);
    442 
    443    printf("\n***** 1024 random integers ****\n");
    444    table = __glxHashCreate();
    445    srandom(0xbeefbeef);
    446    for (i = 0; i < 1024; i++)
    447       __glxHashInsert(table, random(), i);
    448    srandom(0xbeefbeef);
    449    for (i = 0; i < 1024; i++)
    450       check_table(table, random(), i);
    451    srandom(0xbeefbeef);
    452    for (i = 0; i < 1024; i++)
    453       check_table(table, random(), i);
    454    compute_dist(table);
    455    __glxHashDestroy(table);
    456 
    457    printf("\n***** 5000 random integers ****\n");
    458    table = __glxHashCreate();
    459    srandom(0xbeefbeef);
    460    for (i = 0; i < 5000; i++)
    461       __glxHashInsert(table, random(), i);
    462    srandom(0xbeefbeef);
    463    for (i = 0; i < 5000; i++)
    464       check_table(table, random(), i);
    465    srandom(0xbeefbeef);
    466    for (i = 0; i < 5000; i++)
    467       check_table(table, random(), i);
    468    compute_dist(table);
    469    __glxHashDestroy(table);
    470 
    471    return 0;
    472 }
    473 #endif
    474