Merge branch 'master' of git+ssh://joukj@git.freedesktop.org/git/mesa/mesa
[mesa.git] / src / glx / x11 / glxhash.c
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@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@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
75 #define HASH_MAIN 0
76
77 #include <stdio.h>
78 #include <stdlib.h>
79
80 #define HASH_MAGIC 0xdeadbeef
81 #define HASH_DEBUG 0
82 #define HASH_SIZE 512 /* Good for about 100 entries */
83 /* If you change this value, you probably
84 have to change the HashHash hashing
85 function! */
86
87 #define HASH_ALLOC malloc
88 #define HASH_FREE free
89 #define HASH_RANDOM_DECL
90 #define HASH_RANDOM_INIT(seed) srandom(seed)
91 #define HASH_RANDOM random()
92 #define HASH_RANDOM_DESTROY
93
94 typedef struct __glxHashBucket {
95 unsigned long key;
96 void *value;
97 struct __glxHashBucket *next;
98 } __glxHashBucket, *__glxHashBucketPtr;
99
100 typedef struct __glxHashTable *__glxHashTablePtr;
101 struct __glxHashTable {
102 unsigned long magic;
103 unsigned long entries;
104 unsigned long hits; /* At top of linked list */
105 unsigned long partials; /* Not at top of linked list */
106 unsigned long misses; /* Not in table */
107 __glxHashBucketPtr buckets[HASH_SIZE];
108 int p0;
109 __glxHashBucketPtr p1;
110 };
111
112 static unsigned long HashHash(unsigned long key)
113 {
114 unsigned long hash = 0;
115 unsigned long tmp = key;
116 static int init = 0;
117 static unsigned long scatter[256];
118 int i;
119
120 if (!init) {
121 HASH_RANDOM_DECL;
122 HASH_RANDOM_INIT(37);
123 for (i = 0; i < 256; i++) scatter[i] = HASH_RANDOM;
124 HASH_RANDOM_DESTROY;
125 ++init;
126 }
127
128 while (tmp) {
129 hash = (hash << 1) + scatter[tmp & 0xff];
130 tmp >>= 8;
131 }
132
133 hash %= HASH_SIZE;
134 #if HASH_DEBUG
135 printf( "Hash(%d) = %d\n", key, hash);
136 #endif
137 return hash;
138 }
139
140 __glxHashTable *__glxHashCreate(void)
141 {
142 __glxHashTablePtr table;
143 int i;
144
145 table = HASH_ALLOC(sizeof(*table));
146 if (!table) return NULL;
147 table->magic = HASH_MAGIC;
148 table->entries = 0;
149 table->hits = 0;
150 table->partials = 0;
151 table->misses = 0;
152
153 for (i = 0; i < HASH_SIZE; i++) table->buckets[i] = NULL;
154 return table;
155 }
156
157 int __glxHashDestroy(__glxHashTable *t)
158 {
159 __glxHashTablePtr table = (__glxHashTablePtr)t;
160 __glxHashBucketPtr bucket;
161 __glxHashBucketPtr next;
162 int i;
163
164 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
165
166 for (i = 0; i < HASH_SIZE; i++) {
167 for (bucket = table->buckets[i]; bucket;) {
168 next = bucket->next;
169 HASH_FREE(bucket);
170 bucket = next;
171 }
172 }
173 HASH_FREE(table);
174 return 0;
175 }
176
177 /* Find the bucket and organize the list so that this bucket is at the
178 top. */
179
180 static __glxHashBucketPtr HashFind(__glxHashTablePtr table,
181 unsigned long key, unsigned long *h)
182 {
183 unsigned long hash = HashHash(key);
184 __glxHashBucketPtr prev = NULL;
185 __glxHashBucketPtr bucket;
186
187 if (h) *h = hash;
188
189 for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
190 if (bucket->key == key) {
191 if (prev) {
192 /* Organize */
193 prev->next = bucket->next;
194 bucket->next = table->buckets[hash];
195 table->buckets[hash] = bucket;
196 ++table->partials;
197 } else {
198 ++table->hits;
199 }
200 return bucket;
201 }
202 prev = bucket;
203 }
204 ++table->misses;
205 return NULL;
206 }
207
208 int __glxHashLookup(__glxHashTable *t, unsigned long key, void **value)
209 {
210 __glxHashTablePtr table = (__glxHashTablePtr)t;
211 __glxHashBucketPtr bucket;
212
213 if (!table || table->magic != HASH_MAGIC) return -1; /* Bad magic */
214
215 bucket = HashFind(table, key, NULL);
216 if (!bucket) return 1; /* Not found */
217 *value = bucket->value;
218 return 0; /* Found */
219 }
220
221 int __glxHashInsert(__glxHashTable *t, unsigned long key, void *value)
222 {
223 __glxHashTablePtr table = (__glxHashTablePtr)t;
224 __glxHashBucketPtr bucket;
225 unsigned long hash;
226
227 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
228
229 if (HashFind(table, key, &hash)) return 1; /* Already in table */
230
231 bucket = HASH_ALLOC(sizeof(*bucket));
232 if (!bucket) return -1; /* Error */
233 bucket->key = key;
234 bucket->value = value;
235 bucket->next = table->buckets[hash];
236 table->buckets[hash] = bucket;
237 #if HASH_DEBUG
238 printf("Inserted %d at %d/%p\n", key, hash, bucket);
239 #endif
240 return 0; /* Added to table */
241 }
242
243 int __glxHashDelete(__glxHashTable *t, unsigned long key)
244 {
245 __glxHashTablePtr table = (__glxHashTablePtr)t;
246 unsigned long hash;
247 __glxHashBucketPtr bucket;
248
249 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
250
251 bucket = HashFind(table, key, &hash);
252
253 if (!bucket) return 1; /* Not found */
254
255 table->buckets[hash] = bucket->next;
256 HASH_FREE(bucket);
257 return 0;
258 }
259
260 int __glxHashNext(__glxHashTable *t, unsigned long *key, void **value)
261 {
262 __glxHashTablePtr table = (__glxHashTablePtr)t;
263
264 while (table->p0 < HASH_SIZE) {
265 if (table->p1) {
266 *key = table->p1->key;
267 *value = table->p1->value;
268 table->p1 = table->p1->next;
269 return 1;
270 }
271 table->p1 = table->buckets[table->p0];
272 ++table->p0;
273 }
274 return 0;
275 }
276
277 int __glxHashFirst(__glxHashTable *t, unsigned long *key, void **value)
278 {
279 __glxHashTablePtr table = (__glxHashTablePtr)t;
280
281 if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
282
283 table->p0 = 0;
284 table->p1 = table->buckets[0];
285 return __glxHashNext(table, key, value);
286 }
287
288 #if HASH_MAIN
289 #define DIST_LIMIT 10
290 static int dist[DIST_LIMIT];
291
292 static void clear_dist(void) {
293 int i;
294
295 for (i = 0; i < DIST_LIMIT; i++) dist[i] = 0;
296 }
297
298 static int count_entries(__glxHashBucketPtr bucket)
299 {
300 int count = 0;
301
302 for (; bucket; bucket = bucket->next) ++count;
303 return count;
304 }
305
306 static void update_dist(int count)
307 {
308 if (count >= DIST_LIMIT) ++dist[DIST_LIMIT-1];
309 else ++dist[count];
310 }
311
312 static void compute_dist(__glxHashTablePtr table)
313 {
314 int i;
315 __glxHashBucketPtr bucket;
316
317 printf("Entries = %ld, hits = %ld, partials = %ld, misses = %ld\n",
318 table->entries, table->hits, table->partials, table->misses);
319 clear_dist();
320 for (i = 0; i < HASH_SIZE; i++) {
321 bucket = table->buckets[i];
322 update_dist(count_entries(bucket));
323 }
324 for (i = 0; i < DIST_LIMIT; i++) {
325 if (i != DIST_LIMIT-1) printf("%5d %10d\n", i, dist[i]);
326 else printf("other %10d\n", dist[i]);
327 }
328 }
329
330 static void check_table(__glxHashTablePtr table,
331 unsigned long key, unsigned long value)
332 {
333 unsigned long retval = 0;
334 int retcode = __glxHashLookup(table, key, &retval);
335
336 switch (retcode) {
337 case -1:
338 printf("Bad magic = 0x%08lx:"
339 " key = %lu, expected = %lu, returned = %lu\n",
340 table->magic, key, value, retval);
341 break;
342 case 1:
343 printf("Not found: key = %lu, expected = %lu returned = %lu\n",
344 key, value, retval);
345 break;
346 case 0:
347 if (value != retval)
348 printf("Bad value: key = %lu, expected = %lu, returned = %lu\n",
349 key, value, retval);
350 break;
351 default:
352 printf("Bad retcode = %d: key = %lu, expected = %lu, returned = %lu\n",
353 retcode, key, value, retval);
354 break;
355 }
356 }
357
358 int main(void)
359 {
360 __glxHashTablePtr table;
361 int i;
362
363 printf("\n***** 256 consecutive integers ****\n");
364 table = __glxHashCreate();
365 for (i = 0; i < 256; i++) __glxHashInsert(table, i, i);
366 for (i = 0; i < 256; i++) check_table(table, i, i);
367 for (i = 256; i >= 0; i--) check_table(table, i, i);
368 compute_dist(table);
369 __glxHashDestroy(table);
370
371 printf("\n***** 1024 consecutive integers ****\n");
372 table = __glxHashCreate();
373 for (i = 0; i < 1024; i++) __glxHashInsert(table, i, i);
374 for (i = 0; i < 1024; i++) check_table(table, i, i);
375 for (i = 1024; i >= 0; i--) check_table(table, i, i);
376 compute_dist(table);
377 __glxHashDestroy(table);
378
379 printf("\n***** 1024 consecutive page addresses (4k pages) ****\n");
380 table = __glxHashCreate();
381 for (i = 0; i < 1024; i++) __glxHashInsert(table, i*4096, i);
382 for (i = 0; i < 1024; i++) check_table(table, i*4096, i);
383 for (i = 1024; i >= 0; i--) check_table(table, i*4096, i);
384 compute_dist(table);
385 __glxHashDestroy(table);
386
387 printf("\n***** 1024 random integers ****\n");
388 table = __glxHashCreate();
389 srandom(0xbeefbeef);
390 for (i = 0; i < 1024; i++) __glxHashInsert(table, random(), i);
391 srandom(0xbeefbeef);
392 for (i = 0; i < 1024; i++) check_table(table, random(), i);
393 srandom(0xbeefbeef);
394 for (i = 0; i < 1024; i++) check_table(table, random(), i);
395 compute_dist(table);
396 __glxHashDestroy(table);
397
398 printf("\n***** 5000 random integers ****\n");
399 table = __glxHashCreate();
400 srandom(0xbeefbeef);
401 for (i = 0; i < 5000; i++) __glxHashInsert(table, random(), i);
402 srandom(0xbeefbeef);
403 for (i = 0; i < 5000; i++) check_table(table, random(), i);
404 srandom(0xbeefbeef);
405 for (i = 0; i < 5000; i++) check_table(table, random(), i);
406 compute_dist(table);
407 __glxHashDestroy(table);
408
409 return 0;
410 }
411 #endif