/** * \file hash.c * Generic hash table. * * This code taken from Mesa and adapted. */ #include #include #include #include "eglhash.h" #define TABLE_SIZE 1023 /**< Size of lookup table/array */ #define HASH_FUNC(K) ((K) % TABLE_SIZE) /* * Unfinished mutex stuff */ typedef int _EGLMutex; static void _eglInitMutex(_EGLMutex m) { } static void _eglDestroyMutex(_EGLMutex m) { } static void _eglLockMutex(_EGLMutex m) { } static void _eglUnlockMutex(_EGLMutex m) { } typedef struct _egl_hashentry _EGLHashentry; struct _egl_hashentry { EGLuint Key; /**< the entry's key */ void *Data; /**< the entry's data */ _EGLHashentry *Next; /**< pointer to next entry */ }; struct _egl_hashtable { _EGLHashentry *Table[TABLE_SIZE]; /**< the lookup table */ EGLuint MaxKey; /**< highest key inserted so far */ _EGLMutex Mutex; /**< mutual exclusion lock */ }; /** * Create a new hash table. * * \return pointer to a new, empty hash table. */ _EGLHashtable * _eglNewHashTable(void) { _EGLHashtable *table = (_EGLHashtable *) calloc(1, sizeof(_EGLHashtable)); if (table) { _eglInitMutex(table->Mutex); table->MaxKey = 1; } 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 _eglDeleteHashTable(_EGLHashtable *table) { EGLuint i; assert(table); for (i = 0; i < TABLE_SIZE; i++) { _EGLHashentry *entry = table->Table[i]; while (entry) { _EGLHashentry *next = entry->Next; free(entry); entry = next; } } _eglDestroyMutex(table->Mutex); 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 * _eglHashLookup(const _EGLHashtable *table, EGLuint key) { EGLuint pos; const _EGLHashentry *entry; assert(table); if (!key) return NULL; 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 _eglHashInsert(_EGLHashtable *table, EGLuint key, void *data) { /* search for existing entry with this key */ EGLuint pos; _EGLHashentry *entry; assert(table); assert(key); _eglLockMutex(table->Mutex); if (key > table->MaxKey) table->MaxKey = key; pos = HASH_FUNC(key); entry = table->Table[pos]; while (entry) { if (entry->Key == key) { /* replace entry's data */ entry->Data = data; _eglUnlockMutex(table->Mutex); return; } entry = entry->Next; } /* alloc and insert new table entry */ entry = (_EGLHashentry *) malloc(sizeof(_EGLHashentry)); entry->Key = key; entry->Data = data; entry->Next = table->Table[pos]; table->Table[pos] = entry; _eglUnlockMutex(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 _eglHashRemove(_EGLHashtable *table, EGLuint key) { EGLuint pos; _EGLHashentry *entry, *prev; assert(table); assert(key); _eglLockMutex(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; } free(entry); _eglUnlockMutex(table->Mutex); return; } prev = entry; entry = entry->Next; } _eglUnlockMutex(table->Mutex); } /** * Get the key of the "first" entry in the hash table. * * This is used in the course of deleting all display lists when * a context is destroyed. * * \param table the hash table * * \return key for 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. */ EGLuint _eglHashFirstEntry(_EGLHashtable *table) { EGLuint pos; assert(table); _eglLockMutex(table->Mutex); for (pos = 0; pos < TABLE_SIZE; pos++) { if (table->Table[pos]) { _eglUnlockMutex(table->Mutex); return table->Table[pos]->Key; } } _eglUnlockMutex(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. */ EGLuint _eglHashNextEntry(const _EGLHashtable *table, EGLuint key) { const _EGLHashentry *entry; EGLuint pos; assert(table); assert(key); /* Find the entry with given key */ pos = HASH_FUNC(key); entry = table->Table[pos]; while (entry) { if (entry->Key == key) { break; } entry = entry->Next; } if (!entry) { /* the key was not found, we can't find 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 _eglHashPrint(const _EGLHashtable *table) { EGLuint i; assert(table); for (i = 0; i < TABLE_SIZE; i++) { const _EGLHashentry *entry = table->Table[i]; while (entry) { printf("%u %p\n", entry->Key, entry->Data); entry = entry->Next; } } } /** * Return a new, unused hash key. */ EGLuint _eglHashGenKey(_EGLHashtable *table) { EGLuint k; _eglLockMutex(table->Mutex); k = table->MaxKey; table->MaxKey++; _eglUnlockMutex(table->Mutex); return k; }