summaryrefslogtreecommitdiff
path: root/external/vpc/tier1/utlsymbol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/tier1/utlsymbol.cpp')
-rw-r--r--external/vpc/tier1/utlsymbol.cpp513
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;
+}