• 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       /*
66        * Needs to be recursive, since the callback in _mesa_HashWalk()
67        * is allowed to call _mesa_HashRemove().
68        */
69       mtx_init(&table->Mutex, mtx_recursive);
70    }
71    else {
72       _mesa_error_no_memory(__func__);
73    }
74 
75    return table;
76 }
77 
78 
79 
80 /**
81  * Delete a hash table.
82  * Frees each entry on the hash table and then the hash table structure itself.
83  * Note that the caller should have already traversed the table and deleted
84  * the objects in the table (i.e. We don't free the entries' data pointer).
85  *
86  * \param table the hash table to delete.
87  */
88 void
_mesa_DeleteHashTable(struct _mesa_HashTable * table)89 _mesa_DeleteHashTable(struct _mesa_HashTable *table)
90 {
91    assert(table);
92 
93    if (_mesa_hash_table_next_entry(table->ht, NULL) != NULL) {
94       _mesa_problem(NULL, "In _mesa_DeleteHashTable, found non-freed data");
95    }
96 
97    _mesa_hash_table_destroy(table->ht, NULL);
98    if (table->id_alloc) {
99       util_idalloc_fini(table->id_alloc);
100       free(table->id_alloc);
101    }
102 
103    mtx_destroy(&table->Mutex);
104    free(table);
105 }
106 
107 void
_mesa_HashEnableNameReuse(struct _mesa_HashTable * table)108 _mesa_HashEnableNameReuse(struct _mesa_HashTable *table)
109 {
110    _mesa_HashLockMutex(table);
111    assert(_mesa_hash_table_num_entries(table->ht) == 0);
112    table->id_alloc = MALLOC_STRUCT(util_idalloc);
113    util_idalloc_init(table->id_alloc);
114    util_idalloc_resize(table->id_alloc, 8);
115    ASSERTED GLuint reserve0 = util_idalloc_alloc(table->id_alloc);
116    assert (reserve0 == 0);
117    _mesa_HashUnlockMutex(table);
118 }
119 
120 
121 /**
122  * Lookup an entry in the hash table, without locking.
123  * \sa _mesa_HashLookup
124  */
125 static inline void *
_mesa_HashLookup_unlocked(struct _mesa_HashTable * table,GLuint key)126 _mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key)
127 {
128    const struct hash_entry *entry;
129 
130    assert(table);
131    assert(key);
132 
133    if (key == DELETED_KEY_VALUE)
134       return table->deleted_key_data;
135 
136    entry = _mesa_hash_table_search_pre_hashed(table->ht,
137                                               uint_hash(key),
138                                               uint_key(key));
139    if (!entry)
140       return NULL;
141 
142    return entry->data;
143 }
144 
145 
146 /**
147  * Lookup an entry in the hash table.
148  *
149  * \param table the hash table.
150  * \param key the key.
151  *
152  * \return pointer to user's data or NULL if key not in table
153  */
154 void *
_mesa_HashLookup(struct _mesa_HashTable * table,GLuint key)155 _mesa_HashLookup(struct _mesa_HashTable *table, GLuint key)
156 {
157    void *res;
158    _mesa_HashLockMutex(table);
159    res = _mesa_HashLookup_unlocked(table, key);
160    _mesa_HashUnlockMutex(table);
161    return res;
162 }
163 
164 
165 /**
166  * Lookup an entry in the hash table without locking the mutex.
167  *
168  * The hash table mutex must be locked manually by calling
169  * _mesa_HashLockMutex() before calling this function.
170  *
171  * \param table the hash table.
172  * \param key the key.
173  *
174  * \return pointer to user's data or NULL if key not in table
175  */
176 void *
_mesa_HashLookupLocked(struct _mesa_HashTable * table,GLuint key)177 _mesa_HashLookupLocked(struct _mesa_HashTable *table, GLuint key)
178 {
179    return _mesa_HashLookup_unlocked(table, key);
180 }
181 
182 
183 static inline void
_mesa_HashInsert_unlocked(struct _mesa_HashTable * table,GLuint key,void * data)184 _mesa_HashInsert_unlocked(struct _mesa_HashTable *table, GLuint key, void *data)
185 {
186    uint32_t hash = uint_hash(key);
187    struct hash_entry *entry;
188 
189    assert(table);
190    assert(key);
191 
192    if (key > table->MaxKey)
193       table->MaxKey = key;
194 
195    if (key == DELETED_KEY_VALUE) {
196       table->deleted_key_data = data;
197    } else {
198       entry = _mesa_hash_table_search_pre_hashed(table->ht, hash, uint_key(key));
199       if (entry) {
200          entry->data = data;
201       } else {
202          _mesa_hash_table_insert_pre_hashed(table->ht, hash, uint_key(key), data);
203       }
204    }
205 }
206 
207 
208 /**
209  * Insert a key/pointer pair into the hash table without locking the mutex.
210  * If an entry with this key already exists we'll replace the existing entry.
211  *
212  * The hash table mutex must be locked manually by calling
213  * _mesa_HashLockMutex() before calling this function.
214  *
215  * \param table the hash table.
216  * \param key the key (not zero).
217  * \param data pointer to user data.
218  * \param isGenName true if the key has been generated by a HashFindFreeKey* function
219  */
220 void
_mesa_HashInsertLocked(struct _mesa_HashTable * table,GLuint key,void * data,GLboolean isGenName)221 _mesa_HashInsertLocked(struct _mesa_HashTable *table, GLuint key, void *data,
222                        GLboolean isGenName)
223 {
224    _mesa_HashInsert_unlocked(table, key, data);
225    if (!isGenName && table->id_alloc)
226       util_idalloc_reserve(table->id_alloc, key);
227 }
228 
229 
230 /**
231  * Insert a key/pointer pair into the hash table.
232  * If an entry with this key already exists we'll replace the existing entry.
233  *
234  * \param table the hash table.
235  * \param key the key (not zero).
236  * \param data pointer to user data.
237  * \param isGenName true if the key has been generated by a HashFindFreeKey* function
238  */
239 void
_mesa_HashInsert(struct _mesa_HashTable * table,GLuint key,void * data,GLboolean isGenName)240 _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data,
241                  GLboolean isGenName)
242 {
243    _mesa_HashLockMutex(table);
244    _mesa_HashInsert_unlocked(table, key, data);
245    if (!isGenName && table->id_alloc)
246       util_idalloc_reserve(table->id_alloc, key);
247    _mesa_HashUnlockMutex(table);
248 }
249 
250 
251 /**
252  * Remove an entry from the hash table.
253  *
254  * \param table the hash table.
255  * \param key key of entry to remove.
256  *
257  * While holding the hash table's lock, searches the entry with the matching
258  * key and unlinks it.
259  */
260 static inline void
_mesa_HashRemove_unlocked(struct _mesa_HashTable * table,GLuint key)261 _mesa_HashRemove_unlocked(struct _mesa_HashTable *table, GLuint key)
262 {
263    struct hash_entry *entry;
264 
265    assert(table);
266    assert(key);
267 
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 
273    if (key == DELETED_KEY_VALUE) {
274       table->deleted_key_data = NULL;
275    } else {
276       entry = _mesa_hash_table_search_pre_hashed(table->ht,
277                                                  uint_hash(key),
278                                                  uint_key(key));
279       _mesa_hash_table_remove(table->ht, entry);
280    }
281 
282    if (table->id_alloc)
283       util_idalloc_free(table->id_alloc, key);
284 }
285 
286 
287 void
_mesa_HashRemoveLocked(struct _mesa_HashTable * table,GLuint key)288 _mesa_HashRemoveLocked(struct _mesa_HashTable *table, GLuint key)
289 {
290    _mesa_HashRemove_unlocked(table, key);
291 }
292 
293 void
_mesa_HashRemove(struct _mesa_HashTable * table,GLuint key)294 _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
295 {
296    _mesa_HashLockMutex(table);
297    _mesa_HashRemove_unlocked(table, key);
298    _mesa_HashUnlockMutex(table);
299 }
300 
301 /**
302  * Delete all entries in a hash table, but don't delete the table itself.
303  * Invoke the given callback function for each table entry.
304  *
305  * \param table  the hash table to delete
306  * \param callback  the callback function
307  * \param userData  arbitrary pointer to pass along to the callback
308  *                  (this is typically a struct gl_context pointer)
309  */
310 void
_mesa_HashDeleteAll(struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)311 _mesa_HashDeleteAll(struct _mesa_HashTable *table,
312                     void (*callback)(void *data, void *userData),
313                     void *userData)
314 {
315    assert(callback);
316    _mesa_HashLockMutex(table);
317    table->InDeleteAll = GL_TRUE;
318    hash_table_foreach(table->ht, entry) {
319       callback(entry->data, userData);
320       _mesa_hash_table_remove(table->ht, entry);
321    }
322    if (table->deleted_key_data) {
323       callback(table->deleted_key_data, userData);
324       table->deleted_key_data = NULL;
325    }
326    table->InDeleteAll = GL_FALSE;
327    if (table->id_alloc) {
328       util_idalloc_fini(table->id_alloc);
329       free(table->id_alloc);
330       _mesa_HashEnableNameReuse(table);
331    }
332    table->MaxKey = 0;
333    _mesa_HashUnlockMutex(table);
334 }
335 
336 
337 /**
338  * Walk over all entries in a hash table, calling callback function for each.
339  * \param table  the hash table to walk
340  * \param callback  the callback function
341  * \param userData  arbitrary pointer to pass along to the callback
342  *                  (this is typically a struct gl_context pointer)
343  */
344 static void
hash_walk_unlocked(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)345 hash_walk_unlocked(const struct _mesa_HashTable *table,
346                    void (*callback)(void *data, void *userData),
347                    void *userData)
348 {
349    assert(table);
350    assert(callback);
351 
352    hash_table_foreach(table->ht, entry) {
353       callback(entry->data, userData);
354    }
355    if (table->deleted_key_data)
356       callback(table->deleted_key_data, userData);
357 }
358 
359 
360 void
_mesa_HashWalk(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)361 _mesa_HashWalk(const struct _mesa_HashTable *table,
362                void (*callback)(void *data, void *userData),
363                void *userData)
364 {
365    /* cast-away const */
366    struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
367 
368    _mesa_HashLockMutex(table2);
369    hash_walk_unlocked(table, callback, userData);
370    _mesa_HashUnlockMutex(table2);
371 }
372 
373 void
_mesa_HashWalkLocked(const struct _mesa_HashTable * table,void (* callback)(void * data,void * userData),void * userData)374 _mesa_HashWalkLocked(const struct _mesa_HashTable *table,
375                void (*callback)(void *data, void *userData),
376                void *userData)
377 {
378    hash_walk_unlocked(table, callback, userData);
379 }
380 
381 /**
382  * Dump contents of hash table for debugging.
383  *
384  * \param table the hash table.
385  */
386 void
_mesa_HashPrint(const struct _mesa_HashTable * table)387 _mesa_HashPrint(const struct _mesa_HashTable *table)
388 {
389    if (table->deleted_key_data)
390       _mesa_debug(NULL, "%u %p\n", DELETED_KEY_VALUE, table->deleted_key_data);
391 
392    hash_table_foreach(table->ht, entry) {
393       _mesa_debug(NULL, "%u %p\n", (unsigned)(uintptr_t) entry->key,
394                   entry->data);
395    }
396 }
397 
398 
399 /**
400  * Find a block of adjacent unused hash keys.
401  *
402  * \param table the hash table.
403  * \param numKeys number of keys needed.
404  *
405  * \return Starting key of free block or 0 if failure.
406  *
407  * If there are enough free keys between the maximum key existing in the table
408  * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return
409  * the adjacent key. Otherwise do a full search for a free key block in the
410  * allowable key range.
411  */
412 GLuint
_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable * table,GLuint numKeys)413 _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
414 {
415    const GLuint maxKey = ~((GLuint) 0) - 1;
416    if (table->id_alloc && numKeys == 1) {
417       return util_idalloc_alloc(table->id_alloc);
418    } else if (maxKey - numKeys > table->MaxKey) {
419       /* the quick solution */
420       return table->MaxKey + 1;
421    }
422    else {
423       /* the slow solution */
424       GLuint freeCount = 0;
425       GLuint freeStart = 1;
426       GLuint key;
427       for (key = 1; key != maxKey; key++) {
428 	 if (_mesa_HashLookup_unlocked(table, key)) {
429 	    /* darn, this key is already in use */
430 	    freeCount = 0;
431 	    freeStart = key+1;
432 	 }
433 	 else {
434 	    /* this key not in use, check if we've found enough */
435 	    freeCount++;
436 	    if (freeCount == numKeys) {
437 	       return freeStart;
438 	    }
439 	 }
440       }
441       /* cannot allocate a block of numKeys consecutive keys */
442       return 0;
443    }
444 }
445 
446 
447 bool
_mesa_HashFindFreeKeys(struct _mesa_HashTable * table,GLuint * keys,GLuint numKeys)448 _mesa_HashFindFreeKeys(struct _mesa_HashTable *table, GLuint* keys, GLuint numKeys)
449 {
450    if (!table->id_alloc) {
451       GLuint first = _mesa_HashFindFreeKeyBlock(table, numKeys);
452       for (int i = 0; i < numKeys; i++) {
453          keys[i] = first + i;
454       }
455       return first != 0;
456    }
457 
458    for (int i = 0; i < numKeys; i++) {
459       keys[i] = util_idalloc_alloc(table->id_alloc);
460    }
461 
462    return true;
463 }
464 
465 
466 /**
467  * Return the number of entries in the hash table.
468  */
469 GLuint
_mesa_HashNumEntries(const struct _mesa_HashTable * table)470 _mesa_HashNumEntries(const struct _mesa_HashTable *table)
471 {
472    GLuint count = 0;
473 
474    if (table->deleted_key_data)
475       count++;
476 
477    count += _mesa_hash_table_num_entries(table->ht);
478 
479    return count;
480 }
481