• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
16  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a
19  * copy of this software and associated documentation files (the "Software"),
20  * to deal in the Software without restriction, including without limitation
21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22  * and/or sell copies of the Software, and to permit persons to whom the
23  * Software is furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be included
26  * in all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34  * OTHER DEALINGS IN THE SOFTWARE.
35  */
36 
37 #include "errors.h"
38 #include "glheader.h"
39 #include "hash.h"
40 #include "util/hash_table.h"
41 #include "util/u_memory.h"
42 #include "util/u_idalloc.h"
43 
44 
45 /**
46  * Create a new hash table.
47  *
48  * \return pointer to a new, empty hash table.
49  */
50 struct _mesa_HashTable *
_mesa_NewHashTable(void)51 _mesa_NewHashTable(void)
52 {
53    struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
54 
55    if (table) {
56       table->ht = _mesa_hash_table_create(NULL, uint_key_hash,
57                                           uint_key_compare);
58       if (table->ht == NULL) {
59          free(table);
60          _mesa_error_no_memory(__func__);
61          return NULL;
62       }
63 
64       _mesa_hash_table_set_deleted_key(table->ht, uint_key(DELETED_KEY_VALUE));
65       simple_mtx_init(&table->Mutex, mtx_plain);
66    }
67    else {
68       _mesa_error_no_memory(__func__);
69    }
70 
71    return table;
72 }
73 
74 
75 
76 /**
77  * Delete a hash table.
78  * Frees each entry on the hash table and then the hash table structure itself.
79  * Note that the caller should have already traversed the table and deleted
80  * the objects in the table (i.e. We don't free the entries' data pointer).
81  *
82  * \param table the hash table to delete.
83  */
84 void
_mesa_DeleteHashTable(struct _mesa_HashTable * table)85 _mesa_DeleteHashTable(struct _mesa_HashTable *table)
86 {
87    assert(table);
88 
89    if (_mesa_hash_table_next_entry(table->ht, NULL) != NULL) {
90       _mesa_problem(NULL, "In _mesa_DeleteHashTable, found non-freed data");
91    }
92 
93    _mesa_hash_table_destroy(table->ht, NULL);
94    if (table->id_alloc) {
95       util_idalloc_fini(table->id_alloc);
96       free(table->id_alloc);
97    }
98 
99    simple_mtx_destroy(&table->Mutex);
100    free(table);
101 }
102 
init_name_reuse(struct _mesa_HashTable * table)103 static void init_name_reuse(struct _mesa_HashTable *table)
104 {
105    assert(_mesa_hash_table_num_entries(table->ht) == 0);
106    table->id_alloc = MALLOC_STRUCT(util_idalloc);
107    util_idalloc_init(table->id_alloc, 8);
108    ASSERTED GLuint reserve0 = util_idalloc_alloc(table->id_alloc);
109    assert (reserve0 == 0);
110 }
111 
112 void
_mesa_HashEnableNameReuse(struct _mesa_HashTable * table)113 _mesa_HashEnableNameReuse(struct _mesa_HashTable *table)
114 {
115    _mesa_HashLockMutex(table);
116    init_name_reuse(table);
117    _mesa_HashUnlockMutex(table);
118 }
119 
120 /**
121  * Lookup an entry in the hash table, without locking.
122  * \sa _mesa_HashLookup
123  */
124 static inline void *
_mesa_HashLookup_unlocked(struct _mesa_HashTable * table,GLuint key)125 _mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key)
126 {
127    const struct hash_entry *entry;
128 
129    assert(table);
130    assert(key);
131 
132    if (key == DELETED_KEY_VALUE)
133       return table->deleted_key_data;
134 
135    entry = _mesa_hash_table_search_pre_hashed(table->ht,
136                                               uint_hash(key),
137                                               uint_key(key));
138    if (!entry)
139       return NULL;
140 
141    return entry->data;
142 }
143 
144 
145 /**
146  * Lookup an entry in the hash table.
147  *
148  * \param table the hash table.
149  * \param key the key.
150  *
151  * \return pointer to user's data or NULL if key not in table
152  */
153 void *
_mesa_HashLookup(struct _mesa_HashTable * table,GLuint key)154 _mesa_HashLookup(struct _mesa_HashTable *table, GLuint key)
155 {
156    void *res;
157    _mesa_HashLockMutex(table);
158    res = _mesa_HashLookup_unlocked(table, key);
159    _mesa_HashUnlockMutex(table);
160    return res;
161 }
162 
163 
164 /**
165  * Lookup an entry in the hash table without locking the mutex.
166  *
167  * The hash table mutex must be locked manually by calling
168  * _mesa_HashLockMutex() before calling this function.
169  *
170  * \param table the hash table.
171  * \param key the key.
172  *
173  * \return pointer to user's data or NULL if key not in table
174  */
175 void *
_mesa_HashLookupLocked(struct _mesa_HashTable * table,GLuint key)176 _mesa_HashLookupLocked(struct _mesa_HashTable *table, GLuint key)
177 {
178    return _mesa_HashLookup_unlocked(table, key);
179 }
180 
181 
182 static inline void
_mesa_HashInsert_unlocked(struct _mesa_HashTable * table,GLuint key,void * data)183 _mesa_HashInsert_unlocked(struct _mesa_HashTable *table, GLuint key, void *data)
184 {
185    uint32_t hash = uint_hash(key);
186    struct hash_entry *entry;
187 
188    assert(table);
189    assert(key);
190 
191    if (key > table->MaxKey)
192       table->MaxKey = key;
193 
194    if (key == DELETED_KEY_VALUE) {
195       table->deleted_key_data = data;
196    } else {
197       entry = _mesa_hash_table_search_pre_hashed(table->ht, hash, uint_key(key));
198       if (entry) {
199          entry->data = data;
200       } else {
201          _mesa_hash_table_insert_pre_hashed(table->ht, hash, uint_key(key), data);
202       }
203    }
204 }
205 
206 
207 /**
208  * Insert a key/pointer pair into the hash table without locking the mutex.
209  * If an entry with this key already exists we'll replace the existing entry.
210  *
211  * The hash table mutex must be locked manually by calling
212  * _mesa_HashLockMutex() before calling this function.
213  *
214  * \param table the hash table.
215  * \param key the key (not zero).
216  * \param data pointer to user data.
217  * \param isGenName true if the key has been generated by a HashFindFreeKey* function
218  */
219 void
_mesa_HashInsertLocked(struct _mesa_HashTable * table,GLuint key,void * data,GLboolean isGenName)220 _mesa_HashInsertLocked(struct _mesa_HashTable *table, GLuint key, void *data,
221                        GLboolean isGenName)
222 {
223    _mesa_HashInsert_unlocked(table, key, data);
224    if (!isGenName && table->id_alloc)
225       util_idalloc_reserve(table->id_alloc, key);
226 }
227 
228 
229 /**
230  * Insert a key/pointer pair into the hash table.
231  * If an entry with this key already exists we'll replace the existing entry.
232  *
233  * \param table the hash table.
234  * \param key the key (not zero).
235  * \param data pointer to user data.
236  * \param isGenName true if the key has been generated by a HashFindFreeKey* function
237  */
238 void
_mesa_HashInsert(struct _mesa_HashTable * table,GLuint key,void * data,GLboolean isGenName)239 _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data,
240                  GLboolean isGenName)
241 {
242    _mesa_HashLockMutex(table);
243    _mesa_HashInsert_unlocked(table, key, data);
244    if (!isGenName && table->id_alloc)
245       util_idalloc_reserve(table->id_alloc, key);
246    _mesa_HashUnlockMutex(table);
247 }
248 
249 
250 /**
251  * Remove an entry from the hash table.
252  *
253  * \param table the hash table.
254  * \param key key of entry to remove.
255  *
256  * While holding the hash table's lock, searches the entry with the matching
257  * key and unlinks it.
258  */
259 static inline void
_mesa_HashRemove_unlocked(struct _mesa_HashTable * table,GLuint key)260 _mesa_HashRemove_unlocked(struct _mesa_HashTable *table, GLuint key)
261 {
262    struct hash_entry *entry;
263 
264    assert(table);
265    assert(key);
266 
267    #ifndef NDEBUG
268    /* assert if _mesa_HashRemove illegally called from _mesa_HashDeleteAll
269     * callback function. Have to check this outside of mutex lock.
270     */
271    assert(!table->InDeleteAll);
272    #endif
273 
274    if (key == DELETED_KEY_VALUE) {
275       table->deleted_key_data = NULL;
276    } else {
277       entry = _mesa_hash_table_search_pre_hashed(table->ht,
278                                                  uint_hash(key),
279                                                  uint_key(key));
280       _mesa_hash_table_remove(table->ht, entry);
281    }
282 
283    if (table->id_alloc)
284       util_idalloc_free(table->id_alloc, key);
285 }
286 
287 
288 void
_mesa_HashRemoveLocked(struct _mesa_HashTable * table,GLuint key)289 _mesa_HashRemoveLocked(struct _mesa_HashTable *table, GLuint key)
290 {
291    _mesa_HashRemove_unlocked(table, key);
292 }
293 
294 void
_mesa_HashRemove(struct _mesa_HashTable * table,GLuint key)295 _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
296 {
297    _mesa_HashLockMutex(table);
298    _mesa_HashRemove_unlocked(table, key);
299    _mesa_HashUnlockMutex(table);
300 }
301 
302 /**
303  * Delete all entries in a hash table, but don't delete the table itself.
304  * Invoke the given callback function for each table entry.
305  *
306  * \param table  the hash table to delete
307  * \param callback  the callback function
308  * \param userData  arbitrary pointer to pass along to the callback
309  *                  (this is typically a struct gl_context pointer)
310  */
311 void
_mesa_HashDeleteAll(struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)312 _mesa_HashDeleteAll(struct _mesa_HashTable *table,
313                     void (*callback)(void *data, void *userData),
314                     void *userData)
315 {
316    assert(callback);
317    _mesa_HashLockMutex(table);
318    #ifndef NDEBUG
319    table->InDeleteAll = GL_TRUE;
320    #endif
321    hash_table_foreach(table->ht, entry) {
322       callback(entry->data, userData);
323       _mesa_hash_table_remove(table->ht, entry);
324    }
325    if (table->deleted_key_data) {
326       callback(table->deleted_key_data, userData);
327       table->deleted_key_data = NULL;
328    }
329    if (table->id_alloc) {
330       util_idalloc_fini(table->id_alloc);
331       free(table->id_alloc);
332       init_name_reuse(table);
333    }
334    #ifndef NDEBUG
335    table->InDeleteAll = GL_FALSE;
336    #endif
337    table->MaxKey = 0;
338    _mesa_HashUnlockMutex(table);
339 }
340 
341 
342 /**
343  * Walk over all entries in a hash table, calling callback function for each.
344  * \param table  the hash table to walk
345  * \param callback  the callback function
346  * \param userData  arbitrary pointer to pass along to the callback
347  *                  (this is typically a struct gl_context pointer)
348  */
349 static void
hash_walk_unlocked(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)350 hash_walk_unlocked(const struct _mesa_HashTable *table,
351                    void (*callback)(void *data, void *userData),
352                    void *userData)
353 {
354    assert(table);
355    assert(callback);
356 
357    hash_table_foreach(table->ht, entry) {
358       callback(entry->data, userData);
359    }
360    if (table->deleted_key_data)
361       callback(table->deleted_key_data, userData);
362 }
363 
364 
365 void
_mesa_HashWalk(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)366 _mesa_HashWalk(const struct _mesa_HashTable *table,
367                void (*callback)(void *data, void *userData),
368                void *userData)
369 {
370    /* cast-away const */
371    struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
372 
373    _mesa_HashLockMutex(table2);
374    hash_walk_unlocked(table, callback, userData);
375    _mesa_HashUnlockMutex(table2);
376 }
377 
378 void
_mesa_HashWalkLocked(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)379 _mesa_HashWalkLocked(const struct _mesa_HashTable *table,
380                void (*callback)(void *data, void *userData),
381                void *userData)
382 {
383    hash_walk_unlocked(table, callback, userData);
384 }
385 
386 /**
387  * Dump contents of hash table for debugging.
388  *
389  * \param table the hash table.
390  */
391 void
_mesa_HashPrint(const struct _mesa_HashTable * table)392 _mesa_HashPrint(const struct _mesa_HashTable *table)
393 {
394    if (table->deleted_key_data)
395       _mesa_debug(NULL, "%u %p\n", DELETED_KEY_VALUE, table->deleted_key_data);
396 
397    hash_table_foreach(table->ht, entry) {
398       _mesa_debug(NULL, "%u %p\n", (unsigned)(uintptr_t) entry->key,
399                   entry->data);
400    }
401 }
402 
403 
404 /**
405  * Find a block of adjacent unused hash keys.
406  *
407  * \param table the hash table.
408  * \param numKeys number of keys needed.
409  *
410  * \return Starting key of free block or 0 if failure.
411  *
412  * If there are enough free keys between the maximum key existing in the table
413  * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return
414  * the adjacent key. Otherwise do a full search for a free key block in the
415  * allowable key range.
416  */
417 GLuint
_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable * table,GLuint numKeys)418 _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
419 {
420    const GLuint maxKey = ~((GLuint) 0) - 1;
421    if (table->id_alloc && numKeys == 1) {
422       return util_idalloc_alloc(table->id_alloc);
423    } else if (maxKey - numKeys > table->MaxKey) {
424       /* the quick solution */
425       return table->MaxKey + 1;
426    }
427    else {
428       /* the slow solution */
429       GLuint freeCount = 0;
430       GLuint freeStart = 1;
431       GLuint key;
432       for (key = 1; key != maxKey; key++) {
433 	 if (_mesa_HashLookup_unlocked(table, key)) {
434 	    /* darn, this key is already in use */
435 	    freeCount = 0;
436 	    freeStart = key+1;
437 	 }
438 	 else {
439 	    /* this key not in use, check if we've found enough */
440 	    freeCount++;
441 	    if (freeCount == numKeys) {
442 	       return freeStart;
443 	    }
444 	 }
445       }
446       /* cannot allocate a block of numKeys consecutive keys */
447       return 0;
448    }
449 }
450 
451 
452 bool
_mesa_HashFindFreeKeys(struct _mesa_HashTable * table,GLuint * keys,GLuint numKeys)453 _mesa_HashFindFreeKeys(struct _mesa_HashTable *table, GLuint* keys, GLuint numKeys)
454 {
455    if (!table->id_alloc) {
456       GLuint first = _mesa_HashFindFreeKeyBlock(table, numKeys);
457       for (int i = 0; i < numKeys; i++) {
458          keys[i] = first + i;
459       }
460       return first != 0;
461    }
462 
463    for (int i = 0; i < numKeys; i++) {
464       keys[i] = util_idalloc_alloc(table->id_alloc);
465    }
466 
467    return true;
468 }
469 
470 
471 /**
472  * Return the number of entries in the hash table.
473  */
474 GLuint
_mesa_HashNumEntries(const struct _mesa_HashTable * table)475 _mesa_HashNumEntries(const struct _mesa_HashTable *table)
476 {
477    GLuint count = 0;
478 
479    if (table->deleted_key_data)
480       count++;
481 
482    count += _mesa_hash_table_num_entries(table->ht);
483 
484    return count;
485 }
486