xf86drmHash.c revision e6188e58
122944501Smrg/* xf86drmHash.c -- Small hash table support for integer -> integer mapping
222944501Smrg * Created: Sun Apr 18 09:35:45 1999 by faith@precisioninsight.com
322944501Smrg *
422944501Smrg * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
522944501Smrg * All Rights Reserved.
622944501Smrg *
722944501Smrg * Permission is hereby granted, free of charge, to any person obtaining a
822944501Smrg * copy of this software and associated documentation files (the "Software"),
922944501Smrg * to deal in the Software without restriction, including without limitation
1022944501Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1122944501Smrg * and/or sell copies of the Software, and to permit persons to whom the
1222944501Smrg * Software is furnished to do so, subject to the following conditions:
1322944501Smrg *
1422944501Smrg * The above copyright notice and this permission notice (including the next
1522944501Smrg * paragraph) shall be included in all copies or substantial portions of the
1622944501Smrg * Software.
1722944501Smrg *
1822944501Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1922944501Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2022944501Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2122944501Smrg * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2222944501Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2322944501Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2422944501Smrg * DEALINGS IN THE SOFTWARE.
2522944501Smrg *
2622944501Smrg * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
2722944501Smrg *
2822944501Smrg * DESCRIPTION
2922944501Smrg *
3022944501Smrg * This file contains a straightforward implementation of a fixed-sized
3122944501Smrg * hash table using self-organizing linked lists [Knuth73, pp. 398-399] for
3222944501Smrg * collision resolution.  There are two potentially interesting things
3322944501Smrg * about this implementation:
3422944501Smrg *
3522944501Smrg * 1) The table is power-of-two sized.  Prime sized tables are more
3622944501Smrg * traditional, but do not have a significant advantage over power-of-two
3722944501Smrg * sized table, especially when double hashing is not used for collision
3822944501Smrg * resolution.
3922944501Smrg *
4022944501Smrg * 2) The hash computation uses a table of random integers [Hanson97,
4122944501Smrg * pp. 39-41].
4222944501Smrg *
4322944501Smrg * FUTURE ENHANCEMENTS
4422944501Smrg *
4522944501Smrg * With a table size of 512, the current implementation is sufficient for a
4622944501Smrg * few hundred keys.  Since this is well above the expected size of the
4722944501Smrg * tables for which this implementation was designed, the implementation of
4822944501Smrg * dynamic hash tables was postponed until the need arises.  A common (and
4922944501Smrg * naive) approach to dynamic hash table implementation simply creates a
5022944501Smrg * new hash table when necessary, rehashes all the data into the new table,
5122944501Smrg * and destroys the old table.  The approach in [Larson88] is superior in
5222944501Smrg * two ways: 1) only a portion of the table is expanded when needed,
5322944501Smrg * distributing the expansion cost over several insertions, and 2) portions
5422944501Smrg * of the table can be locked, enabling a scalable thread-safe
5522944501Smrg * implementation.
5622944501Smrg *
5722944501Smrg * REFERENCES
5822944501Smrg *
5922944501Smrg * [Hanson97] David R. Hanson.  C Interfaces and Implementations:
6022944501Smrg * Techniques for Creating Reusable Software.  Reading, Massachusetts:
6122944501Smrg * Addison-Wesley, 1997.
6222944501Smrg *
6322944501Smrg * [Knuth73] Donald E. Knuth. The Art of Computer Programming.  Volume 3:
6422944501Smrg * Sorting and Searching.  Reading, Massachusetts: Addison-Wesley, 1973.
6522944501Smrg *
6622944501Smrg * [Larson88] Per-Ake Larson. "Dynamic Hash Tables".  CACM 31(4), April
6722944501Smrg * 1988, pp. 446-457.
6822944501Smrg *
6922944501Smrg */
7022944501Smrg
7122944501Smrg#include <stdio.h>
7222944501Smrg#include <stdlib.h>
7322944501Smrg
74e6188e58Smrg#include "xf86drm.h"
75e6188e58Smrg#include "xf86drmHash.h"
7622944501Smrg
7722944501Smrg#define HASH_MAGIC 0xdeadbeef
7822944501Smrg
7922944501Smrgstatic unsigned long HashHash(unsigned long key)
8022944501Smrg{
8122944501Smrg    unsigned long        hash = 0;
8222944501Smrg    unsigned long        tmp  = key;
8322944501Smrg    static int           init = 0;
8422944501Smrg    static unsigned long scatter[256];
8522944501Smrg    int                  i;
8622944501Smrg
8722944501Smrg    if (!init) {
88e6188e58Smrg	void *state;
89e6188e58Smrg	state = drmRandomCreate(37);
90e6188e58Smrg	for (i = 0; i < 256; i++) scatter[i] = drmRandom(state);
91e6188e58Smrg	drmRandomDestroy(state);
9222944501Smrg	++init;
9322944501Smrg    }
9422944501Smrg
9522944501Smrg    while (tmp) {
9622944501Smrg	hash = (hash << 1) + scatter[tmp & 0xff];
9722944501Smrg	tmp >>= 8;
9822944501Smrg    }
9922944501Smrg
10022944501Smrg    hash %= HASH_SIZE;
101e6188e58Smrg#if DEBUG
102e6188e58Smrg    printf( "Hash(%lu) = %lu\n", key, hash);
10322944501Smrg#endif
10422944501Smrg    return hash;
10522944501Smrg}
10622944501Smrg
10722944501Smrgvoid *drmHashCreate(void)
10822944501Smrg{
10922944501Smrg    HashTablePtr table;
11022944501Smrg    int          i;
11122944501Smrg
112e6188e58Smrg    table           = drmMalloc(sizeof(*table));
11322944501Smrg    if (!table) return NULL;
11422944501Smrg    table->magic    = HASH_MAGIC;
11522944501Smrg    table->entries  = 0;
11622944501Smrg    table->hits     = 0;
11722944501Smrg    table->partials = 0;
11822944501Smrg    table->misses   = 0;
11922944501Smrg
12022944501Smrg    for (i = 0; i < HASH_SIZE; i++) table->buckets[i] = NULL;
12122944501Smrg    return table;
12222944501Smrg}
12322944501Smrg
12422944501Smrgint drmHashDestroy(void *t)
12522944501Smrg{
12622944501Smrg    HashTablePtr  table = (HashTablePtr)t;
12722944501Smrg    HashBucketPtr bucket;
12822944501Smrg    HashBucketPtr next;
12922944501Smrg    int           i;
13022944501Smrg
13122944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
13222944501Smrg
13322944501Smrg    for (i = 0; i < HASH_SIZE; i++) {
13422944501Smrg	for (bucket = table->buckets[i]; bucket;) {
13522944501Smrg	    next = bucket->next;
136e6188e58Smrg	    drmFree(bucket);
13722944501Smrg	    bucket = next;
13822944501Smrg	}
13922944501Smrg    }
140e6188e58Smrg    drmFree(table);
14122944501Smrg    return 0;
14222944501Smrg}
14322944501Smrg
14422944501Smrg/* Find the bucket and organize the list so that this bucket is at the
14522944501Smrg   top. */
14622944501Smrg
14722944501Smrgstatic HashBucketPtr HashFind(HashTablePtr table,
14822944501Smrg			      unsigned long key, unsigned long *h)
14922944501Smrg{
15022944501Smrg    unsigned long hash = HashHash(key);
15122944501Smrg    HashBucketPtr prev = NULL;
15222944501Smrg    HashBucketPtr bucket;
15322944501Smrg
15422944501Smrg    if (h) *h = hash;
15522944501Smrg
15622944501Smrg    for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
15722944501Smrg	if (bucket->key == key) {
15822944501Smrg	    if (prev) {
15922944501Smrg				/* Organize */
16022944501Smrg		prev->next           = bucket->next;
16122944501Smrg		bucket->next         = table->buckets[hash];
16222944501Smrg		table->buckets[hash] = bucket;
16322944501Smrg		++table->partials;
16422944501Smrg	    } else {
16522944501Smrg		++table->hits;
16622944501Smrg	    }
16722944501Smrg	    return bucket;
16822944501Smrg	}
16922944501Smrg	prev = bucket;
17022944501Smrg    }
17122944501Smrg    ++table->misses;
17222944501Smrg    return NULL;
17322944501Smrg}
17422944501Smrg
17522944501Smrgint drmHashLookup(void *t, unsigned long key, void **value)
17622944501Smrg{
17722944501Smrg    HashTablePtr  table = (HashTablePtr)t;
17822944501Smrg    HashBucketPtr bucket;
17922944501Smrg
18022944501Smrg    if (!table || table->magic != HASH_MAGIC) return -1; /* Bad magic */
18122944501Smrg
18222944501Smrg    bucket = HashFind(table, key, NULL);
18322944501Smrg    if (!bucket) return 1;	/* Not found */
18422944501Smrg    *value = bucket->value;
18522944501Smrg    return 0;			/* Found */
18622944501Smrg}
18722944501Smrg
18822944501Smrgint drmHashInsert(void *t, unsigned long key, void *value)
18922944501Smrg{
19022944501Smrg    HashTablePtr  table = (HashTablePtr)t;
19122944501Smrg    HashBucketPtr bucket;
19222944501Smrg    unsigned long hash;
19322944501Smrg
19422944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
19522944501Smrg
19622944501Smrg    if (HashFind(table, key, &hash)) return 1; /* Already in table */
19722944501Smrg
198e6188e58Smrg    bucket               = drmMalloc(sizeof(*bucket));
19922944501Smrg    if (!bucket) return -1;	/* Error */
20022944501Smrg    bucket->key          = key;
20122944501Smrg    bucket->value        = value;
20222944501Smrg    bucket->next         = table->buckets[hash];
20322944501Smrg    table->buckets[hash] = bucket;
204e6188e58Smrg#if DEBUG
205e6188e58Smrg    printf("Inserted %lu at %lu/%p\n", key, hash, bucket);
20622944501Smrg#endif
20722944501Smrg    return 0;			/* Added to table */
20822944501Smrg}
20922944501Smrg
21022944501Smrgint drmHashDelete(void *t, unsigned long key)
21122944501Smrg{
21222944501Smrg    HashTablePtr  table = (HashTablePtr)t;
21322944501Smrg    unsigned long hash;
21422944501Smrg    HashBucketPtr bucket;
21522944501Smrg
21622944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
21722944501Smrg
21822944501Smrg    bucket = HashFind(table, key, &hash);
21922944501Smrg
22022944501Smrg    if (!bucket) return 1;	/* Not found */
22122944501Smrg
22222944501Smrg    table->buckets[hash] = bucket->next;
223e6188e58Smrg    drmFree(bucket);
22422944501Smrg    return 0;
22522944501Smrg}
22622944501Smrg
22722944501Smrgint drmHashNext(void *t, unsigned long *key, void **value)
22822944501Smrg{
22922944501Smrg    HashTablePtr  table = (HashTablePtr)t;
23022944501Smrg
23122944501Smrg    while (table->p0 < HASH_SIZE) {
23222944501Smrg	if (table->p1) {
23322944501Smrg	    *key       = table->p1->key;
23422944501Smrg	    *value     = table->p1->value;
23522944501Smrg	    table->p1  = table->p1->next;
23622944501Smrg	    return 1;
23722944501Smrg	}
23822944501Smrg	table->p1 = table->buckets[table->p0];
23922944501Smrg	++table->p0;
24022944501Smrg    }
24122944501Smrg    return 0;
24222944501Smrg}
24322944501Smrg
24422944501Smrgint drmHashFirst(void *t, unsigned long *key, void **value)
24522944501Smrg{
24622944501Smrg    HashTablePtr  table = (HashTablePtr)t;
24722944501Smrg
24822944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
24922944501Smrg
25022944501Smrg    table->p0 = 0;
25122944501Smrg    table->p1 = table->buckets[0];
25222944501Smrg    return drmHashNext(table, key, value);
25322944501Smrg}
254