Seaside/SpyCustom/stringpool.h
2021-06-16 16:13:22 +03:00

465 lines
11 KiB
C++

#ifndef STRINGPOOL_H
#define STRINGPOOL_H
#if defined( _WIN32 )
#pragma once
#endif
#include "utlrbtree.h"
#include "utlvector.h"
#include "utlbuffer.h"
#include "generichash.h"
enum StringPoolCase_t
{
StringPoolCaseInsensitive,
StringPoolCaseSensitive
};
class CStringPool
{
public:
CStringPool(StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive);
~CStringPool();
unsigned int Count() const;
const char* Allocate(const char* pszValue);
void FreeAll();
const char* Find(const char* pszValue);
protected:
typedef CUtlRBTree<const char*, unsigned short> CStrSet;
CStrSet m_Strings;
};
template<class T>
class CCountedStringPoolBase
{
public:
struct hash_item_t
{
char* pString;
T nNextElement;
unsigned char nReferenceCount;
unsigned char pad;
};
enum
{
INVALID_ELEMENT = 0,
MAX_REFERENCE = 0xFF,
HASH_TABLE_SIZE = 1024
};
CUtlVector<T> m_HashTable;
CUtlVector<hash_item_t> m_Elements;
T m_FreeListStart;
StringPoolCase_t m_caseSensitivity;
public:
CCountedStringPoolBase(StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive);
virtual ~CCountedStringPoolBase();
void FreeAll();
char* FindString(const char* pIntrinsic);
char* ReferenceString(const char* pIntrinsic);
void DereferenceString(const char* pIntrinsic);
T FindStringHandle(const char* pIntrinsic);
T ReferenceStringHandle(const char* pIntrinsic);
char* HandleToString(T handle);
void SpewStrings();
unsigned Hash(const char* pszKey);
bool SaveToBuffer(CUtlBuffer& buffer);
bool RestoreFromBuffer(CUtlBuffer& buffer);
void VerifyNotOverflowed(unsigned int value);
};
typedef CCountedStringPoolBase<unsigned short> CCountedStringPool;
template<class T>
inline CCountedStringPoolBase<T>::CCountedStringPoolBase(StringPoolCase_t caseSensitivity)
{
MEM_ALLOC_CREDIT();
m_HashTable.EnsureCount(HASH_TABLE_SIZE);
for (int i = 0; i < m_HashTable.Count(); i++)
{
m_HashTable[i] = INVALID_ELEMENT;
}
m_FreeListStart = INVALID_ELEMENT;
m_Elements.AddToTail();
m_Elements[0].pString = NULL;
m_Elements[0].nReferenceCount = 0;
m_Elements[0].nNextElement = INVALID_ELEMENT;
m_caseSensitivity = caseSensitivity;
}
template<class T>
inline CCountedStringPoolBase<T>::~CCountedStringPoolBase()
{
FreeAll();
}
template<class T>
inline void CCountedStringPoolBase<T>::FreeAll()
{
int i;
for (i = 0; i < m_HashTable.Count(); i++)
{
m_HashTable[i] = INVALID_ELEMENT;
}
m_FreeListStart = INVALID_ELEMENT;
for (i = 0; i < m_Elements.Count(); i++)
{
if (m_Elements[i].pString)
{
delete[] m_Elements[i].pString;
m_Elements[i].pString = NULL;
m_Elements[i].nReferenceCount = 0;
m_Elements[i].nNextElement = INVALID_ELEMENT;
}
}
m_Elements.RemoveAll();
m_Elements.AddToTail();
m_Elements[0].pString = NULL;
m_Elements[0].nReferenceCount = 0;
m_Elements[0].nNextElement = INVALID_ELEMENT;
}
template<class T>
inline unsigned CCountedStringPoolBase<T>::Hash(const char* pszKey)
{
if (m_caseSensitivity == StringPoolCaseInsensitive)
{
return HashStringCaseless(pszKey);
}
return HashString(pszKey);
}
template<class T>
inline T CCountedStringPoolBase<T>::FindStringHandle(const char* pIntrinsic)
{
if (pIntrinsic == NULL)
return INVALID_ELEMENT;
T nHashBucketIndex = (Hash(pIntrinsic) % HASH_TABLE_SIZE);
T nCurrentBucket = m_HashTable[nHashBucketIndex];
if (nCurrentBucket != INVALID_ELEMENT)
{
for (; nCurrentBucket != INVALID_ELEMENT; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement)
{
if (!Q_stricmp(pIntrinsic, m_Elements[nCurrentBucket].pString))
{
return nCurrentBucket;
}
}
}
return 0;
}
template<class T>
inline char* CCountedStringPoolBase<T>::FindString(const char* pIntrinsic)
{
if (pIntrinsic == NULL)
return NULL;
return m_Elements[FindStringHandle(pIntrinsic)].pString;
}
template<class T>
inline T CCountedStringPoolBase<T>::ReferenceStringHandle(const char* pIntrinsic)
{
if (pIntrinsic == NULL)
return INVALID_ELEMENT;
T nHashBucketIndex = (Hash(pIntrinsic) % HASH_TABLE_SIZE);
T nCurrentBucket = m_HashTable[nHashBucketIndex];
if (nCurrentBucket != INVALID_ELEMENT)
{
for (; nCurrentBucket != INVALID_ELEMENT; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement)
{
if (!Q_stricmp(pIntrinsic, m_Elements[nCurrentBucket].pString))
{
if (m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE)
{
m_Elements[nCurrentBucket].nReferenceCount++;
}
return nCurrentBucket;
}
}
}
if (m_FreeListStart != INVALID_ELEMENT)
{
nCurrentBucket = m_FreeListStart;
m_FreeListStart = m_Elements[nCurrentBucket].nNextElement;
}
else
{
unsigned int newElement = m_Elements.AddToTail();
VerifyNotOverflowed(newElement);
nCurrentBucket = newElement;
}
m_Elements[nCurrentBucket].nReferenceCount = 1;
m_Elements[nCurrentBucket].nNextElement = m_HashTable[nHashBucketIndex];
m_HashTable[nHashBucketIndex] = nCurrentBucket;
m_Elements[nCurrentBucket].pString = new char[Q_strlen(pIntrinsic) + 1];
Q_strcpy(m_Elements[nCurrentBucket].pString, pIntrinsic);
return nCurrentBucket;
}
template<>
inline void CCountedStringPoolBase<unsigned short>::VerifyNotOverflowed(unsigned int value) { Assert(value < 0xffff); }
template<>
inline void CCountedStringPoolBase<unsigned int>::VerifyNotOverflowed(unsigned int value) {}
template<class T>
inline char* CCountedStringPoolBase<T>::ReferenceString(const char* pIntrinsic)
{
if (!pIntrinsic)
return NULL;
return m_Elements[ReferenceStringHandle(pIntrinsic)].pString;
}
template<class T>
inline void CCountedStringPoolBase<T>::DereferenceString(const char* pIntrinsic)
{
if (!pIntrinsic)
return;
T nHashBucketIndex = (Hash(pIntrinsic) % m_HashTable.Count());
T nCurrentBucket = m_HashTable[nHashBucketIndex];
if (nCurrentBucket == INVALID_ELEMENT)
return;
for (T previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement)
{
if (!Q_stricmp(pIntrinsic, m_Elements[nCurrentBucket].pString))
{
if (m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE)
{
m_Elements[nCurrentBucket].nReferenceCount--;
}
if (m_Elements[nCurrentBucket].nReferenceCount == 0)
{
if (previous == INVALID_ELEMENT)
{
m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement;
}
else
{
m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement;
}
delete[] m_Elements[nCurrentBucket].pString;
m_Elements[nCurrentBucket].pString = NULL;
m_Elements[nCurrentBucket].nReferenceCount = 0;
m_Elements[nCurrentBucket].nNextElement = m_FreeListStart;
m_FreeListStart = nCurrentBucket;
break;
}
}
previous = nCurrentBucket;
}
}
template<class T>
inline char* CCountedStringPoolBase<T>::HandleToString(T handle)
{
return m_Elements[handle].pString;
}
template<class T>
inline void CCountedStringPoolBase<T>::SpewStrings()
{
int i;
for (i = 0; i < m_Elements.Count(); i++)
{
char* string = m_Elements[i].pString;
Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL ? "EMPTY - ok for slot zero only!" : string);
}
Msg("\n%d total counted strings.", m_Elements.Count());
}
#define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' )
#define MAX_STRING_SAVE 1024
template<>
inline bool CCountedStringPoolBase<unsigned short>::SaveToBuffer(CUtlBuffer& buffer)
{
if (m_Elements.Count() <= 1)
{
return true;
}
buffer.PutInt(STRING_POOL_VERSION);
buffer.PutUnsignedShort(m_FreeListStart);
buffer.PutInt(m_HashTable.Count());
for (int i = 0; i < m_HashTable.Count(); i++)
{
buffer.PutUnsignedShort(m_HashTable[i]);
}
buffer.PutInt(m_Elements.Count());
for (int i = 1; i < m_Elements.Count(); i++)
{
buffer.PutUnsignedShort(m_Elements[i].nNextElement);
buffer.PutUnsignedChar(m_Elements[i].nReferenceCount);
const char* pString = m_Elements[i].pString;
if (strlen(pString) >= MAX_STRING_SAVE)
{
return false;
}
buffer.PutString(pString ? pString : "");
}
return buffer.IsValid();
}
template<>
inline bool CCountedStringPoolBase<unsigned short>::RestoreFromBuffer(CUtlBuffer& buffer)
{
int signature = buffer.GetInt();
if (signature != STRING_POOL_VERSION)
{
return false;
}
FreeAll();
m_FreeListStart = buffer.GetUnsignedShort();
int hashCount = buffer.GetInt();
m_HashTable.SetCount(hashCount);
for (int i = 0; i < hashCount; i++)
{
m_HashTable[i] = buffer.GetUnsignedShort();
}
int tableCount = buffer.GetInt();
if (tableCount > 1)
{
m_Elements.AddMultipleToTail(tableCount - 1);
}
char tempString[MAX_STRING_SAVE];
for (int i = 1; i < tableCount; i++)
{
m_Elements[i].nNextElement = buffer.GetUnsignedShort();
m_Elements[i].nReferenceCount = buffer.GetUnsignedChar();
buffer.GetString(tempString, sizeof(tempString));
m_Elements[i].pString = strdup(tempString);
}
return buffer.IsValid();
}
template<>
inline bool CCountedStringPoolBase<unsigned int>::SaveToBuffer(CUtlBuffer& buffer)
{
if (m_Elements.Count() <= 1)
{
return true;
}
buffer.PutInt(STRING_POOL_VERSION);
buffer.PutUnsignedInt(m_FreeListStart);
buffer.PutInt(m_HashTable.Count());
for (int i = 0; i < m_HashTable.Count(); i++)
{
buffer.PutUnsignedInt(m_HashTable[i]);
}
buffer.PutInt(m_Elements.Count());
for (int i = 1; i < m_Elements.Count(); i++)
{
buffer.PutUnsignedInt(m_Elements[i].nNextElement);
buffer.PutUnsignedChar(m_Elements[i].nReferenceCount);
const char* pString = m_Elements[i].pString;
if (strlen(pString) >= MAX_STRING_SAVE)
{
return false;
}
buffer.PutString(pString ? pString : "");
}
return buffer.IsValid();
}
template<>
inline bool CCountedStringPoolBase<unsigned int>::RestoreFromBuffer(CUtlBuffer& buffer)
{
int signature = buffer.GetInt();
if (signature != STRING_POOL_VERSION)
{
return false;
}
FreeAll();
m_FreeListStart = buffer.GetUnsignedInt();
int hashCount = buffer.GetInt();
m_HashTable.SetCount(hashCount);
for (int i = 0; i < hashCount; i++)
{
m_HashTable[i] = buffer.GetUnsignedInt();
}
int tableCount = buffer.GetInt();
if (tableCount > 1)
{
m_Elements.AddMultipleToTail(tableCount - 1);
}
char tempString[MAX_STRING_SAVE];
for (int i = 1; i < tableCount; i++)
{
m_Elements[i].nNextElement = buffer.GetUnsignedInt();
m_Elements[i].nReferenceCount = buffer.GetUnsignedChar();
buffer.GetString(tempString, sizeof(tempString));
m_Elements[i].pString = strdup(tempString);
}
return buffer.IsValid();
}
#endif