/*============================================================================= Name : hash.c Purpose : reasonably generic hash table using uints as keys Created 1/9/1998 by khent Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include "hash.h" #include "memory.h" #include "debug.h" /*----------------------------------------------------------------------------- Name : hashNewTable Description : creates a new hashtable structure and initializes all entries to NULL, sets maxkey to 0 Inputs : size - number of distinct entries in the hash table Outputs : Return : a freshly allocated hashtable structure ----------------------------------------------------------------------------*/ hashtable* hashNewTable(udword size) { int i; hashtable* table; table = (hashtable*)memAlloc((size-1) * sizeof(hash_t) + sizeof(hashtable), "hashtable", NonVolatile); table->size = size; table->maxkey = 0; for (i = 0; i < size; i++) { table->table[i] = NULL; } return table; } /*----------------------------------------------------------------------------- Name : hashDeleteTable Description : deletes a hashtable structure, freeing the memory used by the entry blocks but not what the blocks contain (because this is a generic hash) Inputs : table - the hashtable to delete Outputs : Return : ----------------------------------------------------------------------------*/ void hashDeleteTable(hashtable* table) { udword i; hash_t* entry; hash_t* next; dbgAssert(table != NULL); for (i = 0; i < table->size; i++) { entry = table->table[i]; while (entry) { next = entry->next; memFree(entry); entry = next; } } memFree(table); } /*----------------------------------------------------------------------------- Name : hashLookup Description : lookup given key in given hashtable Inputs : table - the hashtable to search key - the key to search for Outputs : Return : the entry corresponding to key, or NULL ----------------------------------------------------------------------------*/ void* hashLookup(hashtable const* table, udword key) { udword pos; hash_t* entry; dbgAssert(table != NULL); if (key == 0) return NULL; pos = key % table->size; entry = table->table[pos]; while (entry) { if (entry->key == key) { return entry->data; } entry = entry->next; } return NULL; } /*----------------------------------------------------------------------------- Name : hashInsert Description : insert an entry into given hashtable with given key Inputs : table - the hashtable key - the hash key (index) data - the data to be stored Outputs : Return : ----------------------------------------------------------------------------*/ void hashInsert(hashtable* table, udword key, void* data) { udword pos; hash_t* entry; dbgAssert(table != NULL); if (key == 0) return; if (key > table->maxkey) table->maxkey = key; pos = key % table->size; entry = table->table[pos]; while (entry) { if (entry->key == key) { /* replace entry's data. user should really avoid this and free collisions manually before inserting to avoid unreferenced but allocated memory */ entry->data = data; return; } entry = entry->next; } /* alloc and insert new table entry */ entry = (hash_t*)memAlloc(sizeof(hash_t), "hash entry", NonVolatile); entry->key = key; entry->data = data; entry->next = table->table[pos]; table->table[pos] = entry; } /*----------------------------------------------------------------------------- Name : hashRemove Description : remove an entry from given hashtable Inputs : table - the hashtable key - the key of the entry to remove Outputs : table is modified to not contain the entry if it was found, and the entry block is freed (but not the data it contains) Return : ----------------------------------------------------------------------------*/ void hashRemove(hashtable* table, udword key) { udword pos; hash_t *entry, *prev; dbgAssert(table != NULL); dbgAssert(key != 0); pos = key % table->size; prev = NULL; entry = table->table[pos]; while (entry) { if (entry->key == key) { if (prev) { prev->next = entry->next; } else { table->table[pos] = entry->next; } memFree(entry); return; } prev = entry; entry = entry->next; } } /*----------------------------------------------------------------------------- Name : hashFindFreeKeyBlock Description : searches thru a hashtable for a block numkeys in length of free keys. glGenTextures uses this fn, for instance Inputs : table - the hashtable numkeys - length of the block Outputs : Return : the starting key index, or 0 if failure ----------------------------------------------------------------------------*/ udword hashFindFreeKeyBlock(hashtable* table, udword numkeys) { udword maxkey = (udword)(~0); if (maxkey - numkeys > table->maxkey) { /* the quick solution */ return table->maxkey + 1; } else { /* the slow solution */ udword freecount = 0; udword freestart = 0; udword key; for (key = 0; key != maxkey; key++) { if (hashLookup(table, key)) { /* key already in use */ freecount = 0; freestart = key + 1; } else { /* this key not in use, maybe we've found enough */ freecount++; if (freecount == numkeys) { return freestart; } } } /* cannot allocate a block of numkeys consecutive keys */ return 0; } }