blob: 71d3d8951f065dcbd2e00121ca5e19295987bd23 [file] [log] [blame]
Daryll Straussb3a57661999-12-05 01:19:48 +00001/* xf86drmHash.c -- Small hash table support for integer -> integer mapping
2 * Created: Sun Apr 18 09:35:45 1999 by faith@precisioninsight.com
Daryll Straussb3a57661999-12-05 01:19:48 +00003 *
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
Gareth Hughese2b2bff2001-03-13 00:22:05 +000013 *
Daryll Straussb3a57661999-12-05 01:19:48 +000014 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
Gareth Hughese2b2bff2001-03-13 00:22:05 +000017 *
Daryll Straussb3a57661999-12-05 01:19:48 +000018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
Gareth Hughese2b2bff2001-03-13 00:22:05 +000025 *
Rik Faith1c8b2b52000-06-13 14:22:03 +000026 * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
27 *
David Dawes18fc5ee2001-04-09 21:56:31 +000028 * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drmHash.c,v 1.4 2001/03/21 18:08:54 dawes Exp $
Daryll Straussb3a57661999-12-05 01:19:48 +000029 *
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
Adam Jacksonf28dddb2005-11-30 03:51:46 +000073#ifdef HAVE_XORG_CONFIG_H
74#include <xorg-config.h>
75#endif
76
Daryll Straussb3a57661999-12-05 01:19:48 +000077#define HASH_MAIN 0
78
79#if HASH_MAIN
80# include <stdio.h>
81# include <stdlib.h>
82#else
Adam Jackson4b23b5f2005-01-30 03:30:45 +000083# include "drm.h"
Daryll Straussb3a57661999-12-05 01:19:48 +000084# include "xf86drm.h"
85# ifdef XFree86LOADER
86# include "xf86.h"
87# include "xf86_ansic.h"
88# else
89# include <stdio.h>
90# include <stdlib.h>
91# endif
92#endif
93
Daryll Straussb3a57661999-12-05 01:19:48 +000094#define HASH_MAGIC 0xdeadbeef
95#define HASH_DEBUG 0
96#define HASH_SIZE 512 /* Good for about 100 entries */
97 /* If you change this value, you probably
98 have to change the HashHash hashing
99 function! */
100
101#if HASH_MAIN
102#define HASH_ALLOC malloc
103#define HASH_FREE free
104#define HASH_RANDOM_DECL
105#define HASH_RANDOM_INIT(seed) srandom(seed)
106#define HASH_RANDOM random()
Brian Paul0472ac52005-11-28 17:33:01 +0000107#define HASH_RANDOM_DESTROY
Daryll Straussb3a57661999-12-05 01:19:48 +0000108#else
109#define HASH_ALLOC drmMalloc
110#define HASH_FREE drmFree
111#define HASH_RANDOM_DECL void *state
112#define HASH_RANDOM_INIT(seed) state = drmRandomCreate(seed)
113#define HASH_RANDOM drmRandom(state)
Brian Paul0472ac52005-11-28 17:33:01 +0000114#define HASH_RANDOM_DESTROY drmRandomDestroy(state)
Daryll Straussb3a57661999-12-05 01:19:48 +0000115
116#endif
117
118typedef struct HashBucket {
119 unsigned long key;
120 void *value;
121 struct HashBucket *next;
122} HashBucket, *HashBucketPtr;
123
124typedef struct HashTable {
125 unsigned long magic;
126 unsigned long entries;
127 unsigned long hits; /* At top of linked list */
128 unsigned long partials; /* Not at top of linked list */
129 unsigned long misses; /* Not in table */
130 HashBucketPtr buckets[HASH_SIZE];
131 int p0;
132 HashBucketPtr p1;
133} HashTable, *HashTablePtr;
134
135#if HASH_MAIN
Adam Jackson22e41ef2006-02-20 23:09:00 +0000136extern void *drmHashCreate(void);
137extern int drmHashDestroy(void *t);
138extern int drmHashLookup(void *t, unsigned long key, unsigned long *value);
139extern int drmHashInsert(void *t, unsigned long key, unsigned long value);
140extern int drmHashDelete(void *t, unsigned long key);
Daryll Straussb3a57661999-12-05 01:19:48 +0000141#endif
142
143static unsigned long HashHash(unsigned long key)
144{
145 unsigned long hash = 0;
146 unsigned long tmp = key;
147 static int init = 0;
148 static unsigned long scatter[256];
149 int i;
150
151 if (!init) {
152 HASH_RANDOM_DECL;
153 HASH_RANDOM_INIT(37);
154 for (i = 0; i < 256; i++) scatter[i] = HASH_RANDOM;
Brian Paul0472ac52005-11-28 17:33:01 +0000155 HASH_RANDOM_DESTROY;
Daryll Straussb3a57661999-12-05 01:19:48 +0000156 ++init;
157 }
158
159 while (tmp) {
160 hash = (hash << 1) + scatter[tmp & 0xff];
161 tmp >>= 8;
162 }
163
164 hash %= HASH_SIZE;
165#if HASH_DEBUG
166 printf( "Hash(%d) = %d\n", key, hash);
167#endif
168 return hash;
169}
170
Adam Jackson22e41ef2006-02-20 23:09:00 +0000171void *drmHashCreate(void)
Daryll Straussb3a57661999-12-05 01:19:48 +0000172{
173 HashTablePtr table;
174 int i;
175
176 table = HASH_ALLOC(sizeof(*table));
177 if (!table) return NULL;
178 table->magic = HASH_MAGIC;
179 table->entries = 0;
180 table->hits = 0;
181 table->partials = 0;
182 table->misses = 0;
183
184 for (i = 0; i < HASH_SIZE; i++) table->buckets[i] = NULL;
185 return table;
186}
187
Adam Jackson22e41ef2006-02-20 23:09:00 +0000188int drmHashDestroy(void *t)
Daryll Straussb3a57661999-12-05 01:19:48 +0000189{
190 HashTablePtr table = (HashTablePtr)t;
191 HashBucketPtr bucket;
192 HashBucketPtr next;
193 int i;
194
195 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000196
Daryll Straussb3a57661999-12-05 01:19:48 +0000197 for (i = 0; i < HASH_SIZE; i++) {
198 for (bucket = table->buckets[i]; bucket;) {
199 next = bucket->next;
200 HASH_FREE(bucket);
201 bucket = next;
202 }
203 }
204 HASH_FREE(table);
205 return 0;
206}
207
208/* Find the bucket and organize the list so that this bucket is at the
209 top. */
210
211static HashBucketPtr HashFind(HashTablePtr table,
212 unsigned long key, unsigned long *h)
213{
214 unsigned long hash = HashHash(key);
215 HashBucketPtr prev = NULL;
216 HashBucketPtr bucket;
217
218 if (h) *h = hash;
219
220 for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
221 if (bucket->key == key) {
222 if (prev) {
223 /* Organize */
224 prev->next = bucket->next;
225 bucket->next = table->buckets[hash];
226 table->buckets[hash] = bucket;
227 ++table->partials;
228 } else {
229 ++table->hits;
230 }
231 return bucket;
232 }
233 prev = bucket;
234 }
235 ++table->misses;
236 return NULL;
237}
238
Adam Jackson22e41ef2006-02-20 23:09:00 +0000239int drmHashLookup(void *t, unsigned long key, void **value)
Daryll Straussb3a57661999-12-05 01:19:48 +0000240{
241 HashTablePtr table = (HashTablePtr)t;
242 HashBucketPtr bucket;
243
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000244 if (!table || table->magic != HASH_MAGIC) return -1; /* Bad magic */
245
Daryll Straussb3a57661999-12-05 01:19:48 +0000246 bucket = HashFind(table, key, NULL);
247 if (!bucket) return 1; /* Not found */
248 *value = bucket->value;
249 return 0; /* Found */
250}
251
Adam Jackson22e41ef2006-02-20 23:09:00 +0000252int drmHashInsert(void *t, unsigned long key, void *value)
Daryll Straussb3a57661999-12-05 01:19:48 +0000253{
254 HashTablePtr table = (HashTablePtr)t;
255 HashBucketPtr bucket;
256 unsigned long hash;
257
258 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000259
Daryll Straussb3a57661999-12-05 01:19:48 +0000260 if (HashFind(table, key, &hash)) return 1; /* Already in table */
261
262 bucket = HASH_ALLOC(sizeof(*bucket));
263 if (!bucket) return -1; /* Error */
264 bucket->key = key;
265 bucket->value = value;
266 bucket->next = table->buckets[hash];
267 table->buckets[hash] = bucket;
268#if HASH_DEBUG
269 printf("Inserted %d at %d/%p\n", key, hash, bucket);
270#endif
271 return 0; /* Added to table */
272}
273
Adam Jackson22e41ef2006-02-20 23:09:00 +0000274int drmHashDelete(void *t, unsigned long key)
Daryll Straussb3a57661999-12-05 01:19:48 +0000275{
276 HashTablePtr table = (HashTablePtr)t;
277 unsigned long hash;
278 HashBucketPtr bucket;
279
280 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000281
Daryll Straussb3a57661999-12-05 01:19:48 +0000282 bucket = HashFind(table, key, &hash);
283
284 if (!bucket) return 1; /* Not found */
285
286 table->buckets[hash] = bucket->next;
287 HASH_FREE(bucket);
288 return 0;
289}
290
Adam Jackson22e41ef2006-02-20 23:09:00 +0000291int drmHashNext(void *t, unsigned long *key, void **value)
Daryll Straussb3a57661999-12-05 01:19:48 +0000292{
293 HashTablePtr table = (HashTablePtr)t;
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000294
Daryll Straussb3a57661999-12-05 01:19:48 +0000295 for (; table->p0 < HASH_SIZE;
296 ++table->p0, table->p1 = table->buckets[table->p0]) {
297 if (table->p1) {
298 *key = table->p1->key;
299 *value = table->p1->value;
300 table->p1 = table->p1->next;
301 return 1;
302 }
303 }
304 return 0;
305}
306
Adam Jackson22e41ef2006-02-20 23:09:00 +0000307int drmHashFirst(void *t, unsigned long *key, void **value)
Daryll Straussb3a57661999-12-05 01:19:48 +0000308{
309 HashTablePtr table = (HashTablePtr)t;
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000310
Daryll Straussb3a57661999-12-05 01:19:48 +0000311 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
312
313 table->p0 = 0;
314 table->p1 = table->buckets[0];
Adam Jackson22e41ef2006-02-20 23:09:00 +0000315 return drmHashNext(table, key, value);
Daryll Straussb3a57661999-12-05 01:19:48 +0000316}
317
318#if HASH_MAIN
319#define DIST_LIMIT 10
320static int dist[DIST_LIMIT];
321
322static void clear_dist(void) {
323 int i;
324
325 for (i = 0; i < DIST_LIMIT; i++) dist[i] = 0;
326}
327
328static int count_entries(HashBucketPtr bucket)
329{
330 int count = 0;
331
332 for (; bucket; bucket = bucket->next) ++count;
333 return count;
334}
335
336static void update_dist(int count)
337{
338 if (count >= DIST_LIMIT) ++dist[DIST_LIMIT-1];
339 else ++dist[count];
340}
341
342static void compute_dist(HashTablePtr table)
343{
344 int i;
345 HashBucketPtr bucket;
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000346
Daryll Straussb3a57661999-12-05 01:19:48 +0000347 printf("Entries = %ld, hits = %ld, partials = %ld, misses = %ld\n",
348 table->entries, table->hits, table->partials, table->misses);
349 clear_dist();
350 for (i = 0; i < HASH_SIZE; i++) {
351 bucket = table->buckets[i];
352 update_dist(count_entries(bucket));
353 }
354 for (i = 0; i < DIST_LIMIT; i++) {
355 if (i != DIST_LIMIT-1) printf("%5d %10d\n", i, dist[i]);
356 else printf("other %10d\n", dist[i]);
357 }
358}
359
360static void check_table(HashTablePtr table,
361 unsigned long key, unsigned long value)
362{
363 unsigned long retval = 0;
Adam Jackson22e41ef2006-02-20 23:09:00 +0000364 int retcode = drmHashLookup(table, key, &retval);
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000365
Daryll Straussb3a57661999-12-05 01:19:48 +0000366 switch (retcode) {
367 case -1:
368 printf("Bad magic = 0x%08lx:"
369 " key = %lu, expected = %lu, returned = %lu\n",
370 table->magic, key, value, retval);
371 break;
372 case 1:
373 printf("Not found: key = %lu, expected = %lu returned = %lu\n",
374 key, value, retval);
375 break;
376 case 0:
377 if (value != retval)
378 printf("Bad value: key = %lu, expected = %lu, returned = %lu\n",
379 key, value, retval);
380 break;
381 default:
382 printf("Bad retcode = %d: key = %lu, expected = %lu, returned = %lu\n",
383 retcode, key, value, retval);
384 break;
385 }
386}
387
388int main(void)
389{
390 HashTablePtr table;
391 int i;
392
393 printf("\n***** 256 consecutive integers ****\n");
Adam Jackson22e41ef2006-02-20 23:09:00 +0000394 table = drmHashCreate();
395 for (i = 0; i < 256; i++) drmHashInsert(table, i, i);
Daryll Straussb3a57661999-12-05 01:19:48 +0000396 for (i = 0; i < 256; i++) check_table(table, i, i);
397 for (i = 256; i >= 0; i--) check_table(table, i, i);
398 compute_dist(table);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000399 drmHashDestroy(table);
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000400
Daryll Straussb3a57661999-12-05 01:19:48 +0000401 printf("\n***** 1024 consecutive integers ****\n");
Adam Jackson22e41ef2006-02-20 23:09:00 +0000402 table = drmHashCreate();
403 for (i = 0; i < 1024; i++) drmHashInsert(table, i, i);
Daryll Straussb3a57661999-12-05 01:19:48 +0000404 for (i = 0; i < 1024; i++) check_table(table, i, i);
405 for (i = 1024; i >= 0; i--) check_table(table, i, i);
406 compute_dist(table);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000407 drmHashDestroy(table);
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000408
Daryll Straussb3a57661999-12-05 01:19:48 +0000409 printf("\n***** 1024 consecutive page addresses (4k pages) ****\n");
Adam Jackson22e41ef2006-02-20 23:09:00 +0000410 table = drmHashCreate();
411 for (i = 0; i < 1024; i++) drmHashInsert(table, i*4096, i);
Daryll Straussb3a57661999-12-05 01:19:48 +0000412 for (i = 0; i < 1024; i++) check_table(table, i*4096, i);
413 for (i = 1024; i >= 0; i--) check_table(table, i*4096, i);
414 compute_dist(table);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000415 drmHashDestroy(table);
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000416
Daryll Straussb3a57661999-12-05 01:19:48 +0000417 printf("\n***** 1024 random integers ****\n");
Adam Jackson22e41ef2006-02-20 23:09:00 +0000418 table = drmHashCreate();
Daryll Straussb3a57661999-12-05 01:19:48 +0000419 srandom(0xbeefbeef);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000420 for (i = 0; i < 1024; i++) drmHashInsert(table, random(), i);
Daryll Straussb3a57661999-12-05 01:19:48 +0000421 srandom(0xbeefbeef);
422 for (i = 0; i < 1024; i++) check_table(table, random(), i);
423 srandom(0xbeefbeef);
424 for (i = 0; i < 1024; i++) check_table(table, random(), i);
425 compute_dist(table);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000426 drmHashDestroy(table);
Daryll Straussb3a57661999-12-05 01:19:48 +0000427
428 printf("\n***** 5000 random integers ****\n");
Adam Jackson22e41ef2006-02-20 23:09:00 +0000429 table = drmHashCreate();
Daryll Straussb3a57661999-12-05 01:19:48 +0000430 srandom(0xbeefbeef);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000431 for (i = 0; i < 5000; i++) drmHashInsert(table, random(), i);
Daryll Straussb3a57661999-12-05 01:19:48 +0000432 srandom(0xbeefbeef);
433 for (i = 0; i < 5000; i++) check_table(table, random(), i);
434 srandom(0xbeefbeef);
435 for (i = 0; i < 5000; i++) check_table(table, random(), i);
436 compute_dist(table);
Adam Jackson22e41ef2006-02-20 23:09:00 +0000437 drmHashDestroy(table);
Gareth Hughese2b2bff2001-03-13 00:22:05 +0000438
Daryll Straussb3a57661999-12-05 01:19:48 +0000439 return 0;
440}
441#endif