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