407 lines
10 KiB
C++
407 lines
10 KiB
C++
/*
|
|
This file is part of Warzone 2100.
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
|
Copyright (C) 2005-2010 Warzone 2100 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 <limits.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
|
|
#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; i<psTable->udwTableSize; 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 = (HASHNODE *)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 );
|
|
}
|
|
|
|
/***************************************************************************/
|