mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-21 17:54:48 +01:00
358 lines
11 KiB
C++
358 lines
11 KiB
C++
/*****************************************************************************/
|
|
/* Map.h Copyright (c) Ladislav Zezula 2014 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Interface of hash-to-ptr map for CascLib */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 10.06.14 1.00 Lad The first version of Map.h */
|
|
/*****************************************************************************/
|
|
|
|
#ifndef __CASC_MAP_H__
|
|
#define __CASC_MAP_H__
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Structures
|
|
|
|
#define MIN_HASH_TABLE_SIZE 0x00000100 // The smallest size of the hash table.
|
|
#define MAX_HASH_TABLE_SIZE 0x00800000 // The largest size of the hash table. Should be enough for any game.
|
|
|
|
typedef int (*PFNCOMPAREFUNC)(const void * pvObjectKey, const void * pvKey, size_t nKeyLength);
|
|
typedef DWORD (*PFNHASHFUNC)(void * pvKey, size_t nKeyLength);
|
|
|
|
typedef enum _KEY_TYPE
|
|
{
|
|
KeyIsHash, // Use when the key is already a hash. If specified, the map will not hash the key
|
|
KeyIsArbitrary, // The key is an arbitrary array of bytes. The map will hash it to get a map index
|
|
KeyIsString // Use when the key is a string
|
|
} KEY_TYPE, *PKEY_TYPE;
|
|
|
|
#define KEY_LENGTH_STRING 0xFFFFFFFF
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Hashing functions
|
|
|
|
// Used when the key is a hash already - no need to hash it again
|
|
inline DWORD CalcHashValue_Hash(void * pvKey, size_t /* nKeyLength */)
|
|
{
|
|
// Get the hash directly as value
|
|
return ConvertBytesToInteger_4((LPBYTE)pvKey);
|
|
}
|
|
|
|
// Calculates hash value from a key
|
|
inline DWORD CalcHashValue_Key(void * pvKey, size_t nKeyLength)
|
|
{
|
|
LPBYTE pbKey = (LPBYTE)pvKey;
|
|
DWORD dwHash = 0x7EEE7EEE;
|
|
|
|
// Construct the hash from the key
|
|
for(DWORD i = 0; i < nKeyLength; i++)
|
|
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[i];
|
|
|
|
// Return the hash limited by the table size
|
|
return dwHash;
|
|
}
|
|
|
|
// Calculates hash value from a string
|
|
inline DWORD CalcHashValue_String(const char * szString, const char * szStringEnd)
|
|
{
|
|
LPBYTE pbKeyEnd = (LPBYTE)szStringEnd;
|
|
LPBYTE pbKey = (LPBYTE)szString;
|
|
DWORD dwHash = 0x7EEE7EEE;
|
|
|
|
// Hash the string itself
|
|
while(pbKey < pbKeyEnd)
|
|
{
|
|
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]];
|
|
pbKey++;
|
|
}
|
|
|
|
// Return the hash limited by the table size
|
|
return dwHash;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Map implementation
|
|
|
|
class CASC_MAP
|
|
{
|
|
public:
|
|
|
|
CASC_MAP()
|
|
{
|
|
PfnCalcHashValue = NULL;
|
|
m_HashTable = NULL;
|
|
m_HashTableSize = 0;
|
|
m_ItemCount = 0;
|
|
m_KeyOffset = 0;
|
|
m_KeyLength = 0;
|
|
m_bKeyIsHash = false;
|
|
}
|
|
|
|
~CASC_MAP()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
DWORD Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash)
|
|
{
|
|
// Set the class variables
|
|
m_KeyLength = CASCLIB_MAX(KeyLength, 8);
|
|
m_KeyOffset = KeyOffset;
|
|
m_ItemCount = 0;
|
|
|
|
// Setup the hashing function
|
|
switch(KeyType)
|
|
{
|
|
case KeyIsHash:
|
|
PfnCalcHashValue = CalcHashValue_Hash;
|
|
break;
|
|
|
|
case KeyIsArbitrary:
|
|
PfnCalcHashValue = CalcHashValue_Key;
|
|
break;
|
|
|
|
case KeyIsString:
|
|
PfnCalcHashValue = NULL;
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Calculate the hash table size. Take 133% of the item count and round it up to the next power of two
|
|
// This will make the hash table indexes somewhat more resilient against count changes and will make
|
|
// e.g. file order in the file tree more stable.
|
|
m_HashTableSize = GetNearestPowerOfTwo(MaxItems * 4 / 3);
|
|
if(m_HashTableSize == 0)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
// Allocate new map for the objects
|
|
m_HashTable = (void **)CASC_ALLOC_ZERO<void *>(m_HashTableSize);
|
|
return (m_HashTable != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
void * FindObject(void * pvKey, PDWORD PtrIndex = NULL)
|
|
{
|
|
void * pvObject;
|
|
DWORD dwHashIndex;
|
|
|
|
// Verify pointer to the map
|
|
if(m_HashTable != NULL)
|
|
{
|
|
// Construct the hash index
|
|
dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
|
|
|
|
// Search the hash table
|
|
while((pvObject = m_HashTable[dwHashIndex]) != NULL)
|
|
{
|
|
// Compare the hash
|
|
if(CompareObject_Key(pvObject, pvKey))
|
|
{
|
|
if(PtrIndex != NULL)
|
|
PtrIndex[0] = dwHashIndex;
|
|
return pvObject;
|
|
}
|
|
|
|
// Move to the next entry
|
|
dwHashIndex = HashToIndex(dwHashIndex + 1);
|
|
}
|
|
}
|
|
|
|
// Not found, sorry
|
|
return NULL;
|
|
}
|
|
|
|
bool InsertObject(void * pvNewObject, void * pvKey)
|
|
{
|
|
void * pvExistingObject;
|
|
DWORD dwHashIndex;
|
|
|
|
// Verify pointer to the map
|
|
if(m_HashTable != NULL)
|
|
{
|
|
// Limit check
|
|
if((m_ItemCount + 1) >= m_HashTableSize)
|
|
return false;
|
|
|
|
// Construct the hash index
|
|
dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
|
|
|
|
// Search the hash table
|
|
while((pvExistingObject = m_HashTable[dwHashIndex]) != NULL)
|
|
{
|
|
// Check if hash being inserted conflicts with an existing hash
|
|
if(CompareObject_Key(pvExistingObject, pvKey))
|
|
return false;
|
|
|
|
// Move to the next entry
|
|
dwHashIndex = HashToIndex(dwHashIndex + 1);
|
|
}
|
|
|
|
// Insert at that position
|
|
m_HashTable[dwHashIndex] = pvNewObject;
|
|
m_ItemCount++;
|
|
return true;
|
|
}
|
|
|
|
// Failed
|
|
return false;
|
|
}
|
|
|
|
const char * FindString(const char * szString, const char * szStringEnd)
|
|
{
|
|
const char * szExistingString;
|
|
DWORD dwHashIndex;
|
|
|
|
// Verify pointer to the map
|
|
if(m_HashTable != NULL)
|
|
{
|
|
// Construct the main index
|
|
dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
|
|
|
|
// Search the hash table
|
|
while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
|
|
{
|
|
// Compare the hash
|
|
if(CompareObject_String(szExistingString, szString, szStringEnd))
|
|
return szExistingString;
|
|
|
|
// Move to the next entry
|
|
dwHashIndex = HashToIndex(dwHashIndex + 1);
|
|
}
|
|
}
|
|
|
|
// Not found, sorry
|
|
return NULL;
|
|
}
|
|
|
|
bool InsertString(const char * szString, bool bCutExtension)
|
|
{
|
|
const char * szExistingString;
|
|
const char * szStringEnd = NULL;
|
|
DWORD dwHashIndex;
|
|
|
|
// Verify pointer to the map
|
|
if(m_HashTable != NULL)
|
|
{
|
|
// Limit check
|
|
if((m_ItemCount + 1) >= m_HashTableSize)
|
|
return false;
|
|
|
|
// Retrieve the length of the string without extension
|
|
if(bCutExtension)
|
|
szStringEnd = GetFileExtension(szString);
|
|
else
|
|
szStringEnd = szString + strlen(szString);
|
|
|
|
// Construct the hash index
|
|
dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
|
|
|
|
// Search the hash table
|
|
while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
|
|
{
|
|
// Check if hash being inserted conflicts with an existing hash
|
|
if(CompareObject_String(szExistingString, szString, szStringEnd))
|
|
return false;
|
|
|
|
// Move to the next entry
|
|
dwHashIndex = HashToIndex(dwHashIndex + 1);
|
|
}
|
|
|
|
// Insert at that position
|
|
m_HashTable[dwHashIndex] = (void *)szString;
|
|
m_ItemCount++;
|
|
return true;
|
|
}
|
|
|
|
// Failed
|
|
return false;
|
|
}
|
|
|
|
void * ItemAt(size_t nIndex)
|
|
{
|
|
assert(nIndex < m_HashTableSize);
|
|
return m_HashTable[nIndex];
|
|
}
|
|
|
|
size_t HashTableSize()
|
|
{
|
|
return m_HashTableSize;
|
|
}
|
|
|
|
size_t ItemCount()
|
|
{
|
|
return m_ItemCount;
|
|
}
|
|
|
|
bool IsInitialized()
|
|
{
|
|
return (m_HashTable && m_HashTableSize);
|
|
}
|
|
|
|
void Free()
|
|
{
|
|
PfnCalcHashValue = NULL;
|
|
CASC_FREE(m_HashTable);
|
|
m_HashTableSize = 0;
|
|
}
|
|
|
|
protected:
|
|
|
|
DWORD HashToIndex(DWORD HashValue)
|
|
{
|
|
return HashValue & (m_HashTableSize - 1);
|
|
}
|
|
|
|
bool CompareObject_Key(void * pvObject, void * pvKey)
|
|
{
|
|
LPBYTE pbObjectKey = (LPBYTE)pvObject + m_KeyOffset;
|
|
return (memcmp(pbObjectKey, pvKey, m_KeyLength) == 0);
|
|
}
|
|
|
|
bool CompareObject_String(const char * szExistingString, const char * szString, const char * szStringEnd)
|
|
{
|
|
// Compare the whole part, case insensitive
|
|
while(szString < szStringEnd)
|
|
{
|
|
if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
|
|
return false;
|
|
|
|
szExistingString++;
|
|
szString++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t GetNearestPowerOfTwo(size_t MaxItems)
|
|
{
|
|
size_t PowerOfTwo;
|
|
|
|
// Round the hash table size up to the nearest power of two
|
|
for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo <= MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1)
|
|
{
|
|
if(PowerOfTwo > MaxItems)
|
|
{
|
|
return PowerOfTwo;
|
|
}
|
|
}
|
|
|
|
// If the hash table is too big, we cannot create the map
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
PFNHASHFUNC PfnCalcHashValue;
|
|
void ** m_HashTable; // Hash table
|
|
size_t m_HashTableSize; // Size of the hash table, in entries. Always a power of two.
|
|
size_t m_ItemCount; // Number of objects in the map
|
|
size_t m_KeyOffset; // How far is the hash from the begin of the objects (in bytes)
|
|
size_t m_KeyLength; // Length of the hash key, in bytes
|
|
bool m_bKeyIsHash; // If set, then it means that the key is a hash of some sort.
|
|
// Will improve performance, as we will not hash a hash :-)
|
|
};
|
|
|
|
#endif // __CASC_MAP_H__
|