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
747cdc0497Smrg#include "libdrm_macros.h"
75e6188e58Smrg#include "xf86drm.h"
76e6188e58Smrg#include "xf86drmHash.h"
7722944501Smrg
7822944501Smrg#define HASH_MAGIC 0xdeadbeef
7922944501Smrg
8022944501Smrgstatic unsigned long HashHash(unsigned long key)
8122944501Smrg{
8222944501Smrg    unsigned long        hash = 0;
8322944501Smrg    unsigned long        tmp  = key;
8422944501Smrg    static int           init = 0;
8522944501Smrg    static unsigned long scatter[256];
8622944501Smrg    int                  i;
8722944501Smrg
8822944501Smrg    if (!init) {
89e6188e58Smrg	void *state;
90e6188e58Smrg	state = drmRandomCreate(37);
91e6188e58Smrg	for (i = 0; i < 256; i++) scatter[i] = drmRandom(state);
92e6188e58Smrg	drmRandomDestroy(state);
9322944501Smrg	++init;
9422944501Smrg    }
9522944501Smrg
9622944501Smrg    while (tmp) {
9722944501Smrg	hash = (hash << 1) + scatter[tmp & 0xff];
9822944501Smrg	tmp >>= 8;
9922944501Smrg    }
10022944501Smrg
10122944501Smrg    hash %= HASH_SIZE;
10222944501Smrg    return hash;
10322944501Smrg}
10422944501Smrg
1057cdc0497Smrgdrm_public void *drmHashCreate(void)
10622944501Smrg{
10722944501Smrg    HashTablePtr table;
10822944501Smrg
109e6188e58Smrg    table           = drmMalloc(sizeof(*table));
11022944501Smrg    if (!table) return NULL;
11122944501Smrg    table->magic    = HASH_MAGIC;
11222944501Smrg
11322944501Smrg    return table;
11422944501Smrg}
11522944501Smrg
1167cdc0497Smrgdrm_public int drmHashDestroy(void *t)
11722944501Smrg{
11822944501Smrg    HashTablePtr  table = (HashTablePtr)t;
11922944501Smrg    HashBucketPtr bucket;
12022944501Smrg    HashBucketPtr next;
12122944501Smrg    int           i;
12222944501Smrg
12322944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
12422944501Smrg
12522944501Smrg    for (i = 0; i < HASH_SIZE; i++) {
12622944501Smrg	for (bucket = table->buckets[i]; bucket;) {
12722944501Smrg	    next = bucket->next;
128e6188e58Smrg	    drmFree(bucket);
12922944501Smrg	    bucket = next;
13022944501Smrg	}
13122944501Smrg    }
132e6188e58Smrg    drmFree(table);
13322944501Smrg    return 0;
13422944501Smrg}
13522944501Smrg
13622944501Smrg/* Find the bucket and organize the list so that this bucket is at the
13722944501Smrg   top. */
13822944501Smrg
13922944501Smrgstatic HashBucketPtr HashFind(HashTablePtr table,
14022944501Smrg			      unsigned long key, unsigned long *h)
14122944501Smrg{
14222944501Smrg    unsigned long hash = HashHash(key);
14322944501Smrg    HashBucketPtr prev = NULL;
14422944501Smrg    HashBucketPtr bucket;
14522944501Smrg
14622944501Smrg    if (h) *h = hash;
14722944501Smrg
14822944501Smrg    for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
14922944501Smrg	if (bucket->key == key) {
15022944501Smrg	    if (prev) {
15122944501Smrg				/* Organize */
15222944501Smrg		prev->next           = bucket->next;
15322944501Smrg		bucket->next         = table->buckets[hash];
15422944501Smrg		table->buckets[hash] = bucket;
15522944501Smrg		++table->partials;
15622944501Smrg	    } else {
15722944501Smrg		++table->hits;
15822944501Smrg	    }
15922944501Smrg	    return bucket;
16022944501Smrg	}
16122944501Smrg	prev = bucket;
16222944501Smrg    }
16322944501Smrg    ++table->misses;
16422944501Smrg    return NULL;
16522944501Smrg}
16622944501Smrg
1677cdc0497Smrgdrm_public int drmHashLookup(void *t, unsigned long key, void **value)
16822944501Smrg{
16922944501Smrg    HashTablePtr  table = (HashTablePtr)t;
17022944501Smrg    HashBucketPtr bucket;
17122944501Smrg
17222944501Smrg    if (!table || table->magic != HASH_MAGIC) return -1; /* Bad magic */
17322944501Smrg
17422944501Smrg    bucket = HashFind(table, key, NULL);
17522944501Smrg    if (!bucket) return 1;	/* Not found */
17622944501Smrg    *value = bucket->value;
17722944501Smrg    return 0;			/* Found */
17822944501Smrg}
17922944501Smrg
1807cdc0497Smrgdrm_public int drmHashInsert(void *t, unsigned long key, void *value)
18122944501Smrg{
18222944501Smrg    HashTablePtr  table = (HashTablePtr)t;
18322944501Smrg    HashBucketPtr bucket;
18422944501Smrg    unsigned long hash;
18522944501Smrg
18622944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
18722944501Smrg
18822944501Smrg    if (HashFind(table, key, &hash)) return 1; /* Already in table */
18922944501Smrg
190e6188e58Smrg    bucket               = drmMalloc(sizeof(*bucket));
19122944501Smrg    if (!bucket) return -1;	/* Error */
19222944501Smrg    bucket->key          = key;
19322944501Smrg    bucket->value        = value;
19422944501Smrg    bucket->next         = table->buckets[hash];
19522944501Smrg    table->buckets[hash] = bucket;
19622944501Smrg    return 0;			/* Added to table */
19722944501Smrg}
19822944501Smrg
1997cdc0497Smrgdrm_public int drmHashDelete(void *t, unsigned long key)
20022944501Smrg{
20122944501Smrg    HashTablePtr  table = (HashTablePtr)t;
20222944501Smrg    unsigned long hash;
20322944501Smrg    HashBucketPtr bucket;
20422944501Smrg
20522944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
20622944501Smrg
20722944501Smrg    bucket = HashFind(table, key, &hash);
20822944501Smrg
20922944501Smrg    if (!bucket) return 1;	/* Not found */
21022944501Smrg
21122944501Smrg    table->buckets[hash] = bucket->next;
212e6188e58Smrg    drmFree(bucket);
21322944501Smrg    return 0;
21422944501Smrg}
21522944501Smrg
2167cdc0497Smrgdrm_public int drmHashNext(void *t, unsigned long *key, void **value)
21722944501Smrg{
21822944501Smrg    HashTablePtr  table = (HashTablePtr)t;
21922944501Smrg
22022944501Smrg    while (table->p0 < HASH_SIZE) {
22122944501Smrg	if (table->p1) {
22222944501Smrg	    *key       = table->p1->key;
22322944501Smrg	    *value     = table->p1->value;
22422944501Smrg	    table->p1  = table->p1->next;
22522944501Smrg	    return 1;
22622944501Smrg	}
22722944501Smrg	table->p1 = table->buckets[table->p0];
22822944501Smrg	++table->p0;
22922944501Smrg    }
23022944501Smrg    return 0;
23122944501Smrg}
23222944501Smrg
2337cdc0497Smrgdrm_public int drmHashFirst(void *t, unsigned long *key, void **value)
23422944501Smrg{
23522944501Smrg    HashTablePtr  table = (HashTablePtr)t;
23622944501Smrg
23722944501Smrg    if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
23822944501Smrg
23922944501Smrg    table->p0 = 0;
24022944501Smrg    table->p1 = table->buckets[0];
24122944501Smrg    return drmHashNext(table, key, value);
24222944501Smrg}
243