jedi-academy/code/Ratl/hash_pool_vs.h
2013-04-23 15:21:39 +10:00

200 lines
No EOL
6 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
// RAVEN STANDARD TEMPLATE LIBRARY
// (c) 2002 Activision
//
//
// Hash Pool
// ---------
// The hash pool stores raw data of variable size. It uses a hash table to check for
// redundant data, and upon finding any, will return the existing handle. Otherwise
// it copies the data to memory and returns a new handle.
//
//
// NOTES:
//
//
//
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RATL_HASH_POOL_VS_INC)
#define RATL_HASH_POOL_VS_INC
////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RATL_COMMON_INC)
#include "ratl_common.h"
#endif
namespace ratl
{
////////////////////////////////////////////////////////////////////////////////////////
// The Hash Pool
////////////////////////////////////////////////////////////////////////////////////////
template <int SIZE, int SIZE_HANDLES>
class hash_pool
{
int mHandles[SIZE_HANDLES]; // each handle holds the start index of it's data
int mDataAlloc; // where the next chunck of data will go
char mData[SIZE];
#ifdef _DEBUG
int mFinds; // counts how many total finds have run
int mCurrentCollisions; // counts how many collisions on the last find
int mTotalCollisions; // counts the total number of collisions
int mTotalAllocs;
#endif
////////////////////////////////////////////////////////////////////////////////////
// This function searches for a handle which already stores the data (assuming the
// handle is a hash within range SIZE_HANDLES).
//
// If it failes, it returns false, and the handle passed in points to the next
// free slot.
////////////////////////////////////////////////////////////////////////////////////
bool find_existing(int& handle, const void* data, int datasize)
{
#ifdef _DEBUG
mFinds++;
mCurrentCollisions = 0;
#endif
while (mHandles[handle]) // So long as a handle exists there
{
if (mem::eql((void*)(&mData[mHandles[handle]]), data, datasize))
{
return true; // found
}
handle=(handle+1)&(SIZE_HANDLES-1); // incriment the handle
#ifdef _DEBUG
mCurrentCollisions ++;
mTotalCollisions ++;
//assert(mCurrentCollisions < 16); // If We Had 16+ Collisions, Hash May Be Inefficient.
// Evaluate SIZE and SIZEHANDLES
#endif
}
return false; // failed to find
}
////////////////////////////////////////////////////////////////////////////////////
// A simple hash function for the range of [0, SIZE_HANDLES]
////////////////////////////////////////////////////////////////////////////////////
int hash(const void* data, int datasize)
{
int h=0;
for (int i=0; i<datasize; i++)
{
h += ((const char*)(data))[i] * (i + 119); // 119. Prime Number?
}
h &= SIZE_HANDLES - 1; // zero out bits beyoned SIZE_HANDLES
return h;
}
public:
hash_pool()
{
clear();
}
////////////////////////////////////////////////////////////////////////////////////
// The Number Of Bytes Allocated
////////////////////////////////////////////////////////////////////////////////////
int size() const
{
return mDataAlloc;
}
////////////////////////////////////////////////////////////////////////////////////
// Check To See If This Memory Pool Is Empty
////////////////////////////////////////////////////////////////////////////////////
bool empty() const
{
return (mDataAlloc==1);
}
////////////////////////////////////////////////////////////////////////////////////
// Check To See If This Memory Pool Has Enough Space Left For (minimum) Bytes
////////////////////////////////////////////////////////////////////////////////////
bool full(int minimum) const
{
return ((SIZE - mDataAlloc)<minimum);
}
////////////////////////////////////////////////////////////////////////////////////
// Clear - Removes all allocation information - Note! DOES NOT CLEAR MEMORY
////////////////////////////////////////////////////////////////////////////////////
void clear()
{
mData[0] = 0;
mDataAlloc = 1;
for (int i=0; i<SIZE_HANDLES; i++)
{
mHandles[i] = 0;
}
#ifdef _DEBUG
mFinds = 0;
mCurrentCollisions = 0;
mTotalCollisions = 0;
mTotalAllocs = 0;
#endif
}
////////////////////////////////////////////////////////////////////////////////////
// This is the primary functionality of the hash pool. It will search for existing
// data of the same size, and failing to find any, it will append the data to the
// memory.
//
// In both cases, it gives you a handle to look up the data later.
////////////////////////////////////////////////////////////////////////////////////
int get_handle(const void* data, int datasize)
{
int handle = hash(data, datasize); // Initialize Our Handle By Hash Fcn
if (!find_existing(handle, data, datasize))
{
assert(mDataAlloc+datasize < SIZE); // Is There Enough Memory?
#ifdef _DEBUG
mTotalAllocs++;
#endif
mem::cpy((void*)(&mData[mDataAlloc]), data, datasize);// Copy Data To Memory
mHandles[handle] = mDataAlloc; // Mark Memory In Hash Tbl
mDataAlloc += datasize; // Adjust Next Alloc Location
}
return handle; // Return The Hash Tbl handleess
}
////////////////////////////////////////////////////////////////////////////////////
// Constant Access Operator
////////////////////////////////////////////////////////////////////////////////////
const void* operator[](int handle) const
{
assert(handle>=0 && handle<SIZE_HANDLES);
return &(mData[mHandles[handle]]);
}
#ifdef _DEBUG
float average_collisions() {return ((float)mTotalCollisions / (float)mFinds);}
int total_allocs() {return mTotalAllocs;}
int total_finds() {return mFinds;}
int total_collisions() {return mTotalCollisions;}
#endif
};
}
#endif