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