1 /**
2  * \file hash.c
3  * Generic hash table.
4  *
5  * Used for display lists, texture objects, vertex/fragment programs,
6  * buffer objects, etc.  The hash functions are thread-safe.
7  *
8  * \note key=0 is illegal.
9  *
10  * \author Brian Paul
11  */
12 
13 /*
14  * Mesa 3-D graphics library
15  * Version:  6.5.1
16  *
17  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
18  *
19  * Permission is hereby granted, free of charge, to any person obtaining a
20  * copy of this software and associated documentation files (the "Software"),
21  * to deal in the Software without restriction, including without limitation
22  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
23  * and/or sell copies of the Software, and to permit persons to whom the
24  * Software is furnished to do so, subject to the following conditions:
25  *
26  * The above copyright notice and this permission notice shall be included
27  * in all copies or substantial portions of the Software.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
32  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
33  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35  */
36 
37 
38 #include "glheader.h"
39 #include "imports.h"
40 #include "glapi/glthread.h"
41 #include "hash.h"
42 
43 
44 #define TABLE_SIZE 1023  /**< Size of lookup table/array */
45 
46 #define HASH_FUNC(K)  ((K) % TABLE_SIZE)
47 
48 
49 /**
50  * An entry in the hash table.
51  */
52 struct HashEntry {
53    GLuint Key;             /**< the entry's key */
54    void *Data;             /**< the entry's data */
55    struct HashEntry *Next; /**< pointer to next entry */
56 };
57 
58 
59 /**
60  * The hash table data structure.
61  */
62 struct _mesa_HashTable {
63    struct HashEntry *Table[TABLE_SIZE];  /**< the lookup table */
64    GLuint MaxKey;                        /**< highest key inserted so far */
65    _glthread_Mutex Mutex;                /**< mutual exclusion lock */
66    _glthread_Mutex WalkMutex;            /**< for _mesa_HashWalk() */
67    GLboolean InDeleteAll;                /**< Debug check */
68 };
69 
70 
71 
72 /**
73  * Create a new hash table.
74  *
75  * \return pointer to a new, empty hash table.
76  */
77 struct _mesa_HashTable *
_mesa_NewHashTable(void)78 _mesa_NewHashTable(void)
79 {
80    struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
81    if (table) {
82       _glthread_INIT_MUTEX(table->Mutex);
83       _glthread_INIT_MUTEX(table->WalkMutex);
84    }
85    return table;
86 }
87 
88 
89 
90 /**
91  * Delete a hash table.
92  * Frees each entry on the hash table and then the hash table structure itself.
93  * Note that the caller should have already traversed the table and deleted
94  * the objects in the table (i.e. We don't free the entries' data pointer).
95  *
96  * \param table the hash table to delete.
97  */
98 void
_mesa_DeleteHashTable(struct _mesa_HashTable * table)99 _mesa_DeleteHashTable(struct _mesa_HashTable *table)
100 {
101    GLuint pos;
102    assert(table);
103    for (pos = 0; pos < TABLE_SIZE; pos++) {
104       struct HashEntry *entry = table->Table[pos];
105       while (entry) {
106 	 struct HashEntry *next = entry->Next;
107          if (entry->Data) {
108             _mesa_problem(NULL,
109                           "In _mesa_DeleteHashTable, found non-freed data");
110          }
111 	 free(entry);
112 	 entry = next;
113       }
114    }
115    _glthread_DESTROY_MUTEX(table->Mutex);
116    _glthread_DESTROY_MUTEX(table->WalkMutex);
117    free(table);
118 }
119 
120 
121 
122 /**
123  * Lookup an entry in the hash table, without locking.
124  * \sa _mesa_HashLookup
125  */
126 static inline void *
_mesa_HashLookup_unlocked(struct _mesa_HashTable * table,GLuint key)127 _mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key)
128 {
129    GLuint pos;
130    const struct HashEntry *entry;
131 
132    assert(table);
133    assert(key);
134 
135    pos = HASH_FUNC(key);
136    entry = table->Table[pos];
137    while (entry) {
138       if (entry->Key == key) {
139          return entry->Data;
140       }
141       entry = entry->Next;
142    }
143    return NULL;
144 }
145 
146 
147 /**
148  * Lookup an entry in the hash table.
149  *
150  * \param table the hash table.
151  * \param key the key.
152  *
153  * \return pointer to user's data or NULL if key not in table
154  */
155 void *
_mesa_HashLookup(struct _mesa_HashTable * table,GLuint key)156 _mesa_HashLookup(struct _mesa_HashTable *table, GLuint key)
157 {
158    void *res;
159    assert(table);
160    _glthread_LOCK_MUTEX(table->Mutex);
161    res = _mesa_HashLookup_unlocked(table, key);
162    _glthread_UNLOCK_MUTEX(table->Mutex);
163    return res;
164 }
165 
166 
167 /**
168  * Insert a key/pointer pair into the hash table.
169  * If an entry with this key already exists we'll replace the existing entry.
170  *
171  * \param table the hash table.
172  * \param key the key (not zero).
173  * \param data pointer to user data.
174  */
175 void
_mesa_HashInsert(struct _mesa_HashTable * table,GLuint key,void * data)176 _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data)
177 {
178    /* search for existing entry with this key */
179    GLuint pos;
180    struct HashEntry *entry;
181 
182    assert(table);
183    assert(key);
184 
185    _glthread_LOCK_MUTEX(table->Mutex);
186 
187    if (key > table->MaxKey)
188       table->MaxKey = key;
189 
190    pos = HASH_FUNC(key);
191 
192    /* check if replacing an existing entry with same key */
193    for (entry = table->Table[pos]; entry; entry = entry->Next) {
194       if (entry->Key == key) {
195          /* replace entry's data */
196 #if 0 /* not sure this check is always valid */
197          if (entry->Data) {
198             _mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert");
199          }
200 #endif
201 	 entry->Data = data;
202          _glthread_UNLOCK_MUTEX(table->Mutex);
203 	 return;
204       }
205    }
206 
207    /* alloc and insert new table entry */
208    entry = MALLOC_STRUCT(HashEntry);
209    if (entry) {
210       entry->Key = key;
211       entry->Data = data;
212       entry->Next = table->Table[pos];
213       table->Table[pos] = entry;
214    }
215 
216    _glthread_UNLOCK_MUTEX(table->Mutex);
217 }
218 
219 
220 
221 /**
222  * Remove an entry from the hash table.
223  *
224  * \param table the hash table.
225  * \param key key of entry to remove.
226  *
227  * While holding the hash table's lock, searches the entry with the matching
228  * key and unlinks it.
229  */
230 void
_mesa_HashRemove(struct _mesa_HashTable * table,GLuint key)231 _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
232 {
233    GLuint pos;
234    struct HashEntry *entry, *prev;
235 
236    assert(table);
237    assert(key);
238 
239    /* have to check this outside of mutex lock */
240    if (table->InDeleteAll) {
241       _mesa_problem(NULL, "_mesa_HashRemove illegally called from "
242                     "_mesa_HashDeleteAll callback function");
243       return;
244    }
245 
246    _glthread_LOCK_MUTEX(table->Mutex);
247 
248    pos = HASH_FUNC(key);
249    prev = NULL;
250    entry = table->Table[pos];
251    while (entry) {
252       if (entry->Key == key) {
253          /* found it! */
254          if (prev) {
255             prev->Next = entry->Next;
256          }
257          else {
258             table->Table[pos] = entry->Next;
259          }
260          free(entry);
261          _glthread_UNLOCK_MUTEX(table->Mutex);
262 	 return;
263       }
264       prev = entry;
265       entry = entry->Next;
266    }
267 
268    _glthread_UNLOCK_MUTEX(table->Mutex);
269 }
270 
271 
272 
273 /**
274  * Delete all entries in a hash table, but don't delete the table itself.
275  * Invoke the given callback function for each table entry.
276  *
277  * \param table  the hash table to delete
278  * \param callback  the callback function
279  * \param userData  arbitrary pointer to pass along to the callback
280  *                  (this is typically a struct gl_context pointer)
281  */
282 void
_mesa_HashDeleteAll(struct _mesa_HashTable * table,void (* callback)(GLuint key,void * data,void * userData),void * userData)283 _mesa_HashDeleteAll(struct _mesa_HashTable *table,
284                     void (*callback)(GLuint key, void *data, void *userData),
285                     void *userData)
286 {
287    GLuint pos;
288    ASSERT(table);
289    ASSERT(callback);
290    _glthread_LOCK_MUTEX(table->Mutex);
291    table->InDeleteAll = GL_TRUE;
292    for (pos = 0; pos < TABLE_SIZE; pos++) {
293       struct HashEntry *entry, *next;
294       for (entry = table->Table[pos]; entry; entry = next) {
295          callback(entry->Key, entry->Data, userData);
296          next = entry->Next;
297          free(entry);
298       }
299       table->Table[pos] = NULL;
300    }
301    table->InDeleteAll = GL_FALSE;
302    _glthread_UNLOCK_MUTEX(table->Mutex);
303 }
304 
305 
306 /**
307  * Walk over all entries in a hash table, calling callback function for each.
308  * Note: we use a separate mutex in this function to avoid a recursive
309  * locking deadlock (in case the callback calls _mesa_HashRemove()) and to
310  * prevent multiple threads/contexts from getting tangled up.
311  * A lock-less version of this function could be used when the table will
312  * not be modified.
313  * \param table  the hash table to walk
314  * \param callback  the callback function
315  * \param userData  arbitrary pointer to pass along to the callback
316  *                  (this is typically a struct gl_context pointer)
317  */
318 void
_mesa_HashWalk(const struct _mesa_HashTable * table,void (* callback)(GLuint key,void * data,void * userData),void * userData)319 _mesa_HashWalk(const struct _mesa_HashTable *table,
320                void (*callback)(GLuint key, void *data, void *userData),
321                void *userData)
322 {
323    /* cast-away const */
324    struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
325    GLuint pos;
326    ASSERT(table);
327    ASSERT(callback);
328    _glthread_LOCK_MUTEX(table2->WalkMutex);
329    for (pos = 0; pos < TABLE_SIZE; pos++) {
330       struct HashEntry *entry, *next;
331       for (entry = table->Table[pos]; entry; entry = next) {
332          /* save 'next' pointer now in case the callback deletes the entry */
333          next = entry->Next;
334          callback(entry->Key, entry->Data, userData);
335       }
336    }
337    _glthread_UNLOCK_MUTEX(table2->WalkMutex);
338 }
339 
340 
341 /**
342  * Return the key of the "first" entry in the hash table.
343  * While holding the lock, walks through all table positions until finding
344  * the first entry of the first non-empty one.
345  *
346  * \param table  the hash table
347  * \return key for the "first" entry in the hash table.
348  */
349 GLuint
_mesa_HashFirstEntry(struct _mesa_HashTable * table)350 _mesa_HashFirstEntry(struct _mesa_HashTable *table)
351 {
352    GLuint pos;
353    assert(table);
354    _glthread_LOCK_MUTEX(table->Mutex);
355    for (pos = 0; pos < TABLE_SIZE; pos++) {
356       if (table->Table[pos]) {
357          _glthread_UNLOCK_MUTEX(table->Mutex);
358          return table->Table[pos]->Key;
359       }
360    }
361    _glthread_UNLOCK_MUTEX(table->Mutex);
362    return 0;
363 }
364 
365 
366 /**
367  * Given a hash table key, return the next key.  This is used to walk
368  * over all entries in the table.  Note that the keys returned during
369  * walking won't be in any particular order.
370  * \return next hash key or 0 if end of table.
371  */
372 GLuint
_mesa_HashNextEntry(const struct _mesa_HashTable * table,GLuint key)373 _mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key)
374 {
375    const struct HashEntry *entry;
376    GLuint pos;
377 
378    assert(table);
379    assert(key);
380 
381    /* Find the entry with given key */
382    pos = HASH_FUNC(key);
383    for (entry = table->Table[pos]; entry ; entry = entry->Next) {
384       if (entry->Key == key) {
385          break;
386       }
387    }
388 
389    if (!entry) {
390       /* the given key was not found, so we can't find the next entry */
391       return 0;
392    }
393 
394    if (entry->Next) {
395       /* return next in linked list */
396       return entry->Next->Key;
397    }
398    else {
399       /* look for next non-empty table slot */
400       pos++;
401       while (pos < TABLE_SIZE) {
402          if (table->Table[pos]) {
403             return table->Table[pos]->Key;
404          }
405          pos++;
406       }
407       return 0;
408    }
409 }
410 
411 
412 /**
413  * Dump contents of hash table for debugging.
414  *
415  * \param table the hash table.
416  */
417 void
_mesa_HashPrint(const struct _mesa_HashTable * table)418 _mesa_HashPrint(const struct _mesa_HashTable *table)
419 {
420    GLuint pos;
421    assert(table);
422    for (pos = 0; pos < TABLE_SIZE; pos++) {
423       const struct HashEntry *entry = table->Table[pos];
424       while (entry) {
425 	 _mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data);
426 	 entry = entry->Next;
427       }
428    }
429 }
430 
431 
432 
433 /**
434  * Find a block of adjacent unused hash keys.
435  *
436  * \param table the hash table.
437  * \param numKeys number of keys needed.
438  *
439  * \return Starting key of free block or 0 if failure.
440  *
441  * If there are enough free keys between the maximum key existing in the table
442  * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return
443  * the adjacent key. Otherwise do a full search for a free key block in the
444  * allowable key range.
445  */
446 GLuint
_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable * table,GLuint numKeys)447 _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
448 {
449    const GLuint maxKey = ~((GLuint) 0);
450    _glthread_LOCK_MUTEX(table->Mutex);
451    if (maxKey - numKeys > table->MaxKey) {
452       /* the quick solution */
453       _glthread_UNLOCK_MUTEX(table->Mutex);
454       return table->MaxKey + 1;
455    }
456    else {
457       /* the slow solution */
458       GLuint freeCount = 0;
459       GLuint freeStart = 1;
460       GLuint key;
461       for (key = 1; key != maxKey; key++) {
462 	 if (_mesa_HashLookup_unlocked(table, key)) {
463 	    /* darn, this key is already in use */
464 	    freeCount = 0;
465 	    freeStart = key+1;
466 	 }
467 	 else {
468 	    /* this key not in use, check if we've found enough */
469 	    freeCount++;
470 	    if (freeCount == numKeys) {
471                _glthread_UNLOCK_MUTEX(table->Mutex);
472 	       return freeStart;
473 	    }
474 	 }
475       }
476       /* cannot allocate a block of numKeys consecutive keys */
477       _glthread_UNLOCK_MUTEX(table->Mutex);
478       return 0;
479    }
480 }
481 
482 
483 /**
484  * Return the number of entries in the hash table.
485  */
486 GLuint
_mesa_HashNumEntries(const struct _mesa_HashTable * table)487 _mesa_HashNumEntries(const struct _mesa_HashTable *table)
488 {
489    GLuint pos, count = 0;
490 
491    for (pos = 0; pos < TABLE_SIZE; pos++) {
492       const struct HashEntry *entry;
493       for (entry = table->Table[pos]; entry; entry = entry->Next) {
494          count++;
495       }
496    }
497 
498    return count;
499 }
500 
501 
502 
503 #if 0 /* debug only */
504 
505 /**
506  * Test walking over all the entries in a hash table.
507  */
508 static void
509 test_hash_walking(void)
510 {
511    struct _mesa_HashTable *t = _mesa_NewHashTable();
512    const GLuint limit = 50000;
513    GLuint i;
514 
515    /* create some entries */
516    for (i = 0; i < limit; i++) {
517       GLuint dummy;
518       GLuint k = (rand() % (limit * 10)) + 1;
519       while (_mesa_HashLookup(t, k)) {
520          /* id already in use, try another */
521          k = (rand() % (limit * 10)) + 1;
522       }
523       _mesa_HashInsert(t, k, &dummy);
524    }
525 
526    /* walk over all entries */
527    {
528       GLuint k = _mesa_HashFirstEntry(t);
529       GLuint count = 0;
530       while (k) {
531          GLuint knext = _mesa_HashNextEntry(t, k);
532          assert(knext != k);
533          _mesa_HashRemove(t, k);
534          count++;
535          k = knext;
536       }
537       assert(count == limit);
538       k = _mesa_HashFirstEntry(t);
539       assert(k==0);
540    }
541 
542    _mesa_DeleteHashTable(t);
543 }
544 
545 
546 void
547 _mesa_test_hash_functions(void)
548 {
549    int a, b, c;
550    struct _mesa_HashTable *t;
551 
552    t = _mesa_NewHashTable();
553    _mesa_HashInsert(t, 501, &a);
554    _mesa_HashInsert(t, 10, &c);
555    _mesa_HashInsert(t, 0xfffffff8, &b);
556    /*_mesa_HashPrint(t);*/
557 
558    assert(_mesa_HashLookup(t,501));
559    assert(!_mesa_HashLookup(t,1313));
560    assert(_mesa_HashFindFreeKeyBlock(t, 100));
561 
562    _mesa_DeleteHashTable(t);
563 
564    test_hash_walking();
565 }
566 
567 #endif
568