diff --git a/r5dev/tier0/platform.h b/r5dev/tier0/platform.h index e8158ec8..d9619b27 100644 --- a/r5dev/tier0/platform.h +++ b/r5dev/tier0/platform.h @@ -343,6 +343,19 @@ template const char* MemAllocClassName(T* p) #define abstract_class class NO_VTABLE #endif +#if defined( _MSC_VER ) +#define OVERRIDE override +// warning C4481: nonstandard extension used: override specifier 'override' +#pragma warning(disable : 4481) +#elif defined( __clang__ ) +#define OVERRIDE override +// warning: 'override' keyword is a C++11 extension [-Wc++11-extensions] +// Disabling this warning is less intrusive than enabling C++11 extensions +#pragma GCC diagnostic ignored "-Wc++11-extensions" +#else +#define OVERRIDE +#endif + //----------------------------------------------------------------------------- // Generally useful platform-independent macros (move to another file?) //----------------------------------------------------------------------------- diff --git a/r5dev/tier1/stringpool.cpp b/r5dev/tier1/stringpool.cpp new file mode 100644 index 00000000..f419e639 --- /dev/null +++ b/r5dev/tier1/stringpool.cpp @@ -0,0 +1,127 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "core/stdafx.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier1/stringpool.h" +#include "tier1/generichash.h" + +// memdbgon must be the last include file in a .cpp file!!! +//#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Comparison function for string sorted associative data structures +//----------------------------------------------------------------------------- + +bool StrLessInsensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( Q_stricmp( pszLeft, pszRight) < 0 ); +} + +bool StrLessSensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( Q_strcmp( pszLeft, pszRight) < 0 ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CStringPool::CStringPool( StringPoolCase_t caseSensitivity ) + : m_Strings( 32, 256, caseSensitivity == StringPoolCaseInsensitive ? StrLessInsensitive : StrLessSensitive ) +{ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CStringPool::~CStringPool() +{ + FreeAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +unsigned int CStringPool::Count() const +{ + return m_Strings.Count(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char * CStringPool::Find( const char *pszValue ) +{ + unsigned short i = m_Strings.Find(pszValue); + if ( m_Strings.IsValidIndex(i) ) + return m_Strings[i]; + + return NULL; +} + +const char * CStringPool::Allocate( const char *pszValue ) +{ + char *pszNew; + + unsigned short i = m_Strings.Find(pszValue); + bool bNew = (i == m_Strings.InvalidIndex()); + + if ( !bNew ) + return m_Strings[i]; + + pszNew = strdup( pszValue ); + m_Strings.Insert( pszNew ); + + return pszNew; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CStringPool::FreeAll() +{ + unsigned short i = m_Strings.FirstInorder(); + while ( i != m_Strings.InvalidIndex() ) + { + free( (void *)m_Strings[i] ); + i = m_Strings.NextInorder(i); + } + m_Strings.RemoveAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/* +#ifdef _DEBUG +CON_COMMAND( test_stringpool, "Tests the class CStringPool" ) +{ + CStringPool pool; + + Assert(pool.Count() == 0); + + pool.Allocate("test"); + Assert(pool.Count() == 1); + + pool.Allocate("test"); + Assert(pool.Count() == 1); + + pool.Allocate("test2"); + Assert(pool.Count() == 2); + + Assert( pool.Find("test2") != NULL ); + Assert( pool.Find("TEST") != NULL ); + Assert( pool.Find("Test2") != NULL ); + Assert( pool.Find("test") != NULL ); + + pool.FreeAll(); + Assert(pool.Count() == 0); + + Msg("Pass."); +} +#endif +*/ \ No newline at end of file diff --git a/r5dev/tier1/stringpool.h b/r5dev/tier1/stringpool.h new file mode 100644 index 00000000..6b8bbd7e --- /dev/null +++ b/r5dev/tier1/stringpool.h @@ -0,0 +1,511 @@ +//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STRINGPOOL_H +#define STRINGPOOL_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "utlrbtree.h" +#include "utlvector.h" +#include "utlbuffer.h" +#include "generichash.h" + +//----------------------------------------------------------------------------- +// Purpose: Allocates memory for strings, checking for duplicates first, +// reusing exising strings if duplicate found. +//----------------------------------------------------------------------------- + +enum StringPoolCase_t +{ + StringPoolCaseInsensitive, + StringPoolCaseSensitive +}; + +class CStringPool +{ +public: + CStringPool( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive ); + ~CStringPool(); + + unsigned int Count() const; + + const char * Allocate( const char *pszValue ); + // This feature is deliberately not supported because it's pretty dangerous + // given current uses of CStringPool, which assume they can copy string pointers without + // any refcounts. + //void Free( const char *pszValue ); + void FreeAll(); + + // searches for a string already in the pool + const char * Find( const char *pszValue ); + +protected: + typedef CUtlRBTree CStrSet; + + CStrSet m_Strings; +}; + +//----------------------------------------------------------------------------- +// Purpose: A reference counted string pool. +// +// Elements are stored more efficiently than in the conventional string pool, +// quicker to look up, and storage is tracked via reference counts. +// +// At some point this should replace CStringPool +//----------------------------------------------------------------------------- +template +class CCountedStringPoolBase +{ +public: // HACK, hash_item_t structure should not be 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 m_HashTable; // Points to each element + CUtlVector 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 ); + + // These are only reliable if there are less than 64k strings in your string pool + 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 ); + + // Debug helper method to validate that we didn't overflow + void VerifyNotOverflowed( unsigned int value ); +}; + +typedef CCountedStringPoolBase CCountedStringPool; + +template +inline CCountedStringPoolBase::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 +inline CCountedStringPoolBase::~CCountedStringPoolBase() +{ + FreeAll(); +} + +template +inline void CCountedStringPoolBase::FreeAll() +{ + int i; + + // Reset the hash table: + for( i = 0; i < m_HashTable.Count(); i++ ) + { + m_HashTable[i] = INVALID_ELEMENT; + } + + // Blow away the free list: + 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; + } + } + + // Remove all but the 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 +inline unsigned CCountedStringPoolBase::Hash( const char *pszKey ) +{ + if ( m_caseSensitivity == StringPoolCaseInsensitive ) + { + return HashStringCaseless( pszKey ); + } + return HashString( pszKey ); +} + +template +inline T CCountedStringPoolBase::FindStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + T nHashBucketIndex = ( Hash( pIntrinsic ) %HASH_TABLE_SIZE); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + 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 +inline char* CCountedStringPoolBase::FindString( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return NULL; + + // Yes, this will be NULL on failure. + return m_Elements[FindStringHandle(pIntrinsic)].pString; +} + +template +inline T CCountedStringPoolBase::ReferenceStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + T nHashBucketIndex = ( Hash( pIntrinsic ) % HASH_TABLE_SIZE); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + if( nCurrentBucket != INVALID_ELEMENT ) + { + for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + // Anyone who hits 65k references is permanant + 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; + + // Insert at the beginning of the bucket: + 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::VerifyNotOverflowed( unsigned int value ) { Assert( value < 0xffff ); } + +template<> +inline void CCountedStringPoolBase::VerifyNotOverflowed( unsigned int value ) {} + +template +inline char* CCountedStringPoolBase::ReferenceString( const char* pIntrinsic ) +{ + if(!pIntrinsic) + return NULL; + + return m_Elements[ReferenceStringHandle( pIntrinsic)].pString; +} + +template +inline void CCountedStringPoolBase::DereferenceString( const char* pIntrinsic ) +{ + // If we get a NULL pointer, just return + if (!pIntrinsic) + return; + + T nHashBucketIndex = (Hash( pIntrinsic ) % m_HashTable.Count()); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // If there isn't anything in the bucket, just return. + 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 ) ) + { + // Anyone who hits 65k references is permanant + 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 +inline char* CCountedStringPoolBase::HandleToString( T handle ) +{ + return m_Elements[handle].pString; +} + +template +inline void CCountedStringPoolBase::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::SaveToBuffer( CUtlBuffer &buffer ) +{ + if ( m_Elements.Count() <= 1 ) + { + // pool is empty, saving nothing + // caller can check put position of buffer to detect + return true; + } + + // signature/version + 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::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + int signature = buffer.GetInt(); + if ( signature != STRING_POOL_VERSION ) + { + // wrong 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::SaveToBuffer( CUtlBuffer &buffer ) +{ + if ( m_Elements.Count() <= 1 ) + { + // pool is empty, saving nothing + // caller can check put position of buffer to detect + return true; + } + + // signature/version + 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::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + int signature = buffer.GetInt(); + if ( signature != STRING_POOL_VERSION ) + { + // wrong 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 // STRINGPOOL_H diff --git a/r5dev/tier1/utlmap.h b/r5dev/tier1/utlmap.h new file mode 100644 index 00000000..1a769137 --- /dev/null +++ b/r5dev/tier1/utlmap.h @@ -0,0 +1,295 @@ +//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLMAP_H +#define UTLMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "utlrbtree.h" + +//----------------------------------------------------------------------------- +// +// Purpose: An associative container. Pretty much identical to std::map. +// +//----------------------------------------------------------------------------- + +// This is a useful macro to iterate from start to end in order in a map +#define FOR_EACH_MAP( mapName, iteratorName ) \ + for ( int iteratorName = (mapName).FirstInorder(); (mapName).IsUtlMap && iteratorName != (mapName).InvalidIndex(); iteratorName = (mapName).NextInorder( iteratorName ) ) + +// faster iteration, but in an unspecified order +#define FOR_EACH_MAP_FAST( mapName, iteratorName ) \ + for ( int iteratorName = 0; (mapName).IsUtlMap && iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else + + +struct base_utlmap_t +{ +public: + // This enum exists so that FOR_EACH_MAP and FOR_EACH_MAP_FAST cannot accidentally + // be used on a type that is not a CUtlMap. If the code compiles then all is well. + // The check for IsUtlMap being true should be free. + // Using an enum rather than a static const bool ensures that this trick works even + // with optimizations disabled on gcc. + enum { IsUtlMap = true }; +}; + +template +class CUtlMap : public base_utlmap_t +{ +public: + typedef K KeyType_t; + typedef T ElemType_t; + typedef I IndexType_t; + + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below + CUtlMap( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ) + : m_Tree( growSize, initSize, CKeyLess( lessfunc ) ) + { + } + + CUtlMap( LessFunc_t lessfunc ) + : m_Tree( CKeyLess( lessfunc ) ) + { + } + + void EnsureCapacity( int num ) { m_Tree.EnsureCapacity( num ); } + + // gets particular elements + ElemType_t & Element( IndexType_t i ) { return m_Tree.Element( i ).elem; } + const ElemType_t & Element( IndexType_t i ) const { return m_Tree.Element( i ).elem; } + ElemType_t & operator[]( IndexType_t i ) { return m_Tree.Element( i ).elem; } + const ElemType_t & operator[]( IndexType_t i ) const { return m_Tree.Element( i ).elem; } + KeyType_t & Key( IndexType_t i ) { return m_Tree.Element( i ).key; } + const KeyType_t & Key( IndexType_t i ) const { return m_Tree.Element( i ).key; } + + + // Num elements + unsigned int Count() const { return m_Tree.Count(); } + + // Max "size" of the vector + IndexType_t MaxElement() const { return m_Tree.MaxElement(); } + + // Checks if a node is valid and in the map + bool IsValidIndex( IndexType_t i ) const { return m_Tree.IsValidIndex( i ); } + + // Checks if the map as a whole is valid + bool IsValid() const { return m_Tree.IsValid(); } + + // Invalid index + static IndexType_t InvalidIndex() { return CTree::InvalidIndex(); } + + // Sets the less func + void SetLessFunc( LessFunc_t func ) + { + m_Tree.SetLessFunc( CKeyLess( func ) ); + } + + // Insert method (inserts in order) + IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) + { + Node_t node; + node.key = key; + node.elem = insert; + return m_Tree.Insert( node ); + } + + IndexType_t Insert( const KeyType_t &key ) + { + Node_t node; + node.key = key; + return m_Tree.Insert( node ); + } + + // API to match src2 for Panorama + // Note in src2 straight Insert() calls will assert on duplicates + // Choosing not to take that change until discussed further + + IndexType_t InsertWithDupes( const KeyType_t &key, const ElemType_t &insert ) + { + Node_t node; + node.key = key; + node.elem = insert; + return m_Tree.Insert( node ); + } + + IndexType_t InsertWithDupes( const KeyType_t &key ) + { + Node_t node; + node.key = key; + return m_Tree.Insert( node ); + } + + + bool HasElement( const KeyType_t &key ) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.HasElement( dummyNode ); + } + + + // Find method + IndexType_t Find( const KeyType_t &key ) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Find( dummyNode ); + } + + // FindFirst method + // This finds the first inorder occurrence of key + IndexType_t FindFirst( const KeyType_t &key ) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.FindFirst( dummyNode ); + } + + + const ElemType_t &FindElement( const KeyType_t &key, const ElemType_t &defaultValue ) const + { + IndexType_t i = Find( key ); + if ( i == InvalidIndex() ) + return defaultValue; + return Element( i ); + } + + + // First element >= key + IndexType_t FindClosest( const KeyType_t &key, CompareOperands_t eFindCriteria ) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.FindClosest( dummyNode, eFindCriteria ); + } + + // Remove methods + void RemoveAt( IndexType_t i ) { m_Tree.RemoveAt( i ); } + bool Remove( const KeyType_t &key ) + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Remove( dummyNode ); + } + + void RemoveAll( ) { m_Tree.RemoveAll(); } + void Purge( ) { m_Tree.Purge(); } + + // Purges the list and calls delete on each element in it. + void PurgeAndDeleteElements(); + + // Iteration + IndexType_t FirstInorder() const { return m_Tree.FirstInorder(); } + IndexType_t NextInorder( IndexType_t i ) const { return m_Tree.NextInorder( i ); } + IndexType_t PrevInorder( IndexType_t i ) const { return m_Tree.PrevInorder( i ); } + IndexType_t LastInorder() const { return m_Tree.LastInorder(); } + + // API Matching src2 for Panorama + IndexType_t NextInorderSameKey( IndexType_t i ) const + { + IndexType_t iNext = NextInorder( i ); + if ( !IsValidIndex( iNext ) ) + return InvalidIndex(); + if ( Key( iNext ) != Key( i ) ) + return InvalidIndex(); + return iNext; + } + + // If you change the search key, this can be used to reinsert the + // element into the map. + void Reinsert( const KeyType_t &key, IndexType_t i ) + { + m_Tree[i].key = key; + m_Tree.Reinsert(i); + } + + IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) + { + IndexType_t i = Find( key ); + if ( i != InvalidIndex() ) + { + Element( i ) = insert; + return i; + } + + return Insert( key, insert ); + } + + void Swap( CUtlMap< K, T, I > &that ) + { + m_Tree.Swap( that.m_Tree ); + } + + + struct Node_t + { + Node_t() + { + } + + Node_t( const Node_t &from ) + : key( from.key ), + elem( from.elem ) + { + } + + KeyType_t key; + ElemType_t elem; + }; + + class CKeyLess + { + public: + CKeyLess( const LessFunc_t& lessFunc ) : m_LessFunc(lessFunc) {} + + bool operator!() const + { + return !m_LessFunc; + } + + bool operator()( const Node_t &left, const Node_t &right ) const + { + return m_LessFunc( left.key, right.key ); + } + + LessFunc_t m_LessFunc; + }; + + typedef CUtlRBTree CTree; + + CTree *AccessTree() { return &m_Tree; } + +protected: + CTree m_Tree; +}; + +//----------------------------------------------------------------------------- + +// Purges the list and calls delete on each element in it. +template< typename K, typename T, typename I, typename LessFunc_t > +inline void CUtlMap::PurgeAndDeleteElements() +{ + for ( I i = 0; i < MaxElement(); ++i ) + { + if ( !IsValidIndex( i ) ) + continue; + + delete Element( i ); + } + + Purge(); +} + +#endif // UTLMAP_H diff --git a/r5dev/tier1/utlsymbol.h b/r5dev/tier1/utlsymbol.h new file mode 100644 index 00000000..3133559d --- /dev/null +++ b/r5dev/tier1/utlsymbol.h @@ -0,0 +1,380 @@ +//==== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =====// +// +// Purpose: Defines a symbol table +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTLSYMBOL_H +#define UTLSYMBOL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/threadtools.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utllinkedlist.h" +#include "tier1/stringpool.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class CUtlSymbolTable; +class CUtlSymbolTableMT; + + +//----------------------------------------------------------------------------- +// This is a symbol, which is a easier way of dealing with strings. +//----------------------------------------------------------------------------- +typedef unsigned short UtlSymId_t; + +#define UTL_INVAL_SYMBOL ((UtlSymId_t)~0) + +class CUtlSymbol +{ +public: + // constructor, destructor + CUtlSymbol() : m_Id(UTL_INVAL_SYMBOL) {} + CUtlSymbol( UtlSymId_t id ) : m_Id(id) {} + CUtlSymbol( const char* pStr ); + CUtlSymbol( CUtlSymbol const& sym ) : m_Id(sym.m_Id) {} + + // operator= + CUtlSymbol& operator=( CUtlSymbol const& src ) { m_Id = src.m_Id; return *this; } + + // operator== + bool operator==( CUtlSymbol const& src ) const { return m_Id == src.m_Id; } + bool operator==( const char* pStr ) const; + + // Is valid? + bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; } + + // Gets at the symbol + operator UtlSymId_t () const { return m_Id; } + + // Gets the string associated with the symbol + const char* String( ) const; + + // Modules can choose to disable the static symbol table so to prevent accidental use of them. + static void DisableStaticSymbolTable(); + + // Methods with explicit locking mechanism. Only use for optimization reasons. + static void LockTableForRead(); + static void UnlockTableForRead(); + const char * StringNoLock() const; + +protected: + UtlSymId_t m_Id; + + // Initializes the symbol table + static void Initialize(); + + // returns the current symbol table + static CUtlSymbolTableMT* CurrTable(); + + // The standard global symbol table + static CUtlSymbolTableMT* s_pSymbolTable; + + static bool s_bAllowStaticSymbolTable; + + friend class CCleanupUtlSymbolTable; +}; + + +//----------------------------------------------------------------------------- +// CUtlSymbolTable: +// description: +// This class defines a symbol table, which allows us to perform mappings +// of strings to symbols and back. The symbol class itself contains +// a static version of this class for creating global strings, but this +// class can also be instanced to create local symbol tables. +// +// This class stores the strings in a series of string pools. The first +// two bytes of each string are decorated with a hash to speed up +// comparisons. +//----------------------------------------------------------------------------- + +class CUtlSymbolTable +{ +public: + // constructor, destructor + CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); + ~CUtlSymbolTable(); + + // Finds and/or creates a symbol based on the string + CUtlSymbol AddString( const char* pString ); + + // Finds the symbol for pString + CUtlSymbol Find( const char* pString ) const; + + // Look up the string associated with a particular symbol + const char* String( CUtlSymbol id ) const; + + inline bool HasElement(const char* pStr) const + { + return Find(pStr) != UTL_INVAL_SYMBOL; + } + + // Remove all symbols in the table. + void RemoveAll(); + + int GetNumStrings( void ) const + { + return m_Lookup.Count(); + } + + // We store one of these at the beginning of every string to speed + // up comparisons. + typedef unsigned short hashDecoration_t; + +protected: + class CStringPoolIndex + { + public: + inline CStringPoolIndex() + { + } + + inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset ) + : m_iPool(iPool), m_iOffset(iOffset) + {} + + inline bool operator==( const CStringPoolIndex &other ) const + { + return m_iPool == other.m_iPool && m_iOffset == other.m_iOffset; + } + + unsigned short m_iPool; // Index into m_StringPools. + unsigned short m_iOffset; // Index into the string pool. + }; + + class CLess + { + 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; + }; + + // Stores the symbol lookup + class CTree : public CUtlRBTree + { + public: + CTree( int growSize, int initSize ) : CUtlRBTree( 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; + char m_Data[1]; + }; + + CTree m_Lookup; + + bool m_bInsensitive; + mutable unsigned short m_nUserSearchStringHash; + mutable const char* m_pUserSearchString; + + // stores the string data + CUtlVector m_StringPools; + +private: + int FindPoolWithSpace( int len ) const; + const char* StringFromIndex( const CStringPoolIndex &index ) const; + const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; + + friend class CLess; + friend class CSymbolHash; + +}; + +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 +}; + + + +//----------------------------------------------------------------------------- +// CUtlFilenameSymbolTable: +// description: +// This class defines a symbol table of individual filenames, stored more +// efficiently than a standard symbol table. Internally filenames are broken +// up into file and path entries, and a file handle class allows convenient +// access to these. +//----------------------------------------------------------------------------- + +// The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor +// copies them into a static char buffer for return. +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; + +#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 m_PathStringPool; + CCountedStringPoolBase 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 +// manage those symbols. Avoids the possibility of the code polluting the +// 'global'/default symbol table, while letting the code look like +// it's just using = and .String() to look at CUtlSymbol type objects +// +// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course) +// +#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \ + class typename \ + { \ + public: \ + typename(); \ + typename( const char* pStr ); \ + typename& operator=( typename const& src ); \ + bool operator==( typename const& src ) const; \ + const char* String( ) const; \ + private: \ + CUtlSymbol m_SymbolId; \ + }; + +// Put this in the .cpp file that uses the above typename +#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \ + static CUtlSymbolTable g_##typename##SymbolTable; \ + typename::typename() \ + { \ + m_SymbolId = UTL_INVAL_SYMBOL; \ + } \ + typename::typename( const char* pStr ) \ + { \ + m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \ + } \ + typename& typename::operator=( typename const& src ) \ + { \ + m_SymbolId = src.m_SymbolId; \ + return *this; \ + } \ + bool typename::operator==( typename const& src ) const \ + { \ + return ( m_SymbolId == src.m_SymbolId ); \ + } \ + const char* typename::String( ) const \ + { \ + return g_##typename##SymbolTable.String( m_SymbolId ); \ + } + +#endif // UTLSYMBOL_H