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