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