⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hash.c

📁 Mesa is an open-source implementation of the OpenGL specification - a system for rendering interacti
💻 C
字号:
/** * \file hash.c * Generic hash table.  * * Used for display lists, texture objects, vertex/fragment programs, * buffer objects, etc.  The hash functions are thread-safe. *  * \note key=0 is illegal. * * \author Brian Paul *//* * Mesa 3-D graphics library * Version:  6.5.1 * * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */#include "glheader.h"#include "imports.h"#include "glapi/glthread.h"#include "hash.h"#define TABLE_SIZE 1023  /**< Size of lookup table/array */#define HASH_FUNC(K)  ((K) % TABLE_SIZE)/** * An entry in the hash table.   */struct HashEntry {   GLuint Key;             /**< the entry's key */   void *Data;             /**< the entry's data */   struct HashEntry *Next; /**< pointer to next entry */};/** * The hash table data structure.   */struct _mesa_HashTable {   struct HashEntry *Table[TABLE_SIZE];  /**< the lookup table */   GLuint MaxKey;                        /**< highest key inserted so far */   _glthread_Mutex Mutex;                /**< mutual exclusion lock */   GLboolean InDeleteAll;                /**< Debug check */};/** * Create a new hash table. *  * \return pointer to a new, empty hash table. */struct _mesa_HashTable *_mesa_NewHashTable(void){   struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);   if (table) {      _glthread_INIT_MUTEX(table->Mutex);   }   return table;}/** * Delete a hash table. * Frees each entry on the hash table and then the hash table structure itself. * Note that the caller should have already traversed the table and deleted * the objects in the table (i.e. We don't free the entries' data pointer). * * \param table the hash table to delete. */void_mesa_DeleteHashTable(struct _mesa_HashTable *table){   GLuint pos;   assert(table);   for (pos = 0; pos < TABLE_SIZE; pos++) {      struct HashEntry *entry = table->Table[pos];      while (entry) {	 struct HashEntry *next = entry->Next;         if (entry->Data) {            _mesa_problem(NULL,                          "In _mesa_DeleteHashTable, found non-freed data");         }	 _mesa_free(entry);	 entry = next;      }   }   _glthread_DESTROY_MUTEX(table->Mutex);   _mesa_free(table);}/** * Lookup an entry in the hash table. *  * \param table the hash table. * \param key the key. *  * \return pointer to user's data or NULL if key not in table */void *_mesa_HashLookup(const struct _mesa_HashTable *table, GLuint key){   GLuint pos;   const struct HashEntry *entry;   assert(table);   assert(key);   pos = HASH_FUNC(key);   entry = table->Table[pos];   while (entry) {      if (entry->Key == key) {	 return entry->Data;      }      entry = entry->Next;   }   return NULL;}/** * Insert a key/pointer pair into the hash table.   * If an entry with this key already exists we'll replace the existing entry. *  * \param table the hash table. * \param key the key (not zero). * \param data pointer to user data. */void_mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data){   /* search for existing entry with this key */   GLuint pos;   struct HashEntry *entry;   assert(table);   assert(key);   _glthread_LOCK_MUTEX(table->Mutex);   if (key > table->MaxKey)      table->MaxKey = key;   pos = HASH_FUNC(key);   /* check if replacing an existing entry with same key */   for (entry = table->Table[pos]; entry; entry = entry->Next) {      if (entry->Key == key) {         /* replace entry's data */#if 0 /* not sure this check is always valid */         if (entry->Data) {            _mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert");         }#endif	 entry->Data = data;         _glthread_UNLOCK_MUTEX(table->Mutex);	 return;      }   }   /* alloc and insert new table entry */   entry = MALLOC_STRUCT(HashEntry);   entry->Key = key;   entry->Data = data;   entry->Next = table->Table[pos];   table->Table[pos] = entry;   _glthread_UNLOCK_MUTEX(table->Mutex);}/** * Remove an entry from the hash table. *  * \param table the hash table. * \param key key of entry to remove. * * While holding the hash table's lock, searches the entry with the matching * key and unlinks it. */void_mesa_HashRemove(struct _mesa_HashTable *table, GLuint key){   GLuint pos;   struct HashEntry *entry, *prev;   assert(table);   assert(key);   /* have to check this outside of mutex lock */   if (table->InDeleteAll) {      _mesa_problem(NULL, "_mesa_HashRemove illegally called from "                    "_mesa_HashDeleteAll callback function");      return;   }   _glthread_LOCK_MUTEX(table->Mutex);   pos = HASH_FUNC(key);   prev = NULL;   entry = table->Table[pos];   while (entry) {      if (entry->Key == key) {         /* found it! */         if (prev) {            prev->Next = entry->Next;         }         else {            table->Table[pos] = entry->Next;         }         _mesa_free(entry);         _glthread_UNLOCK_MUTEX(table->Mutex);	 return;      }      prev = entry;      entry = entry->Next;   }   _glthread_UNLOCK_MUTEX(table->Mutex);}/** * Delete all entries in a hash table, but don't delete the table itself. * Invoke the given callback function for each table entry. * * \param table  the hash table to delete * \param callback  the callback function * \param userData  arbitrary pointer to pass along to the callback *                  (this is typically a GLcontext pointer) */void_mesa_HashDeleteAll(struct _mesa_HashTable *table,                    void (*callback)(GLuint key, void *data, void *userData),                    void *userData){   GLuint pos;   ASSERT(table);   ASSERT(callback);   _glthread_LOCK_MUTEX(table->Mutex);   table->InDeleteAll = GL_TRUE;   for (pos = 0; pos < TABLE_SIZE; pos++) {      struct HashEntry *entry, *next;      for (entry = table->Table[pos]; entry; entry = next) {         callback(entry->Key, entry->Data, userData);         next = entry->Next;         _mesa_free(entry);      }      table->Table[pos] = NULL;   }   table->InDeleteAll = GL_FALSE;   _glthread_UNLOCK_MUTEX(table->Mutex);}/** * Walk over all entries in a hash table, calling callback function for each. * \param table  the hash table to walk * \param callback  the callback function * \param userData  arbitrary pointer to pass along to the callback *                  (this is typically a GLcontext pointer) */void_mesa_HashWalk(const struct _mesa_HashTable *table,               void (*callback)(GLuint key, void *data, void *userData),               void *userData){   /* cast-away const */   struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;   GLuint pos;   ASSERT(table);   ASSERT(callback);   _glthread_UNLOCK_MUTEX(table2->Mutex);   for (pos = 0; pos < TABLE_SIZE; pos++) {      struct HashEntry *entry;      for (entry = table->Table[pos]; entry; entry = entry->Next) {         callback(entry->Key, entry->Data, userData);      }   }   _glthread_UNLOCK_MUTEX(table2->Mutex);}/** * Return the key of the "first" entry in the hash table. * While holding the lock, walks through all table positions until finding * the first entry of the first non-empty one. *  * \param table  the hash table * \return key for the "first" entry in the hash table. */GLuint_mesa_HashFirstEntry(struct _mesa_HashTable *table){   GLuint pos;   assert(table);   _glthread_LOCK_MUTEX(table->Mutex);   for (pos = 0; pos < TABLE_SIZE; pos++) {      if (table->Table[pos]) {         _glthread_UNLOCK_MUTEX(table->Mutex);         return table->Table[pos]->Key;      }   }   _glthread_UNLOCK_MUTEX(table->Mutex);   return 0;}/** * Given a hash table key, return the next key.  This is used to walk * over all entries in the table.  Note that the keys returned during * walking won't be in any particular order. * \return next hash key or 0 if end of table. */GLuint_mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key){   const struct HashEntry *entry;   GLuint pos;   assert(table);   assert(key);   /* Find the entry with given key */   pos = HASH_FUNC(key);   for (entry = table->Table[pos]; entry ; entry = entry->Next) {      if (entry->Key == key) {         break;      }   }   if (!entry) {      /* the given key was not found, so we can't find the next entry */      return 0;   }   if (entry->Next) {      /* return next in linked list */      return entry->Next->Key;   }   else {      /* look for next non-empty table slot */      pos++;      while (pos < TABLE_SIZE) {         if (table->Table[pos]) {            return table->Table[pos]->Key;         }         pos++;      }      return 0;   }}/** * Dump contents of hash table for debugging. *  * \param table the hash table. */void_mesa_HashPrint(const struct _mesa_HashTable *table){   GLuint pos;   assert(table);   for (pos = 0; pos < TABLE_SIZE; pos++) {      const struct HashEntry *entry = table->Table[pos];      while (entry) {	 _mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data);	 entry = entry->Next;      }   }}/** * Find a block of adjacent unused hash keys. *  * \param table the hash table. * \param numKeys number of keys needed. *  * \return Starting key of free block or 0 if failure. * * If there are enough free keys between the maximum key existing in the table * (_mesa_HashTable::MaxKey) and the maximum key possible, then simply return * the adjacent key. Otherwise do a full search for a free key block in the * allowable key range. */GLuint_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys){   const GLuint maxKey = ~((GLuint) 0);   _glthread_LOCK_MUTEX(table->Mutex);   if (maxKey - numKeys > table->MaxKey) {      /* the quick solution */      _glthread_UNLOCK_MUTEX(table->Mutex);      return table->MaxKey + 1;   }   else {      /* the slow solution */      GLuint freeCount = 0;      GLuint freeStart = 1;      GLuint key;      for (key = 1; key != maxKey; key++) {	 if (_mesa_HashLookup(table, key)) {	    /* darn, this key is already in use */	    freeCount = 0;	    freeStart = key+1;	 }	 else {	    /* this key not in use, check if we've found enough */	    freeCount++;	    if (freeCount == numKeys) {               _glthread_UNLOCK_MUTEX(table->Mutex);	       return freeStart;	    }	 }      }      /* cannot allocate a block of numKeys consecutive keys */      _glthread_UNLOCK_MUTEX(table->Mutex);      return 0;   }}#if 0 /* debug only *//** * Test walking over all the entries in a hash table. */static voidtest_hash_walking(void){   struct _mesa_HashTable *t = _mesa_NewHashTable();   const GLuint limit = 50000;   GLuint i;   /* create some entries */   for (i = 0; i < limit; i++) {      GLuint dummy;      GLuint k = (rand() % (limit * 10)) + 1;      while (_mesa_HashLookup(t, k)) {         /* id already in use, try another */         k = (rand() % (limit * 10)) + 1;      }      _mesa_HashInsert(t, k, &dummy);   }   /* walk over all entries */   {      GLuint k = _mesa_HashFirstEntry(t);      GLuint count = 0;      while (k) {         GLuint knext = _mesa_HashNextEntry(t, k);         assert(knext != k);         _mesa_HashRemove(t, k);         count++;         k = knext;      }      assert(count == limit);      k = _mesa_HashFirstEntry(t);      assert(k==0);   }   _mesa_DeleteHashTable(t);}void_mesa_test_hash_functions(void){   int a, b, c;   struct _mesa_HashTable *t;   t = _mesa_NewHashTable();   _mesa_HashInsert(t, 501, &a);   _mesa_HashInsert(t, 10, &c);   _mesa_HashInsert(t, 0xfffffff8, &b);   /*_mesa_HashPrint(t);*/   assert(_mesa_HashLookup(t,501));   assert(!_mesa_HashLookup(t,1313));   assert(_mesa_HashFindFreeKeyBlock(t, 100));   _mesa_DeleteHashTable(t);   test_hash_walking();}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -