Replace _mesa_malloc, _mesa_calloc and _mesa_free with plain libc versions
[mesa.git] / src / mesa / main / hash.c
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 *
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
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.
124 *
125 * \param table the hash table.
126 * \param key the key.
127 *
128 * \return pointer to user's data or NULL if key not in table
129 */
130 void *
131 _mesa_HashLookup(struct _mesa_HashTable *table, GLuint key)
132 {
133 GLuint pos;
134 const struct HashEntry *entry;
135
136 assert(table);
137 assert(key);
138
139 pos = HASH_FUNC(key);
140 _glthread_LOCK_MUTEX(table->Mutex);
141 entry = table->Table[pos];
142 while (entry) {
143 if (entry->Key == key) {
144 _glthread_UNLOCK_MUTEX(table->Mutex);
145 return entry->Data;
146 }
147 entry = entry->Next;
148 }
149 _glthread_UNLOCK_MUTEX(table->Mutex);
150 return NULL;
151 }
152
153
154
155 /**
156 * Insert a key/pointer pair into the hash table.
157 * If an entry with this key already exists we'll replace the existing entry.
158 *
159 * \param table the hash table.
160 * \param key the key (not zero).
161 * \param data pointer to user data.
162 */
163 void
164 _mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data)
165 {
166 /* search for existing entry with this key */
167 GLuint pos;
168 struct HashEntry *entry;
169
170 assert(table);
171 assert(key);
172
173 _glthread_LOCK_MUTEX(table->Mutex);
174
175 if (key > table->MaxKey)
176 table->MaxKey = key;
177
178 pos = HASH_FUNC(key);
179
180 /* check if replacing an existing entry with same key */
181 for (entry = table->Table[pos]; entry; entry = entry->Next) {
182 if (entry->Key == key) {
183 /* replace entry's data */
184 #if 0 /* not sure this check is always valid */
185 if (entry->Data) {
186 _mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert");
187 }
188 #endif
189 entry->Data = data;
190 _glthread_UNLOCK_MUTEX(table->Mutex);
191 return;
192 }
193 }
194
195 /* alloc and insert new table entry */
196 entry = MALLOC_STRUCT(HashEntry);
197 if (entry) {
198 entry->Key = key;
199 entry->Data = data;
200 entry->Next = table->Table[pos];
201 table->Table[pos] = entry;
202 }
203
204 _glthread_UNLOCK_MUTEX(table->Mutex);
205 }
206
207
208
209 /**
210 * Remove an entry from the hash table.
211 *
212 * \param table the hash table.
213 * \param key key of entry to remove.
214 *
215 * While holding the hash table's lock, searches the entry with the matching
216 * key and unlinks it.
217 */
218 void
219 _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
220 {
221 GLuint pos;
222 struct HashEntry *entry, *prev;
223
224 assert(table);
225 assert(key);
226
227 /* have to check this outside of mutex lock */
228 if (table->InDeleteAll) {
229 _mesa_problem(NULL, "_mesa_HashRemove illegally called from "
230 "_mesa_HashDeleteAll callback function");
231 return;
232 }
233
234 _glthread_LOCK_MUTEX(table->Mutex);
235
236 pos = HASH_FUNC(key);
237 prev = NULL;
238 entry = table->Table[pos];
239 while (entry) {
240 if (entry->Key == key) {
241 /* found it! */
242 if (prev) {
243 prev->Next = entry->Next;
244 }
245 else {
246 table->Table[pos] = entry->Next;
247 }
248 free(entry);
249 _glthread_UNLOCK_MUTEX(table->Mutex);
250 return;
251 }
252 prev = entry;
253 entry = entry->Next;
254 }
255
256 _glthread_UNLOCK_MUTEX(table->Mutex);
257 }
258
259
260
261 /**
262 * Delete all entries in a hash table, but don't delete the table itself.
263 * Invoke the given callback function for each table entry.
264 *
265 * \param table the hash table to delete
266 * \param callback the callback function
267 * \param userData arbitrary pointer to pass along to the callback
268 * (this is typically a GLcontext pointer)
269 */
270 void
271 _mesa_HashDeleteAll(struct _mesa_HashTable *table,
272 void (*callback)(GLuint key, void *data, void *userData),
273 void *userData)
274 {
275 GLuint pos;
276 ASSERT(table);
277 ASSERT(callback);
278 _glthread_LOCK_MUTEX(table->Mutex);
279 table->InDeleteAll = GL_TRUE;
280 for (pos = 0; pos < TABLE_SIZE; pos++) {
281 struct HashEntry *entry, *next;
282 for (entry = table->Table[pos]; entry; entry = next) {
283 callback(entry->Key, entry->Data, userData);
284 next = entry->Next;
285 free(entry);
286 }
287 table->Table[pos] = NULL;
288 }
289 table->InDeleteAll = GL_FALSE;
290 _glthread_UNLOCK_MUTEX(table->Mutex);
291 }
292
293
294 /**
295 * Walk over all entries in a hash table, calling callback function for each.
296 * Note: we use a separate mutex in this function to avoid a recursive
297 * locking deadlock (in case the callback calls _mesa_HashRemove()) and to
298 * prevent multiple threads/contexts from getting tangled up.
299 * A lock-less version of this function could be used when the table will
300 * not be modified.
301 * \param table the hash table to walk
302 * \param callback the callback function
303 * \param userData arbitrary pointer to pass along to the callback
304 * (this is typically a GLcontext pointer)
305 */
306 void
307 _mesa_HashWalk(const struct _mesa_HashTable *table,
308 void (*callback)(GLuint key, void *data, void *userData),
309 void *userData)
310 {
311 /* cast-away const */
312 struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
313 GLuint pos;
314 ASSERT(table);
315 ASSERT(callback);
316 _glthread_LOCK_MUTEX(table2->WalkMutex);
317 for (pos = 0; pos < TABLE_SIZE; pos++) {
318 struct HashEntry *entry, *next;
319 for (entry = table->Table[pos]; entry; entry = next) {
320 /* save 'next' pointer now in case the callback deletes the entry */
321 next = entry->Next;
322 callback(entry->Key, entry->Data, userData);
323 }
324 }
325 _glthread_UNLOCK_MUTEX(table2->WalkMutex);
326 }
327
328
329 /**
330 * Return the key of the "first" entry in the hash table.
331 * While holding the lock, walks through all table positions until finding
332 * the first entry of the first non-empty one.
333 *
334 * \param table the hash table
335 * \return key for the "first" entry in the hash table.
336 */
337 GLuint
338 _mesa_HashFirstEntry(struct _mesa_HashTable *table)
339 {
340 GLuint pos;
341 assert(table);
342 _glthread_LOCK_MUTEX(table->Mutex);
343 for (pos = 0; pos < TABLE_SIZE; pos++) {
344 if (table->Table[pos]) {
345 _glthread_UNLOCK_MUTEX(table->Mutex);
346 return table->Table[pos]->Key;
347 }
348 }
349 _glthread_UNLOCK_MUTEX(table->Mutex);
350 return 0;
351 }
352
353
354 /**
355 * Given a hash table key, return the next key. This is used to walk
356 * over all entries in the table. Note that the keys returned during
357 * walking won't be in any particular order.
358 * \return next hash key or 0 if end of table.
359 */
360 GLuint
361 _mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key)
362 {
363 const struct HashEntry *entry;
364 GLuint pos;
365
366 assert(table);
367 assert(key);
368
369 /* Find the entry with given key */
370 pos = HASH_FUNC(key);
371 for (entry = table->Table[pos]; entry ; entry = entry->Next) {
372 if (entry->Key == key) {
373 break;
374 }
375 }
376
377 if (!entry) {
378 /* the given key was not found, so we can't find the next entry */
379 return 0;
380 }
381
382 if (entry->Next) {
383 /* return next in linked list */
384 return entry->Next->Key;
385 }
386 else {
387 /* look for next non-empty table slot */
388 pos++;
389 while (pos < TABLE_SIZE) {
390 if (table->Table[pos]) {
391 return table->Table[pos]->Key;
392 }
393 pos++;
394 }
395 return 0;
396 }
397 }
398
399
400 /**
401 * Dump contents of hash table for debugging.
402 *
403 * \param table the hash table.
404 */
405 void
406 _mesa_HashPrint(const struct _mesa_HashTable *table)
407 {
408 GLuint pos;
409 assert(table);
410 for (pos = 0; pos < TABLE_SIZE; pos++) {
411 const struct HashEntry *entry = table->Table[pos];
412 while (entry) {
413 _mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data);
414 entry = entry->Next;
415 }
416 }
417 }
418
419
420
421 /**
422 * Find a block of adjacent unused hash keys.
423 *
424 * \param table the hash table.
425 * \param numKeys number of keys needed.
426 *
427 * \return Starting key of free block or 0 if failure.
428 *
429 * If there are enough free keys between the maximum key existing in the table
430 * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return
431 * the adjacent key. Otherwise do a full search for a free key block in the
432 * allowable key range.
433 */
434 GLuint
435 _mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
436 {
437 const GLuint maxKey = ~((GLuint) 0);
438 _glthread_LOCK_MUTEX(table->Mutex);
439 if (maxKey - numKeys > table->MaxKey) {
440 /* the quick solution */
441 _glthread_UNLOCK_MUTEX(table->Mutex);
442 return table->MaxKey + 1;
443 }
444 else {
445 /* the slow solution */
446 GLuint freeCount = 0;
447 GLuint freeStart = 1;
448 GLuint key;
449 for (key = 1; key != maxKey; key++) {
450 if (_mesa_HashLookup(table, key)) {
451 /* darn, this key is already in use */
452 freeCount = 0;
453 freeStart = key+1;
454 }
455 else {
456 /* this key not in use, check if we've found enough */
457 freeCount++;
458 if (freeCount == numKeys) {
459 _glthread_UNLOCK_MUTEX(table->Mutex);
460 return freeStart;
461 }
462 }
463 }
464 /* cannot allocate a block of numKeys consecutive keys */
465 _glthread_UNLOCK_MUTEX(table->Mutex);
466 return 0;
467 }
468 }
469
470
471 #if 0 /* debug only */
472
473 /**
474 * Test walking over all the entries in a hash table.
475 */
476 static void
477 test_hash_walking(void)
478 {
479 struct _mesa_HashTable *t = _mesa_NewHashTable();
480 const GLuint limit = 50000;
481 GLuint i;
482
483 /* create some entries */
484 for (i = 0; i < limit; i++) {
485 GLuint dummy;
486 GLuint k = (rand() % (limit * 10)) + 1;
487 while (_mesa_HashLookup(t, k)) {
488 /* id already in use, try another */
489 k = (rand() % (limit * 10)) + 1;
490 }
491 _mesa_HashInsert(t, k, &dummy);
492 }
493
494 /* walk over all entries */
495 {
496 GLuint k = _mesa_HashFirstEntry(t);
497 GLuint count = 0;
498 while (k) {
499 GLuint knext = _mesa_HashNextEntry(t, k);
500 assert(knext != k);
501 _mesa_HashRemove(t, k);
502 count++;
503 k = knext;
504 }
505 assert(count == limit);
506 k = _mesa_HashFirstEntry(t);
507 assert(k==0);
508 }
509
510 _mesa_DeleteHashTable(t);
511 }
512
513
514 void
515 _mesa_test_hash_functions(void)
516 {
517 int a, b, c;
518 struct _mesa_HashTable *t;
519
520 t = _mesa_NewHashTable();
521 _mesa_HashInsert(t, 501, &a);
522 _mesa_HashInsert(t, 10, &c);
523 _mesa_HashInsert(t, 0xfffffff8, &b);
524 /*_mesa_HashPrint(t);*/
525
526 assert(_mesa_HashLookup(t,501));
527 assert(!_mesa_HashLookup(t,1313));
528 assert(_mesa_HashFindFreeKeyBlock(t, 100));
529
530 _mesa_DeleteHashTable(t);
531
532 test_hash_walking();
533 }
534
535 #endif