Merge branch 'modesetting-101' of git+ssh://git.freedesktop.org/git/mesa/drm into...
[profile/ivi/libdrm.git] / libdrm / xf86drmHash.c
1 /* xf86drmHash.c -- Small hash table support for integer -> integer mapping
2  * Created: Sun Apr 18 09:35:45 1999 by faith@precisioninsight.com
3  *
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:
13  *
14  * 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.
17  *
18  * 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.
25  *
26  * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
27  *
28  * DESCRIPTION
29  *
30  * This file contains a straightforward implementation of a fixed-sized
31  * hash table using self-organizing linked lists [Knuth73, pp. 398-399] for
32  * collision resolution.  There are two potentially interesting things
33  * about this implementation:
34  *
35  * 1) The table is power-of-two sized.  Prime sized tables are more
36  * traditional, but do not have a significant advantage over power-of-two
37  * sized table, especially when double hashing is not used for collision
38  * resolution.
39  *
40  * 2) The hash computation uses a table of random integers [Hanson97,
41  * pp. 39-41].
42  *
43  * FUTURE ENHANCEMENTS
44  *
45  * With a table size of 512, the current implementation is sufficient for a
46  * few hundred keys.  Since this is well above the expected size of the
47  * tables for which this implementation was designed, the implementation of
48  * dynamic hash tables was postponed until the need arises.  A common (and
49  * naive) approach to dynamic hash table implementation simply creates a
50  * new hash table when necessary, rehashes all the data into the new table,
51  * and destroys the old table.  The approach in [Larson88] is superior in
52  * two ways: 1) only a portion of the table is expanded when needed,
53  * distributing the expansion cost over several insertions, and 2) portions
54  * of the table can be locked, enabling a scalable thread-safe
55  * implementation.
56  *
57  * REFERENCES
58  *
59  * [Hanson97] David R. Hanson.  C Interfaces and Implementations:
60  * Techniques for Creating Reusable Software.  Reading, Massachusetts:
61  * Addison-Wesley, 1997.
62  *
63  * [Knuth73] Donald E. Knuth. The Art of Computer Programming.  Volume 3:
64  * Sorting and Searching.  Reading, Massachusetts: Addison-Wesley, 1973.
65  *
66  * [Larson88] Per-Ake Larson. "Dynamic Hash Tables".  CACM 31(4), April
67  * 1988, pp. 446-457.
68  *
69  */
70
71 #include <stdio.h>
72 #include <stdlib.h>
73
74 #define HASH_MAIN 0
75
76 #if !HASH_MAIN
77 # include "drm.h"
78 # include "xf86drm.h"
79 #endif
80
81 #define HASH_MAGIC 0xdeadbeef
82 #define HASH_DEBUG 0
83 #define HASH_SIZE  512          /* Good for about 100 entries */
84                                 /* If you change this value, you probably
85                                    have to change the HashHash hashing
86                                    function! */
87
88 #if HASH_MAIN
89 #define HASH_ALLOC malloc
90 #define HASH_FREE  free
91 #define HASH_RANDOM_DECL
92 #define HASH_RANDOM_INIT(seed)  srandom(seed)
93 #define HASH_RANDOM             random()
94 #define HASH_RANDOM_DESTROY
95 #else
96 #define HASH_ALLOC drmMalloc
97 #define HASH_FREE  drmFree
98 #define HASH_RANDOM_DECL        void *state
99 #define HASH_RANDOM_INIT(seed)  state = drmRandomCreate(seed)
100 #define HASH_RANDOM             drmRandom(state)
101 #define HASH_RANDOM_DESTROY     drmRandomDestroy(state)
102
103 #endif
104
105 typedef struct HashBucket {
106     unsigned long     key;
107     void              *value;
108     struct HashBucket *next;
109 } HashBucket, *HashBucketPtr;
110
111 typedef struct HashTable {
112     unsigned long    magic;
113     unsigned long    entries;
114     unsigned long    hits;      /* At top of linked list */
115     unsigned long    partials;  /* Not at top of linked list */
116     unsigned long    misses;    /* Not in table */
117     HashBucketPtr    buckets[HASH_SIZE];
118     int              p0;
119     HashBucketPtr    p1;
120 } HashTable, *HashTablePtr;
121
122 #if HASH_MAIN
123 extern void *drmHashCreate(void);
124 extern int  drmHashDestroy(void *t);
125 extern int  drmHashLookup(void *t, unsigned long key, unsigned long *value);
126 extern int  drmHashInsert(void *t, unsigned long key, unsigned long value);
127 extern int  drmHashDelete(void *t, unsigned long key);
128 #endif
129
130 static unsigned long HashHash(unsigned long key)
131 {
132     unsigned long        hash = 0;
133     unsigned long        tmp  = key;
134     static int           init = 0;
135     static unsigned long scatter[256];
136     int                  i;
137
138     if (!init) {
139         HASH_RANDOM_DECL;
140         HASH_RANDOM_INIT(37);
141         for (i = 0; i < 256; i++) scatter[i] = HASH_RANDOM;
142         HASH_RANDOM_DESTROY;
143         ++init;
144     }
145
146     while (tmp) {
147         hash = (hash << 1) + scatter[tmp & 0xff];
148         tmp >>= 8;
149     }
150
151     hash %= HASH_SIZE;
152 #if HASH_DEBUG
153     printf( "Hash(%d) = %d\n", key, hash);
154 #endif
155     return hash;
156 }
157
158 void *drmHashCreate(void)
159 {
160     HashTablePtr table;
161     int          i;
162
163     table           = HASH_ALLOC(sizeof(*table));
164     if (!table) return NULL;
165     table->magic    = HASH_MAGIC;
166     table->entries  = 0;
167     table->hits     = 0;
168     table->partials = 0;
169     table->misses   = 0;
170
171     for (i = 0; i < HASH_SIZE; i++) table->buckets[i] = NULL;
172     return table;
173 }
174
175 int drmHashDestroy(void *t)
176 {
177     HashTablePtr  table = (HashTablePtr)t;
178     HashBucketPtr bucket;
179     HashBucketPtr next;
180     int           i;
181
182     if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
183
184     for (i = 0; i < HASH_SIZE; i++) {
185         for (bucket = table->buckets[i]; bucket;) {
186             next = bucket->next;
187             HASH_FREE(bucket);
188             bucket = next;
189         }
190     }
191     HASH_FREE(table);
192     return 0;
193 }
194
195 /* Find the bucket and organize the list so that this bucket is at the
196    top. */
197
198 static HashBucketPtr HashFind(HashTablePtr table,
199                               unsigned long key, unsigned long *h)
200 {
201     unsigned long hash = HashHash(key);
202     HashBucketPtr prev = NULL;
203     HashBucketPtr bucket;
204
205     if (h) *h = hash;
206
207     for (bucket = table->buckets[hash]; bucket; bucket = bucket->next) {
208         if (bucket->key == key) {
209             if (prev) {
210                                 /* Organize */
211                 prev->next           = bucket->next;
212                 bucket->next         = table->buckets[hash];
213                 table->buckets[hash] = bucket;
214                 ++table->partials;
215             } else {
216                 ++table->hits;
217             }
218             return bucket;
219         }
220         prev = bucket;
221     }
222     ++table->misses;
223     return NULL;
224 }
225
226 int drmHashLookup(void *t, unsigned long key, void **value)
227 {
228     HashTablePtr  table = (HashTablePtr)t;
229     HashBucketPtr bucket;
230
231     if (!table || table->magic != HASH_MAGIC) return -1; /* Bad magic */
232
233     bucket = HashFind(table, key, NULL);
234     if (!bucket) return 1;      /* Not found */
235     *value = bucket->value;
236     return 0;                   /* Found */
237 }
238
239 int drmHashInsert(void *t, unsigned long key, void *value)
240 {
241     HashTablePtr  table = (HashTablePtr)t;
242     HashBucketPtr bucket;
243     unsigned long hash;
244
245     if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
246
247     if (HashFind(table, key, &hash)) return 1; /* Already in table */
248
249     bucket               = HASH_ALLOC(sizeof(*bucket));
250     if (!bucket) return -1;     /* Error */
251     bucket->key          = key;
252     bucket->value        = value;
253     bucket->next         = table->buckets[hash];
254     table->buckets[hash] = bucket;
255 #if HASH_DEBUG
256     printf("Inserted %d at %d/%p\n", key, hash, bucket);
257 #endif
258     return 0;                   /* Added to table */
259 }
260
261 int drmHashDelete(void *t, unsigned long key)
262 {
263     HashTablePtr  table = (HashTablePtr)t;
264     unsigned long hash;
265     HashBucketPtr bucket;
266
267     if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
268
269     bucket = HashFind(table, key, &hash);
270
271     if (!bucket) return 1;      /* Not found */
272
273     table->buckets[hash] = bucket->next;
274     HASH_FREE(bucket);
275     return 0;
276 }
277
278 int drmHashNext(void *t, unsigned long *key, void **value)
279 {
280     HashTablePtr  table = (HashTablePtr)t;
281
282     while (table->p0 < HASH_SIZE) {
283         if (table->p1) {
284             *key       = table->p1->key;
285             *value     = table->p1->value;
286             table->p1  = table->p1->next;
287             return 1;
288         }
289         table->p1 = table->buckets[table->p0];
290         ++table->p0;
291     }
292     return 0;
293 }
294
295 int drmHashFirst(void *t, unsigned long *key, void **value)
296 {
297     HashTablePtr  table = (HashTablePtr)t;
298
299     if (table->magic != HASH_MAGIC) return -1; /* Bad magic */
300
301     table->p0 = 0;
302     table->p1 = table->buckets[0];
303     return drmHashNext(table, key, value);
304 }
305
306 #if HASH_MAIN
307 #define DIST_LIMIT 10
308 static int dist[DIST_LIMIT];
309
310 static void clear_dist(void) {
311     int i;
312
313     for (i = 0; i < DIST_LIMIT; i++) dist[i] = 0;
314 }
315
316 static int count_entries(HashBucketPtr bucket)
317 {
318     int count = 0;
319
320     for (; bucket; bucket = bucket->next) ++count;
321     return count;
322 }
323
324 static void update_dist(int count)
325 {
326     if (count >= DIST_LIMIT) ++dist[DIST_LIMIT-1];
327     else                     ++dist[count];
328 }
329
330 static void compute_dist(HashTablePtr table)
331 {
332     int           i;
333     HashBucketPtr bucket;
334
335     printf("Entries = %ld, hits = %ld, partials = %ld, misses = %ld\n",
336            table->entries, table->hits, table->partials, table->misses);
337     clear_dist();
338     for (i = 0; i < HASH_SIZE; i++) {
339         bucket = table->buckets[i];
340         update_dist(count_entries(bucket));
341     }
342     for (i = 0; i < DIST_LIMIT; i++) {
343         if (i != DIST_LIMIT-1) printf("%5d %10d\n", i, dist[i]);
344         else                   printf("other %10d\n", dist[i]);
345     }
346 }
347
348 static void check_table(HashTablePtr table,
349                         unsigned long key, unsigned long value)
350 {
351     unsigned long retval  = 0;
352     int           retcode = drmHashLookup(table, key, &retval);
353
354     switch (retcode) {
355     case -1:
356         printf("Bad magic = 0x%08lx:"
357                " key = %lu, expected = %lu, returned = %lu\n",
358                table->magic, key, value, retval);
359         break;
360     case 1:
361         printf("Not found: key = %lu, expected = %lu returned = %lu\n",
362                key, value, retval);
363         break;
364     case 0:
365         if (value != retval)
366             printf("Bad value: key = %lu, expected = %lu, returned = %lu\n",
367                    key, value, retval);
368         break;
369     default:
370         printf("Bad retcode = %d: key = %lu, expected = %lu, returned = %lu\n",
371                retcode, key, value, retval);
372         break;
373     }
374 }
375
376 int main(void)
377 {
378     HashTablePtr table;
379     int          i;
380
381     printf("\n***** 256 consecutive integers ****\n");
382     table = drmHashCreate();
383     for (i = 0; i < 256; i++) drmHashInsert(table, i, i);
384     for (i = 0; i < 256; i++) check_table(table, i, i);
385     for (i = 256; i >= 0; i--) check_table(table, i, i);
386     compute_dist(table);
387     drmHashDestroy(table);
388
389     printf("\n***** 1024 consecutive integers ****\n");
390     table = drmHashCreate();
391     for (i = 0; i < 1024; i++) drmHashInsert(table, i, i);
392     for (i = 0; i < 1024; i++) check_table(table, i, i);
393     for (i = 1024; i >= 0; i--) check_table(table, i, i);
394     compute_dist(table);
395     drmHashDestroy(table);
396
397     printf("\n***** 1024 consecutive page addresses (4k pages) ****\n");
398     table = drmHashCreate();
399     for (i = 0; i < 1024; i++) drmHashInsert(table, i*4096, i);
400     for (i = 0; i < 1024; i++) check_table(table, i*4096, i);
401     for (i = 1024; i >= 0; i--) check_table(table, i*4096, i);
402     compute_dist(table);
403     drmHashDestroy(table);
404
405     printf("\n***** 1024 random integers ****\n");
406     table = drmHashCreate();
407     srandom(0xbeefbeef);
408     for (i = 0; i < 1024; i++) drmHashInsert(table, random(), i);
409     srandom(0xbeefbeef);
410     for (i = 0; i < 1024; i++) check_table(table, random(), i);
411     srandom(0xbeefbeef);
412     for (i = 0; i < 1024; i++) check_table(table, random(), i);
413     compute_dist(table);
414     drmHashDestroy(table);
415
416     printf("\n***** 5000 random integers ****\n");
417     table = drmHashCreate();
418     srandom(0xbeefbeef);
419     for (i = 0; i < 5000; i++) drmHashInsert(table, random(), i);
420     srandom(0xbeefbeef);
421     for (i = 0; i < 5000; i++) check_table(table, random(), i);
422     srandom(0xbeefbeef);
423     for (i = 0; i < 5000; i++) check_table(table, random(), i);
424     compute_dist(table);
425     drmHashDestroy(table);
426
427     return 0;
428 }
429 #endif