Tier1: fully implement CUtlSymbol(*)

The class has been modified to match the implementation of the engine, the only modifications done were changing size types, so they compile to the correct size based on the platform (in case of the GameSDK project, this will be 64 bits).
This commit is contained in:
Kawe Mazidjatari 2024-03-14 02:37:39 +01:00
parent 615598c1b9
commit 582ec3791e
5 changed files with 703 additions and 147 deletions

View File

@ -248,7 +248,7 @@ inline T CCountedStringPoolBase<T>::ReferenceStringHandle( const char* pIntrinsi
}
else
{
unsigned int newElement = m_Elements.AddToTail();
T newElement = (T)m_Elements.AddToTail();
VerifyNotOverflowed( newElement );
nCurrentBucket = newElement;
}
@ -260,7 +260,7 @@ inline T CCountedStringPoolBase<T>::ReferenceStringHandle( const char* pIntrinsi
m_HashTable[ nHashBucketIndex ] = nCurrentBucket;
m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1];
Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic );
strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic );
return nCurrentBucket;
}
@ -342,9 +342,10 @@ inline void CCountedStringPoolBase<T>::SpewStrings()
int i;
for ( i = 0; i < m_Elements.Count(); i++ )
{
char* string = m_Elements[i].pString;
char* pString = m_Elements[i].pString;
NOTE_UNUSED(pString);
DevMsg("String %d: ref:%hhu %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string);
DevMsg("String %d: ref:%hhu %s\n", i, m_Elements[i].nReferenceCount, pString == NULL? "EMPTY - ok for slot zero only!" : pString);
}
DevMsg("\n%d total counted strings.", m_Elements.Count());

View File

@ -158,11 +158,12 @@ void V_ComposeFileName(const char* path, const char* filename, char* dest, size_
// Remove any extension from in and return resulting string in out
void V_StripExtension(const char* in, char* out, size_t outLen);
// Copy out the file extension into dest
void V_ExtractFileExtension(const char* path, char* dest, size_t destSize);
// Returns a pointer to the file extension or NULL if one doesn't exist
const char* V_GetFileExtension(const char* path, const bool keepDot = false);
// Copy out the file extension into dest
void V_ExtractFileExtension(const char* path, char* dest, size_t destSize);
bool V_ExtractFilePath(const char* path, char* dest, size_t destSize);
// Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator)
void V_FileBase(const char* in, OUT_Z_CAP(maxlen) char* out, size_t maxlen);

View File

@ -104,7 +104,7 @@ class CUtlSymbolTable
{
public:
// constructor, destructor
CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false );
CUtlSymbolTable( ssize_t growSize = 0, ssize_t initSize = 16, bool caseInsensitive = false );
~CUtlSymbolTable();
// Finds and/or creates a symbol based on the string
@ -159,21 +159,21 @@ protected:
public:
CLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree
bool operator!() const { return false; }
bool operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const;
int operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const;
};
// Stores the symbol lookup
class CTree : public CUtlRBTree<CStringPoolIndex, unsigned short, CLess>
{
public:
CTree( int growSize, int initSize ) : CUtlRBTree<CStringPoolIndex, unsigned short, CLess>( growSize, initSize ) {}
CTree( ssize_t growSize, ssize_t initSize ) : CUtlRBTree<CStringPoolIndex, unsigned short, CLess>( growSize, initSize ) {}
friend class CUtlSymbolTable::CLess; // Needed to allow CLess to calculate pointer to symbol table
};
struct StringPool_t
{
int m_TotalLen; // How large is
int m_SpaceUsed;
size_t m_TotalLen; // How large is
size_t m_SpaceUsed;
char m_Data[1];
};
@ -187,7 +187,7 @@ protected:
CUtlVector<StringPool_t*> m_StringPools;
private:
int FindPoolWithSpace( int len ) const;
int FindPoolWithSpace( size_t len ) const;
const char* StringFromIndex( const CStringPoolIndex &index ) const;
const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const;
@ -196,62 +196,60 @@ private:
};
// TODO[ AMOS ]: implement CThreadSpinRWLock
class CUtlSymbolTableMT : public CUtlSymbolTable
{
public:
CUtlSymbolTableMT( ssize_t growSize = 0, ssize_t initSize = 32, bool caseInsensitive = false )
: CUtlSymbolTable( growSize, initSize, caseInsensitive )
{
}
//class CUtlSymbolTableMT : public CUtlSymbolTable
//{
//public:
// CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false )
// : CUtlSymbolTable( growSize, initSize, caseInsensitive )
// {
// }
//
// CUtlSymbol AddString( const char* pString )
// {
// m_lock.LockForWrite();
// CUtlSymbol result = CUtlSymbolTable::AddString( pString );
// m_lock.UnlockWrite();
// return result;
// }
//
// CUtlSymbol Find( const char* pString ) const
// {
// m_lock.LockForWrite();
// CUtlSymbol result = CUtlSymbolTable::Find( pString );
// m_lock.UnlockWrite();
// return result;
// }
//
// const char* String( CUtlSymbol id ) const
// {
// m_lock.LockForRead();
// const char *pszResult = CUtlSymbolTable::String( id );
// m_lock.UnlockRead();
// return pszResult;
// }
//
// const char * StringNoLock( CUtlSymbol id ) const
// {
// return CUtlSymbolTable::String( id );
// }
//
// void LockForRead()
// {
// m_lock.LockForRead();
// }
//
// void UnlockForRead()
// {
// m_lock.UnlockRead();
// }
//
//private:
//#ifdef WIN32
// mutable CThreadSpinRWLock m_lock;
//#else
// mutable CThreadRWLock m_lock;
//#endif
//};
CUtlSymbol AddString( const char* pString )
{
m_lock.LockForWrite();
CUtlSymbol result = CUtlSymbolTable::AddString( pString );
m_lock.UnlockWrite();
return result;
}
CUtlSymbol Find( const char* pString ) const
{
m_lock.LockForWrite();
CUtlSymbol result = CUtlSymbolTable::Find( pString );
m_lock.UnlockWrite();
return result;
}
const char* String( CUtlSymbol id ) const
{
m_lock.LockForRead();
const char *pszResult = CUtlSymbolTable::String( id );
m_lock.UnlockRead();
return pszResult;
}
const char * StringNoLock( CUtlSymbol id ) const
{
return CUtlSymbolTable::String( id );
}
void LockForRead()
{
m_lock.LockForRead();
}
void UnlockForRead()
{
m_lock.UnlockRead();
}
private:
#ifdef WIN32
mutable CThreadSpinRWLock m_lock;
#else
mutable CThreadRWLock m_lock;
#endif
};
@ -270,71 +268,68 @@ typedef void* FileNameHandle_t;
// Symbol table for more efficiently storing filenames by breaking paths and filenames apart.
// Refactored from BaseFileSystem.h
class CUtlFilenameSymbolTable
{
// Internal representation of a FileHandle_t
// If we get more than 64K filenames, we'll have to revisit...
// Right now CUtlSymbol is a short, so this packs into an int/void * pointer size...
struct FileNameHandleInternal_t
{
FileNameHandleInternal_t()
{
COMPILE_TIME_ASSERT( sizeof( *this ) == sizeof( FileNameHandle_t ) );
COMPILE_TIME_ASSERT( sizeof( value ) == 4 );
value = 0;
// TODO[ AMOS ]: implement CThreadSpinRWLock
#ifdef PLATFORM_64BITS
pad = 0;
#endif
}
//class CUtlFilenameSymbolTable
//{
// // Internal representation of a FileHandle_t
// // If we get more than 64K filenames, we'll have to revisit...
// // Right now CUtlSymbol is a short, so this packs into an int/void * pointer size...
// struct FileNameHandleInternal_t
// {
// FileNameHandleInternal_t()
// {
// COMPILE_TIME_ASSERT( sizeof( *this ) == sizeof( FileNameHandle_t ) );
// COMPILE_TIME_ASSERT( sizeof( value ) == 4 );
// value = 0;
//
//#ifdef PLATFORM_64BITS
// pad = 0;
//#endif
// }
//
// // We pack the path and file values into a single 32 bit value. We were running
// // out of space with the two 16 bit values (more than 64k files) so instead of increasing
// // the total size we split the underlying pool into two (paths and files) and
// // use a smaller path string pool and a larger file string pool.
// unsigned int value;
//
//#ifdef PLATFORM_64BITS
// // some padding to make sure we are the same size as FileNameHandle_t on 64 bit.
// unsigned int pad;
//#endif
//
// static const unsigned int cNumBitsInPath = 12;
// static const unsigned int cNumBitsInFile = 32 - cNumBitsInPath;
//
// static const unsigned int cMaxPathValue = 1 << cNumBitsInPath;
// static const unsigned int cMaxFileValue = 1 << cNumBitsInFile;
//
// static const unsigned int cPathBitMask = cMaxPathValue - 1;
// static const unsigned int cFileBitMask = cMaxFileValue - 1;
//
// // Part before the final '/' character
// unsigned int GetPath() const { return ((value >> cNumBitsInFile) & cPathBitMask); }
// void SetPath( unsigned int path ) { Assert( path < cMaxPathValue ); value = ((value & cFileBitMask) | ((path & cPathBitMask) << cNumBitsInFile)); }
//
// // Part after the final '/', including extension
// unsigned int GetFile() const { return (value & cFileBitMask); }
// void SetFile( unsigned int file ) { Assert( file < cMaxFileValue ); value = ((value & (cPathBitMask << cNumBitsInFile)) | (file & cFileBitMask)); }
// };
//
//public:
// FileNameHandle_t FindOrAddFileName( const char *pFileName );
// FileNameHandle_t FindFileName( const char *pFileName );
// int PathIndex( const FileNameHandle_t &handle ) { return (( const FileNameHandleInternal_t * )&handle)->GetPath(); }
// bool String( const FileNameHandle_t& handle, char *buf, int buflen );
// void RemoveAll();
// void SpewStrings();
// bool SaveToBuffer( CUtlBuffer &buffer );
// bool RestoreFromBuffer( CUtlBuffer &buffer );
//
//private:
// CCountedStringPoolBase<unsigned short> m_PathStringPool;
// CCountedStringPoolBase<unsigned int> m_FileStringPool;
// mutable CThreadSpinRWLock m_lock;
//};
// We pack the path and file values into a single 32 bit value. We were running
// out of space with the two 16 bit values (more than 64k files) so instead of increasing
// the total size we split the underlying pool into two (paths and files) and
// use a smaller path string pool and a larger file string pool.
unsigned int value;
#ifdef PLATFORM_64BITS
// some padding to make sure we are the same size as FileNameHandle_t on 64 bit.
unsigned int pad;
#endif
static const unsigned int cNumBitsInPath = 12;
static const unsigned int cNumBitsInFile = 32 - cNumBitsInPath;
static const unsigned int cMaxPathValue = 1 << cNumBitsInPath;
static const unsigned int cMaxFileValue = 1 << cNumBitsInFile;
static const unsigned int cPathBitMask = cMaxPathValue - 1;
static const unsigned int cFileBitMask = cMaxFileValue - 1;
// Part before the final '/' character
unsigned int GetPath() const { return ((value >> cNumBitsInFile) & cPathBitMask); }
void SetPath( unsigned int path ) { Assert( path < cMaxPathValue ); value = ((value & cFileBitMask) | ((path & cPathBitMask) << cNumBitsInFile)); }
// Part after the final '/', including extension
unsigned int GetFile() const { return (value & cFileBitMask); }
void SetFile( unsigned int file ) { Assert( file < cMaxFileValue ); value = ((value & (cPathBitMask << cNumBitsInFile)) | (file & cFileBitMask)); }
};
public:
FileNameHandle_t FindOrAddFileName( const char *pFileName );
FileNameHandle_t FindFileName( const char *pFileName );
int PathIndex( const FileNameHandle_t &handle ) { return (( const FileNameHandleInternal_t * )&handle)->GetPath(); }
bool String( const FileNameHandle_t& handle, char *buf, int buflen );
void RemoveAll();
void SpewStrings();
bool SaveToBuffer( CUtlBuffer &buffer );
bool RestoreFromBuffer( CUtlBuffer &buffer );
private:
CCountedStringPoolBase<unsigned short> m_PathStringPool;
CCountedStringPoolBase<unsigned int> m_FileStringPool;
mutable CThreadSpinRWLock m_lock;
};
// This creates a simple class that includes the underlying CUtlSymbol
// as a private member and then instances a private symbol table to

