diff options
Diffstat (limited to 'external/vpc/tier1/utlsymbol.cpp')
| -rw-r--r-- | external/vpc/tier1/utlsymbol.cpp | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/external/vpc/tier1/utlsymbol.cpp b/external/vpc/tier1/utlsymbol.cpp new file mode 100644 index 0000000..f9dc84e --- /dev/null +++ b/external/vpc/tier1/utlsymbol.cpp @@ -0,0 +1,513 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines a symbol table +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#pragma warning (disable:4514) + +#include "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. +bool 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); + AssertMsg2( 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); + AssertMsg2( 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 false; + if ( !str2 && str1 ) + return true; + if ( !str1 && !str2 ) + return false; + + // 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 false; + if ( !str2 && str1 ) + return true; + if ( !str1 && !str2 ) + return false; + if ( !pTable->m_bInsensitive ) + return strcmp( str1, str2 ) < 0; + else + return strcmpi( str1, str2 ) < 0; +#endif +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CUtlSymbolTable::CUtlSymbolTable( int growSize, int initSize, bool caseInsensitive ) : + m_Lookup( growSize, initSize ), m_bInsensitive( caseInsensitive ), m_StringPools( 8 ) +{ +} + +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 ? HashStringCaseless(pString) : 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( int 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; + + int lenString = strlen(pString) + 1; // length of just the string + int 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. + int 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 ? HashStringCaseless(pString) : 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 = 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 = iPool; + index.m_iOffset = 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 ]; + V_strncpy( fn, pFileName, sizeof( fn ) ); + V_RemoveDotSlashes( fn ); + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + V_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + V_strncpy( filename, fn + V_strlen( basepath ), sizeof( filename ) ); + + // not found, lock and look again + FileNameHandleInternal_t handle; + m_lock.LockForWrite(); + handle.path = m_StringPool.FindStringHandle( basepath ); + handle.file = m_StringPool.FindStringHandle( filename ); + if ( handle.path && handle.file ) + { + // found + m_lock.UnlockWrite(); + return *( FileNameHandle_t * )( &handle ); + } + + // safely add it + handle.path = m_StringPool.ReferenceStringHandle( basepath ); + handle.file = m_StringPool.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 ]; + V_strncpy( fn, pFileName, sizeof( fn ) ); + V_RemoveDotSlashes( fn ); + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + V_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + V_strncpy( filename, fn + V_strlen( basepath ), sizeof( filename ) ); + + FileNameHandleInternal_t handle; + + m_lock.LockForRead(); + handle.path = m_StringPool.FindStringHandle(basepath); + handle.file = m_StringPool.FindStringHandle(filename); + m_lock.UnlockRead(); + + + if ( ( handle.path == 0 ) || ( handle.file == 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 *internal = ( FileNameHandleInternal_t * )&handle; + if ( !internal ) + { + return false; + } + + m_lock.LockForRead(); + const char *path = m_StringPool.HandleToString(internal->path); + const char *fn = m_StringPool.HandleToString(internal->file); + m_lock.UnlockRead(); + + if ( !path || !fn ) + { + return false; + } + + V_strncpy( buf, path, buflen ); + V_strncat( buf, fn, buflen, COPY_ALL_CHARACTERS ); + + return true; +} + +void CUtlFilenameSymbolTable::RemoveAll() +{ + m_StringPool.FreeAll(); +} + +void CUtlFilenameSymbolTable::SpewStrings() +{ + m_lock.LockForRead(); + m_StringPool.SpewStrings(); + m_lock.UnlockRead(); +} + +bool CUtlFilenameSymbolTable::SaveToBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForRead(); + bool bResult = m_StringPool.SaveToBuffer( buffer ); + m_lock.UnlockRead(); + + return bResult; +} + +bool CUtlFilenameSymbolTable::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForWrite(); + bool bResult = m_StringPool.RestoreFromBuffer( buffer ); + m_lock.UnlockWrite(); + + return bResult; +} |