/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2009 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /***************************************************************************/ #include #include #include #include "lib/framework/frame.h" #include "hashtable.h" /***************************************************************************/ static UDWORD HashTest(intptr_t iKey1, intptr_t iKey2) { return (UDWORD) iKey1 + iKey2; } /***************************************************************************/ BOOL hashTable_Create( HASHTABLE **ppsTable, UDWORD udwTableSize, UDWORD udwInitElements, UDWORD udwExtElements, UDWORD udwElementSize ) { UDWORD udwSize; /* allocate and init table */ (*ppsTable) = (HASHTABLE*)malloc( sizeof(HASHTABLE) ); udwSize = udwTableSize * sizeof(HASHNODE *); (*ppsTable)->ppsNode = (HASHNODE**)malloc( udwSize ); memset( (*ppsTable)->ppsNode, 0, udwSize ); /* init members */ (*ppsTable)->udwTableSize = udwTableSize; (*ppsTable)->udwElements = udwInitElements; (*ppsTable)->udwExtElements = udwExtElements; (*ppsTable)->udwElementSize = udwElementSize; (*ppsTable)->sdwCurIndex = 0; (*ppsTable)->psNextNode = (*ppsTable)->ppsNode[0]; (*ppsTable)->pFreeFunc = NULL; /* set hash function to internal */ hashTable_SetHashFunction( (*ppsTable), HashTest ); return true; } /***************************************************************************/ void hashTable_Destroy( HASHTABLE *psTable ) { ASSERT( psTable != NULL, "hashTable_Destroy: table pointer invalid\n" ); hashTable_Clear( psTable ); /* free table */ free( psTable->ppsNode ); free( psTable ); } /***************************************************************************/ /* * hashTable_Clear * * Returns all nodes from hash table to free node list */ /***************************************************************************/ void hashTable_Clear( HASHTABLE *psTable ) { HASHNODE *psNode, *psNodeTmp; UDWORD i; ASSERT( psTable != NULL, "hashTable_Destroy: table pointer invalid\n" ); /* free nodes */ for ( i=0; iudwTableSize; i++ ) { /* free table entry nodelist */ psNode = psTable->ppsNode[i]; while ( psNode != NULL ) { /* return node element to heap */ ASSERT( psNode->psElement != NULL, "hashTable_Destroy: element pointer invalid\n" ); /* do free-element callback if set */ if ( psTable->pFreeFunc != NULL ) { (psTable->pFreeFunc) (psNode->psElement); } /* free element */ free(psNode->psElement); /* return node to heap */ ASSERT( psNode != NULL, "hashTable_Destroy: node pointer invalid\n" ); psNodeTmp = psNode->psNext; free(psNode); psNode = psNodeTmp; } /* set table entry to NULL */ psTable->ppsNode[i] = NULL; } } /***************************************************************************/ void hashTable_SetHashFunction( HASHTABLE *psTable, HASHFUNC pHashFunc ) { ASSERT( psTable != NULL, "hashTable_SetHashFunction: table pointer invalid\n" ); psTable->pHashFunc = pHashFunc; } /***************************************************************************/ void hashTable_SetFreeElementFunction( HASHTABLE *psTable, HASHFREEFUNC pFreeFunc ) { ASSERT( psTable != NULL, "hashTable_SetFreeElementFunction: table pointer invalid\n" ); psTable->pFreeFunc = pFreeFunc; } /***************************************************************************/ /* * hashTable_GetElement * * Gets free node from heap and returns element pointer */ /***************************************************************************/ void * hashTable_GetElement( HASHTABLE *psTable ) { void *psElement; ASSERT( psTable != NULL, "hashTable_GetElement: table pointer invalid\n" ); psElement = malloc(psTable->udwElementSize); if (psElement == NULL) // if the alloc fails then return NULL { debug(LOG_ERROR, "hashTable_GetElement: Out of memory"); return NULL; } return psElement; } /***************************************************************************/ static UDWORD hashTable_GetHashKey(HASHTABLE *psTable, intptr_t iKey1, intptr_t iKey2) { ASSERT( psTable != NULL, "hashTable_GetFirst: hash table pointer invalid\n" ); /* get hashed index */ return (psTable->pHashFunc) ( iKey1, iKey2 ) % psTable->udwTableSize; } /***************************************************************************/ void hashTable_InsertElement(HASHTABLE *psTable, void *psElement, intptr_t iKey1, intptr_t iKey2) { UDWORD udwHashIndex; HASHNODE *psNode; ASSERT( psTable != NULL, "hashTable_InsertElement: table pointer invalid\n" ); ASSERT( psElement != NULL, "hashTable_InsertElement: element pointer invalid\n" ); /* get hashed index */ udwHashIndex = hashTable_GetHashKey( psTable, iKey1, iKey2 ); /* get node from heap */ psNode = malloc(sizeof(HASHNODE)); /* set node elements */ psNode->iKey1 = iKey1; psNode->iKey2 = iKey2; psNode->psElement = psElement; /* add new node to head of list */ psNode->psNext = psTable->ppsNode[udwHashIndex]; psTable->ppsNode[udwHashIndex] = psNode; } /***************************************************************************/ /* * hashTable_FindElement * * Calculates hash index from keys and returns element in hash table */ /***************************************************************************/ void *hashTable_FindElement(HASHTABLE *psTable, intptr_t iKey1, intptr_t iKey2) { UDWORD udwHashIndex; HASHNODE *psNode; ASSERT( psTable != NULL, "hashTable_FindElement: table pointer invalid\n" ); /* get hashed index */ udwHashIndex = hashTable_GetHashKey( psTable, iKey1, iKey2 ); psNode = psTable->ppsNode[udwHashIndex]; /* loop through node list to find element match */ while ( psNode != NULL && !( psNode->iKey1 == iKey1 && psNode->iKey2 == iKey2) ) { psNode = psNode->psNext; } /* remove node from hash table and return to heap */ if ( psNode == NULL ) { return false; } else { return psNode->psElement; } } /***************************************************************************/ static void hashTable_SetNextNode( HASHTABLE *psTable, BOOL bMoveToNextNode ) { if ( (bMoveToNextNode == true) && (psTable->psNextNode != NULL) ) { /* get next node */ psTable->psNextNode = psTable->psNextNode->psNext; /* if next node NULL increment index */ if ( psTable->psNextNode == NULL ) { psTable->sdwCurIndex++; } } /* search through table for next allocated node */ while ( psTable->sdwCurIndex < psTable->udwTableSize && psTable->psNextNode == NULL ) { psTable->psNextNode = psTable->ppsNode[psTable->sdwCurIndex]; if ( psTable->psNextNode == NULL ) { psTable->sdwCurIndex++; } } /* reset pointer if table overrun */ if ( psTable->sdwCurIndex >= psTable->udwTableSize ) { psTable->psNextNode = NULL; } } /***************************************************************************/ BOOL hashTable_RemoveElement(HASHTABLE *psTable, void *psElement, intptr_t iKey1, intptr_t iKey2) { UDWORD udwHashIndex; HASHNODE *psNode, *psPrev; ASSERT( psTable != NULL, "hashTable_RemoveElement: table pointer invalid\n" ); /* get hashed index */ udwHashIndex = hashTable_GetHashKey( psTable, iKey1, iKey2 ); /* init previous node pointer */ psPrev = NULL; /* get first node in table slot */ psNode = psTable->ppsNode[udwHashIndex]; /* loop through node list to find element match */ while ( psNode != NULL && !( psElement == psNode->psElement && psNode->iKey1 == iKey1 && psNode->iKey2 == iKey2) ) { psPrev = psNode; psNode = psNode->psNext; } /* remove node from hash table and return to heap */ if ( psNode == NULL ) { return false; } else { /* remove from hash table */ if ( psPrev == NULL ) { psTable->ppsNode[udwHashIndex] = psNode->psNext; } else { psPrev->psNext = psNode->psNext; } /* set next node pointer to this one if necessary */ if ( psTable->psNextNode == psNode ) { psTable->psNextNode = psPrev; } /* setup next node pointer */ hashTable_SetNextNode( psTable, true ); /* return element to heap */ ASSERT( psNode->psElement != NULL, "hashTable_RemoveElement: element pointer invalid\n" ); free(psNode->psElement); /* return node to heap */ ASSERT( psNode != NULL, "hashTable_RemoveElement: node pointer invalid\n" ); free(psNode); return true; } } /***************************************************************************/ void * hashTable_GetNext( HASHTABLE *psTable ) { void *psElement; ASSERT( psTable != NULL, "hashTable_GetNext: hash table pointer invalid\n" ); if ( psTable->psNextNode == NULL ) { return NULL; } else { psElement = psTable->psNextNode->psElement; /* setup next node pointer */ hashTable_SetNextNode( psTable, true ); return psElement; } } /***************************************************************************/ void * hashTable_GetFirst( HASHTABLE *psTable ) { ASSERT( psTable != NULL, "hashTable_GetFirst: hash table pointer invalid\n" ); /* init current index and node to start of table */ psTable->sdwCurIndex = 0; psTable->psNextNode = psTable->ppsNode[0]; /* search through table for first allocated node */ hashTable_SetNextNode( psTable, false ); /* return it */ return hashTable_GetNext( psTable ); } /***************************************************************************/