View File

@ -1240,21 +1240,6 @@ void V_StripExtension(const char* in, char* out, size_t outSize)
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
// *dest -
// destSize -
// Output : void V_ExtractFileExtension
//-----------------------------------------------------------------------------
void V_ExtractFileExtension(const char* path, char* dest, size_t destSize)
{
*dest = 0;
const char* extension = V_GetFileExtension(path);
if (NULL != extension)
V_strncpy(dest, extension, destSize);
}
//-----------------------------------------------------------------------------
// Purpose: Returns a pointer to the file extension within a file name string
// Input: in - file name
@ -1288,6 +1273,52 @@ const char* V_GetFileExtension(const char* path, const bool keepDot)
return out ? out : path;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
// *dest -
// destSize -
// Output : void V_ExtractFileExtension
//-----------------------------------------------------------------------------
void V_ExtractFileExtension(const char* path, char* dest, size_t destSize)
{
*dest = 0;
const char* extension = V_GetFileExtension(path);
if (NULL != extension)
V_strncpy(dest, extension, destSize);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
// *dest -
// destSize -
// Output : void V_ExtractFilePath
//-----------------------------------------------------------------------------
bool V_ExtractFilePath(const char* path, char* dest, size_t destSize)
{
Assert(destSize >= 1);
if (destSize < 1)
{
return false;
}
// Last char
const size_t len = V_strlen(path);
const char* src = path + (len ? len - 1 : 0);
// back up until a \ or the start
while (src != path && !PATHSEPARATOR(*(src - 1)))
{
src--;
}
const ssize_t copysize = Min(size_t(src - path), destSize - 1);
memcpy(dest, path, copysize);
dest[copysize] = 0;
return copysize != 0 ? true : false;
}
//-----------------------------------------------------------------------------
// Purpose: Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator)

528
r5dev/tier1/utlsymbol.cpp Normal file
View File

@ -0,0 +1,528 @@
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose: Defines a symbol table
//
// $Header: $
// $NoKeywords: $
//=============================================================================//
#pragma warning (disable:4514)
#include "tier1/utlsymbol.h"
#include "tier0/threadtools.h"
//#include "stringpool.h"
//#include "generichash.h"
//#include "tier0/vprof.h"
#include <stddef.h>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define INVALID_STRING_INDEX CStringPoolIndex( 0xFFFF, 0xFFFF )
#define MIN_STRING_POOL_SIZE 2048
//-----------------------------------------------------------------------------
// globals
//-----------------------------------------------------------------------------
CUtlSymbolTableMT* CUtlSymbol::s_pSymbolTable = 0;
bool CUtlSymbol::s_bAllowStaticSymbolTable = true;
//-----------------------------------------------------------------------------
// symbol methods
//-----------------------------------------------------------------------------
void CUtlSymbol::Initialize()
{
// If this assert fails, then the module that this call is in has chosen to disallow
// use of the static symbol table. Usually, it's to prevent confusion because it's easy
// to accidentally use the global symbol table when you really want to use a specific one.
Assert( s_bAllowStaticSymbolTable );
// necessary to allow us to create global symbols
static bool symbolsInitialized = false;
if (!symbolsInitialized)
{
s_pSymbolTable = new CUtlSymbolTableMT;
symbolsInitialized = true;
}
}
void CUtlSymbol::LockTableForRead()
{
Initialize();
s_pSymbolTable->LockForRead();
}
void CUtlSymbol::UnlockTableForRead()
{
s_pSymbolTable->UnlockForRead();
}
//-----------------------------------------------------------------------------
// Purpose: Singleton to delete table on exit from module
//-----------------------------------------------------------------------------
class CCleanupUtlSymbolTable
{
public:
~CCleanupUtlSymbolTable()
{
delete CUtlSymbol::s_pSymbolTable;
CUtlSymbol::s_pSymbolTable = NULL;
}
};
static CCleanupUtlSymbolTable g_CleanupSymbolTable;
CUtlSymbolTableMT* CUtlSymbol::CurrTable()
{
Initialize();
return s_pSymbolTable;
}
//-----------------------------------------------------------------------------
// string->symbol->string
//-----------------------------------------------------------------------------
CUtlSymbol::CUtlSymbol( const char* pStr )
{
m_Id = CurrTable()->AddString( pStr );
}
const char* CUtlSymbol::String( ) const
{
return CurrTable()->String(m_Id);
}
const char* CUtlSymbol::StringNoLock( ) const
{
return CurrTable()->StringNoLock(m_Id);
}
void CUtlSymbol::DisableStaticSymbolTable()
{
s_bAllowStaticSymbolTable = false;
}
//-----------------------------------------------------------------------------
// checks if the symbol matches a string
//-----------------------------------------------------------------------------
bool CUtlSymbol::operator==( const char* pStr ) const
{
if (m_Id == UTL_INVAL_SYMBOL)
return false;
return strcmp( String(), pStr ) == 0;
}
//-----------------------------------------------------------------------------
// symbol table stuff
//-----------------------------------------------------------------------------
inline const char* CUtlSymbolTable::DecoratedStringFromIndex( const CStringPoolIndex &index ) const
{
Assert( index.m_iPool < m_StringPools.Count() );
Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen );
// step over the hash decorating the beginning of the string
return (&m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]);
}
inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const
{
// step over the hash decorating the beginning of the string
return DecoratedStringFromIndex(index)+sizeof(hashDecoration_t);
}
// The first two bytes of each string in the pool are actually the hash for that string.
// Thus we compare hashes rather than entire strings for a significant perf benefit.
// However since there is a high rate of hash collision we must still compare strings
// if the hashes match.
int CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStringPoolIndex &i2 ) const
{
// Need to do pointer math because CUtlSymbolTable is used in CUtlVectors, and hence
// can be arbitrarily moved in memory on a realloc. Yes, this is portable. In reality,
// right now at least, because m_LessFunc is the first member of CUtlRBTree, and m_Lookup
// is the first member of CUtlSymbolTabke, this == pTable
CUtlSymbolTable *pTable = (CUtlSymbolTable *)( (byte *)this - offsetof(CUtlSymbolTable::CTree, m_LessFunc) ) - offsetof(CUtlSymbolTable, m_Lookup );
#if 1 // using the hashes
const char *str1, *str2;
hashDecoration_t hash1, hash2;
if (i1 == INVALID_STRING_INDEX)
{
str1 = pTable->m_pUserSearchString;
hash1 = pTable->m_nUserSearchStringHash;
}
else
{
str1 = pTable->DecoratedStringFromIndex( i1 );
hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str1);
str1 += sizeof(hashDecoration_t);
AssertMsg( storedHash == ( !pTable->m_bInsensitive ? HashString(str1) : HashStringCaseless(str1) ),
"The stored hash (%d) for symbol %s is not correct.", storedHash, str1 );
hash1 = storedHash;
}
if (i2 == INVALID_STRING_INDEX)
{
str2 = pTable->m_pUserSearchString;
hash2 = pTable->m_nUserSearchStringHash;
}
else
{
str2 = pTable->DecoratedStringFromIndex( i2 );
hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str2);
str2 += sizeof(hashDecoration_t);
AssertMsg( storedHash == ( !pTable->m_bInsensitive ? HashString(str2) : HashStringCaseless(str2) ),
"The stored hash (%d) for symbol '%s' is not correct.", storedHash, str2 );
hash2 = storedHash;
}
// compare the hashes
if ( hash1 == hash2 )
{
if ( !str1 && str2 )
return 1;
if ( !str2 && str1 )
return -1;
if ( !str1 && !str2 )
return 0;
// if the hashes match compare the strings
if ( !pTable->m_bInsensitive )
return strcmp( str1, str2 ) < 0;
else
return V_stricmp( str1, str2 ) < 0;
}
else
{
return hash1 < hash2;
}
#else // not using the hashes, just comparing strings
const char* str1 = (i1 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
pTable->StringFromIndex( i1 );
const char* str2 = (i2 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
pTable->StringFromIndex( i2 );
if ( !str1 && str2 )
return 1;
if ( !str2 && str1 )
return -1;
if ( !str1 && !str2 )
return 0;
if ( !pTable->m_bInsensitive )
return strcmp( str1, str2 ) < 0;
else
return strcmpi( str1, str2 ) < 0;
#endif
}
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CUtlSymbolTable::CUtlSymbolTable( ssize_t growSize, ssize_t initSize, bool caseInsensitive ) :
m_Lookup( growSize, initSize ), m_bInsensitive( caseInsensitive ), m_StringPools( 8 ),
m_nUserSearchStringHash( 0 ), m_pUserSearchString( nullptr )
{
}
CUtlSymbolTable::~CUtlSymbolTable()
{
// Release the stringpool string data
RemoveAll();
}
CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const
{
//VPROF( "CUtlSymbol::Find" );
if (!pString)
return CUtlSymbol();
// Store a special context used to help with insertion
m_pUserSearchString = pString;
m_nUserSearchStringHash = m_bInsensitive
? ( unsigned short )HashStringCaseless( pString )
: ( unsigned short )HashString( pString );
// Passing this special invalid symbol makes the comparison function
// use the string passed in the context
UtlSymId_t idx = m_Lookup.Find( INVALID_STRING_INDEX );
#ifdef _DEBUG
m_pUserSearchString = NULL;
m_nUserSearchStringHash = 0;
#endif
return CUtlSymbol( idx );
}
int CUtlSymbolTable::FindPoolWithSpace( size_t len ) const
{
for ( int i=0; i < m_StringPools.Count(); i++ )
{
StringPool_t *pPool = m_StringPools[i];
if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len )
{
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Finds and/or creates a symbol based on the string
//-----------------------------------------------------------------------------
CUtlSymbol CUtlSymbolTable::AddString( const char* pString )
{
//VPROF("CUtlSymbol::AddString");
if (!pString)
return CUtlSymbol( UTL_INVAL_SYMBOL );
CUtlSymbol id = Find( pString );
if (id.IsValid())
return id;
size_t lenString = strlen(pString) + 1; // length of just the string
size_t lenDecorated = lenString + sizeof(hashDecoration_t); // and with its hash decoration
// make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly
COMPILE_TIME_ASSERT(sizeof(hashDecoration_t) == 2);
lenDecorated = (lenDecorated + 1) & (~0x01); // round up to nearest multiple of 2
// Find a pool with space for this string, or allocate a new one.
int iPool = FindPoolWithSpace( lenDecorated );
if ( iPool == -1 )
{
// Add a new pool.
size_t newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE );
StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize );
pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t );
pPool->m_SpaceUsed = 0;
iPool = m_StringPools.AddToTail( pPool );
}
// Compute a hash
hashDecoration_t hash = m_bInsensitive
? ( unsigned short )HashStringCaseless( pString )
: ( unsigned short )HashString( pString );
// Copy the string in.
StringPool_t *pPool = m_StringPools[iPool];
Assert( pPool->m_SpaceUsed < 0xFFFF ); // This should never happen, because if we had a string > 64k, it
// would have been given its entire own pool.
unsigned short iStringOffset = ( unsigned short )pPool->m_SpaceUsed;
const char *startingAddr = &pPool->m_Data[pPool->m_SpaceUsed];
// store the hash at the head of the string
*((hashDecoration_t *)(startingAddr)) = hash;
// and then the string's data
memcpy( (void *)(startingAddr + sizeof(hashDecoration_t)), pString, lenString );
pPool->m_SpaceUsed += lenDecorated;
// insert the string into the vector.
CStringPoolIndex index;
index.m_iPool = ( unsigned short )iPool;
index.m_iOffset = ( unsigned short )iStringOffset;
MEM_ALLOC_CREDIT();
UtlSymId_t idx = m_Lookup.Insert( index );
return CUtlSymbol( idx );
}
//-----------------------------------------------------------------------------
// Look up the string associated with a particular symbol
//-----------------------------------------------------------------------------
const char* CUtlSymbolTable::String( CUtlSymbol id ) const
{
if (!id.IsValid())
return "";
Assert( m_Lookup.IsValidIndex((UtlSymId_t)id) );
return StringFromIndex( m_Lookup[id] );
}
//-----------------------------------------------------------------------------
// Remove all symbols in the table.
//-----------------------------------------------------------------------------
void CUtlSymbolTable::RemoveAll()
{
m_Lookup.Purge();
for ( int i=0; i < m_StringPools.Count(); i++ )
free( m_StringPools[i] );
m_StringPools.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
// Output : FileNameHandle_t
//-----------------------------------------------------------------------------
FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileName )
{
if ( !pFileName )
{
return NULL;
}
// find first
FileNameHandle_t hFileName = FindFileName( pFileName );
if ( hFileName )
{
return hFileName;
}
// Fix slashes+dotslashes and make lower case first..
char fn[ MAX_PATH ];
Q_strncpy( fn, pFileName, sizeof( fn ) );
V_RemoveDotSlashes( fn );
#ifdef _WIN32
strlwr( fn );
#endif
// Split the filename into constituent parts
char basepath[ MAX_PATH ];
V_ExtractFilePath( fn, basepath, sizeof( basepath ) );
char filename[ MAX_PATH ];
Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
// not found, lock and look again
FileNameHandleInternal_t handle;
m_lock.LockForWrite();
handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) );
handle.SetFile( m_FileStringPool.FindStringHandle( filename ) );
if ( handle.GetPath() && handle.GetFile() )
{
// found
m_lock.UnlockWrite();
return *( FileNameHandle_t * )( &handle );
}
// safely add it
handle.SetPath( m_PathStringPool.ReferenceStringHandle( basepath ) );
handle.SetFile( m_FileStringPool.ReferenceStringHandle( filename ) );
m_lock.UnlockWrite();
return *( FileNameHandle_t * )( &handle );
}
FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName )
{
if ( !pFileName )
{
return NULL;
}
// Fix slashes+dotslashes and make lower case first..
char fn[ MAX_PATH ];
Q_strncpy( fn, pFileName, sizeof( fn ) );
V_RemoveDotSlashes( fn );
#ifdef _WIN32
strlwr( fn );
#endif
// Split the filename into constituent parts
char basepath[ MAX_PATH ];
V_ExtractFilePath( fn, basepath, sizeof( basepath ) );
char filename[ MAX_PATH ];
Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
FileNameHandleInternal_t handle;
m_lock.LockForRead();
handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) );
handle.SetFile( m_FileStringPool.FindStringHandle( filename ) );
m_lock.UnlockRead();
if ( ( handle.GetPath() == 0 ) || ( handle.GetFile() == 0 ) )
return NULL;
return *( FileNameHandle_t * )( &handle );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handle -
// Output : const char
//-----------------------------------------------------------------------------
bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, int buflen )
{
buf[ 0 ] = 0;
FileNameHandleInternal_t *internalFileHandle = ( FileNameHandleInternal_t * )&handle;
if ( !internalFileHandle )
{
return false;
}
m_lock.LockForRead();
const char *path = m_PathStringPool.HandleToString( (unsigned short)internalFileHandle->GetPath() );
const char *fn = m_FileStringPool.HandleToString( (unsigned short)internalFileHandle->GetFile() );
m_lock.UnlockRead();
if ( !path || !fn )
{
return false;
}
Q_strncpy( buf, path, buflen );
Q_strncat( buf, fn, buflen );
return true;
}
void CUtlFilenameSymbolTable::RemoveAll()
{
m_PathStringPool.FreeAll();
m_FileStringPool.FreeAll();
}
void CUtlFilenameSymbolTable::SpewStrings()
{
m_lock.LockForRead();
m_PathStringPool.SpewStrings();
m_FileStringPool.SpewStrings();
m_lock.UnlockRead();
}
bool CUtlFilenameSymbolTable::SaveToBuffer( CUtlBuffer &buffer )
{
m_lock.LockForRead();
bool bResult = m_PathStringPool.SaveToBuffer( buffer );
bResult = bResult && m_FileStringPool.SaveToBuffer( buffer );
m_lock.UnlockRead();
return bResult;
}
bool CUtlFilenameSymbolTable::RestoreFromBuffer( CUtlBuffer &buffer )
{
m_lock.LockForWrite();
bool bResult = m_PathStringPool.RestoreFromBuffer( buffer );
bResult = bResult && m_FileStringPool.RestoreFromBuffer( buffer );
m_lock.UnlockWrite();
return bResult;
}