diff --git a/lib/public/tier1.lib b/lib/public/tier1.lib index 0a2d23391..f42058bba 100644 Binary files a/lib/public/tier1.lib and b/lib/public/tier1.lib differ diff --git a/linux_sdk/Makefile.tier1 b/linux_sdk/Makefile.tier1 index 1b451ce7e..6e095fb4b 100644 --- a/linux_sdk/Makefile.tier1 +++ b/linux_sdk/Makefile.tier1 @@ -56,6 +56,7 @@ LIB_OBJS= \ $(LIB_OBJ_DIR)/utlbufferutil.o \ $(LIB_OBJ_DIR)/utlstring.o \ $(LIB_OBJ_DIR)/utlsymbol.o \ + $(LIB_OBJ_DIR)/murmurhash3.o \ all: dirs $(NAME)_$(ARCH).$(SHLIBEXT) diff --git a/linux_sdk/x86-64/Makefile.tier1 b/linux_sdk/x86-64/Makefile.tier1 index cd2547cd8..5e5f6d3d1 100644 --- a/linux_sdk/x86-64/Makefile.tier1 +++ b/linux_sdk/x86-64/Makefile.tier1 @@ -56,6 +56,7 @@ LIB_OBJS= \ $(LIB_OBJ_DIR)/utlbufferutil.o \ $(LIB_OBJ_DIR)/utlstring.o \ $(LIB_OBJ_DIR)/utlsymbol.o \ + $(LIB_OBJ_DIR)/murmurhash3.o \ all: dirs $(NAME).$(SHLIBEXT) diff --git a/public/bitvec.h b/public/bitvec.h index f8e17fcb3..e6120f218 100644 --- a/public/bitvec.h +++ b/public/bitvec.h @@ -13,7 +13,7 @@ #include #include "tier0/dbg.h" #include "tier0/basetypes.h" - +#include "tier1/strtools.h" class CBitVecAccessor { @@ -46,7 +46,7 @@ inline int FirstBitInWord( unsigned int elem, int offset ) #if _WIN32 if ( !elem ) return -1; -#if _X360 +#if defined( _X360 ) // this implements CountTrailingZeros() / BitScanForward() unsigned int mask = elem-1; unsigned int comp = ~elem; @@ -201,6 +201,38 @@ inline int GetBitForBitnumByte( int bitNum ) inline int CalcNumIntsForBits( int numBits ) { return (numBits + (BITS_PER_INT-1)) / BITS_PER_INT; } +// http://bits.stephan-brumme.com/PopulationCount.html +// http://graphics.stanford.edu/~seander/bithacks.html#PopulationCountSetParallel +inline uint PopulationCount( uint32 v ) +{ + uint32 const w = v - ((v >> 1) & 0x55555555); + uint32 const x = (w & 0x33333333) + ((w >> 2) & 0x33333333); + return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +inline uint PopulationCount( uint64 v ) +{ + uint64 const w = v - ((v >> 1) & 0x5555555555555555ull); + uint64 const x = (w & 0x3333333333333333ull) + ((w >> 2) & 0x3333333333333333ull); + return ( ( x + ( x >> 4 ) & 0x0F0F0F0F0F0F0F0Full ) * 0x0101010101010101ull ) >> 56; // [Sergiy] I'm not sure if it's faster to multiply here to reduce the bit sum further first, so feel free to optimize, please +} + +inline uint PopulationCount( uint16 v ) +{ + uint16 const w = v - ((v >> 1) & 0x5555); + uint16 const x = (w & 0x3333) + ((w >> 2) & 0x3333); + return ( ( x + ( x >> 4 ) & 0x0F0F) * 0x101 ) >> 8; +} + + +inline uint PopulationCount( uint8 v ) +{ + uint8 const w = v - ( ( v >> 1 ) & 0x55); + uint8 const x = ( w & 0x33 ) + ( ( w >> 2 ) & 0x33 ); + return x + ( x >> 4 ) & 0x0F; +} + + #ifdef _X360 #define BitVec_Bit( bitNum ) GetBitForBitnum( bitNum ) #define BitVec_BitInByte( bitNum ) GetBitForBitnumByte( bitNum ) @@ -226,12 +258,13 @@ class CBitVecT : public BASE_OPS { public: CBitVecT(); - CBitVecT(int numBits); // Must be initialized with the number of bits + explicit CBitVecT(int numBits); // Must be initialized with the number of bits void Init(int val = 0); // Access the bits like an array. CBitVecAccessor operator[](int i); + uint32 operator[]( int i )const; // Do NOT override bitwise operators (see note in header) void And(const CBitVecT &andStr, CBitVecT *out) const; @@ -264,6 +297,7 @@ class CBitVecT : public BASE_OPS uint32 GetDWord(int i) const; void SetDWord(int i, uint32 val); + uint PopulationCount()const; CBitVecT& operator=(const CBitVecT &other) { other.CopyTo( this ); return *this; } bool operator==(const CBitVecT &other) { return Compare( other ); } @@ -276,7 +310,7 @@ class CBitVecT : public BASE_OPS // class CVarBitVecBase // // Defines the operations necessary for a variable sized bit array - +template class CVarBitVecBase { public: @@ -295,19 +329,19 @@ class CVarBitVecBase protected: CVarBitVecBase(); - CVarBitVecBase(int numBits); - CVarBitVecBase( const CVarBitVecBase &from ); - CVarBitVecBase &operator=( const CVarBitVecBase &from ); + explicit CVarBitVecBase(int numBits); + CVarBitVecBase( const CVarBitVecBase &from ); + CVarBitVecBase &operator=( const CVarBitVecBase &from ); ~CVarBitVecBase(void); - void ValidateOperand( const CVarBitVecBase &operand ) const { Assert(GetNumBits() == operand.GetNumBits()); } + void ValidateOperand( const CVarBitVecBase &operand ) const { Assert(GetNumBits() == operand.GetNumBits()); } unsigned GetEndMask() const { return ::GetEndMask( GetNumBits() ); } private: - unsigned short m_numBits; // Number of bits in the bitstring - unsigned short m_numInts; // Number of ints to needed to store bitstring + BITCOUNTTYPE m_numBits; // Number of bits in the bitstring + BITCOUNTTYPE m_numInts; // Number of ints to needed to store bitstring uint32 m_iBitStringStorage; // If the bit string fits in one int, it goes here uint32 * m_pInt; // Array of ints containing the bitstring @@ -374,7 +408,11 @@ class CFixedBitVecBase protected: CFixedBitVecBase() {} - CFixedBitVecBase(int numBits) { Assert( numBits == NUM_BITS ); } // doesn't make sense, really. Supported to simplify templates & allow easy replacement of variable + explicit CFixedBitVecBase(int numBits) + { + // doesn't make sense, really. Supported to simplify templates & allow easy replacement of variable + Assert( numBits == NUM_BITS ); + } void ValidateOperand( const CFixedBitVecBase &operand ) const { } // no need, compiler does so statically @@ -396,15 +434,36 @@ class CFixedBitVecBase // // inheritance instead of typedef to allow forward declarations -class CVarBitVec : public CBitVecT +template< class CountType = unsigned short > +class CVarBitVecT : public CBitVecT< CVarBitVecBase< CountType > > { public: - CVarBitVec() + CVarBitVecT() { } - CVarBitVec(int numBits) - : CBitVecT(numBits) + explicit CVarBitVecT(int numBits) + : CBitVecT >(numBits) + { + } +}; + +class CVarBitVec : public CVarBitVecT +{ +public: + CVarBitVec() : CVarBitVecT() {} + explicit CVarBitVec( int nBitCount ) : CVarBitVecT(nBitCount) {} +}; + +class CLargeVarBitVec : public CBitVecT< CVarBitVecBase > +{ +public: + CLargeVarBitVec() + { + } + + explicit CLargeVarBitVec(int numBits) + : CBitVecT< CVarBitVecBase >(numBits) { } }; @@ -419,7 +478,7 @@ class CBitVec : public CBitVecT< CFixedBitVecBase > { } - CBitVec(int numBits) + explicit CBitVec(int numBits) : CBitVecT< CFixedBitVecBase >(numBits) { } @@ -432,14 +491,16 @@ typedef CBitVec<32> CDWordBitVec; //----------------------------------------------------------------------------- -inline CVarBitVecBase::CVarBitVecBase() +template +inline CVarBitVecBase::CVarBitVecBase() { Plat_FastMemset( this, 0, sizeof( *this ) ); } //----------------------------------------------------------------------------- -inline CVarBitVecBase::CVarBitVecBase(int numBits) +template +inline CVarBitVecBase::CVarBitVecBase(int numBits) { Assert( numBits ); m_numBits = numBits; @@ -452,7 +513,8 @@ inline CVarBitVecBase::CVarBitVecBase(int numBits) //----------------------------------------------------------------------------- -inline CVarBitVecBase::CVarBitVecBase( const CVarBitVecBase &from ) +template +inline CVarBitVecBase::CVarBitVecBase( const CVarBitVecBase &from ) { if ( from.m_numInts ) { @@ -468,7 +530,8 @@ inline CVarBitVecBase::CVarBitVecBase( const CVarBitVecBase &from ) //----------------------------------------------------------------------------- -inline CVarBitVecBase &CVarBitVecBase::operator=( const CVarBitVecBase &from ) +template +inline CVarBitVecBase &CVarBitVecBase::operator=( const CVarBitVecBase &from ) { Resize( from.GetNumBits() ); if ( m_pInt ) @@ -482,14 +545,16 @@ inline CVarBitVecBase &CVarBitVecBase::operator=( const CVarBitVecBase &from ) // Output : //----------------------------------------------------------------------------- -inline CVarBitVecBase::~CVarBitVecBase(void) +template +inline CVarBitVecBase::~CVarBitVecBase(void) { FreeInts(); } //----------------------------------------------------------------------------- -inline void CVarBitVecBase::Attach( uint32 *pBits, int numBits ) +template +inline void CVarBitVecBase::Attach( uint32 *pBits, int numBits ) { FreeInts(); m_numBits = numBits; @@ -508,7 +573,8 @@ inline void CVarBitVecBase::Attach( uint32 *pBits, int numBits ) //----------------------------------------------------------------------------- -inline bool CVarBitVecBase::Detach( uint32 **ppBits, int *pNumBits ) +template +inline bool CVarBitVecBase::Detach( uint32 **ppBits, int *pNumBits ) { if ( !m_numBits ) { @@ -568,6 +634,12 @@ inline CBitVecAccessor CBitVecT::operator[](int i) return CBitVecAccessor(this->Base(), i); } +template +inline uint32 CBitVecT::operator[]( int i )const +{ + Assert( i >= 0 && i < this->GetNumBits() ); + return this->Base()[ i >> 5 ] & ( 1 << ( i & 31 ) ); +} //----------------------------------------------------------------------------- @@ -743,7 +815,7 @@ inline void CBitVecT::Xor(const CBitVecT &xorStr, CBitVecT *out) const template inline void CBitVecT::Not(CBitVecT *out) const { - ValidateOperand( *out ); + this->ValidateOperand( *out ); uint32 * pDest = out->Base(); const uint32 *pOperand = this->Base(); @@ -851,7 +923,7 @@ inline void CBitVecT::Copy( const CBitVecT &other, int nBits this->Resize( nBits ); - ValidateOperand( other ); + this->ValidateOperand( other ); Assert( &other != this ); memcpy( this->Base(), other.Base(), this->GetNumDWords() * sizeof( uint32 ) ); @@ -900,6 +972,20 @@ inline void CBitVecT::SetDWord(int i, uint32 val) this->Base()[i] = val; } +//----------------------------------------------------------------------------- +template +inline uint32 CBitVecT::PopulationCount() const +{ + int nDwordCount = this->GetNumDWords(); + const uint32 *pBase = this->Base(); + uint32 nCount = 0; + for( int i = 0; i < nDwordCount; ++i ) + { + nCount += ::PopulationCount( pBase[i] ); + } + return nCount; +} + //----------------------------------------------------------------------------- inline unsigned GetStartBitMask( int startBit ) @@ -943,7 +1029,130 @@ inline unsigned GetStartBitMask( int startBit ) return g_StartMask[ startBit & 31 ]; } -inline int CVarBitVecBase::FindNextSetBit( int startBit ) const + +#ifdef _X360 +#define BitVec_Bit( bitNum ) GetBitForBitnum( bitNum ) +#define BitVec_BitInByte( bitNum ) GetBitForBitnumByte( bitNum ) +#else +#define BitVec_Bit( bitNum ) ( 1 << ( (bitNum) & (BITS_PER_INT-1) ) ) +#define BitVec_BitInByte( bitNum ) ( 1 << ( (bitNum) & 7 ) ) +#endif +#define BitVec_Int( bitNum ) ( (bitNum) >> LOG2_BITS_PER_INT ) + +inline bool BitVec_IsBitSet( const uint32 *pBase, int nBitNum ) +{ + const uint32 *pInt = pBase + BitVec_Int( nBitNum ); + return ( ( *pInt & BitVec_Bit( nBitNum ) ) != 0 ); +} + +inline void Bitvec_Set( uint32 *pBits, int nBitNum ) +{ + uint32 *pInt = pBits + BitVec_Int( nBitNum ); + *pInt |= BitVec_Bit( nBitNum ); +} + +inline void Bitvec_Unset( uint32 *pBits, int nBitNum ) +{ + uint32 *pInt = pBits + BitVec_Int( nBitNum ); + *pInt &= ~BitVec_Bit( nBitNum ); +} + +inline bool BitVec_TestAndSetBit( uint32 *pBase, int nBitNum ) +{ + uint32 *pInt = pBase + BitVec_Int( nBitNum ); + uint32 nBits = *pInt; + uint32 nSetMask = BitVec_Bit( nBitNum ); + *pInt = nBits | nSetMask; + return ( nBits & nSetMask ) ? true : false; +} + +inline void BitVec_ClearAll( uint32 *pDst, int nDWords, uint32 nEndMask ) +{ + if ( nDWords > 1 ) + { + V_memset( pDst, 0, ( nDWords - 1 ) * sizeof( uint32 ) ); + } + pDst[ nDWords - 1 ] &= ~nEndMask; +} + +inline void BitVec_ClearAll( uint32 *pDst, int nDWords ) +{ + V_memset( pDst, 0, nDWords * sizeof( uint32 ) ); +} + +inline void BitVec_SetAll( uint32 *pDst, int nDWords, uint32 nEndMask ) +{ + if ( nDWords > 1 ) + { + V_memset( pDst, 0xff, ( nDWords - 1 ) * sizeof( uint32 ) ); + } + pDst[ nDWords - 1 ] |= nEndMask; +} + +inline void BitVec_SetAll( uint32 *pDst, int nDWords ) +{ + V_memset( pDst, 0xff, nDWords * sizeof( uint32 ) ); +} + +inline void BitVec_Or( uint32 *pDst, const uint32 *pSrc, int nDWords, uint32 nEndMask ) +{ + uint32 *pEnd = pDst + nDWords - 1; + for ( ; pDst < pEnd; ++pSrc, ++pDst ) + { + *pDst = *pDst | *pSrc; + } + *pDst = *pDst | ( *pSrc & nEndMask ); +} + +inline void BitVec_Or( uint32 *pDst, const uint32 *pSrc, int nDWords ) +{ + uint32 *pEnd = pDst + nDWords; + for ( ; pDst < pEnd; ++pSrc, ++pDst ) + { + *pDst = *pDst | *pSrc; + } +} + +inline void BitVec_AndNot( uint32 *pDst, const uint32 *pSrc, const uint32 *pAndNot, int nDWords ) +{ + uint32 *pEnd = pDst + nDWords; + for ( ; pDst < pEnd; ++pSrc, ++pDst, ++pAndNot ) + { + *pDst = *pSrc & ~( *pAndNot ); + } +} + +inline bool BitVec_IsAnySet( const uint32 *pDst, int nDWords ) +{ + const uint32 *pEnd = pDst + nDWords; + for ( ; pDst < pEnd; ++pDst ) + { + if ( *pDst != 0 ) + return true; + } + return false; +} + +inline int BitVec_CountNewBits( const uint32 *pOld, const uint32 *pNew, int nDWords, uint32 nEndMask ) +{ + // NOTE - this assumes that any unused bits are unchanged between pOld and pNew + + int nNewBits = 0; + + const uint32 *pEnd = pNew + nDWords - 1; + for ( ; pNew < pEnd; ++pOld, ++pNew ) + { + uint32 diff = *pNew & ~*pOld; + nNewBits += PopulationCount( diff ); + } + nNewBits += PopulationCount( ( *pNew & ~*pOld ) & nEndMask ); + + return nNewBits; +} + + +template +inline int CVarBitVecBase::FindNextSetBit( int startBit ) const { if ( startBit < GetNumBits() ) { @@ -1276,9 +1485,10 @@ inline void CBitVecT< CFixedBitVecBase<32> >::Set( int bitNum, bool bNewVal ) // Purpose: Resizes the bit string to a new number of bits // Input : resizeNumBits - //----------------------------------------------------------------------------- -inline void CVarBitVecBase::Resize( int resizeNumBits, bool bClearAll ) +template +inline void CVarBitVecBase::Resize( int resizeNumBits, bool bClearAll ) { - Assert( resizeNumBits >= 0 && resizeNumBits <= USHRT_MAX ); + Assert( resizeNumBits >= 0 && ((BITCOUNTTYPE)resizeNumBits == resizeNumBits) ); int newIntCount = CalcNumIntsForBits( resizeNumBits ); if ( newIntCount != GetNumDWords() ) @@ -1320,7 +1530,8 @@ inline void CVarBitVecBase::Resize( int resizeNumBits, bool bClearAll ) // Purpose: Allocate the storage for the ints // Input : numInts - //----------------------------------------------------------------------------- -inline void CVarBitVecBase::AllocInts( int numInts ) +template +inline void CVarBitVecBase::AllocInts( int numInts ) { Assert( !m_pInt ); @@ -1341,7 +1552,8 @@ inline void CVarBitVecBase::AllocInts( int numInts ) // Purpose: Reallocate the storage for the ints // Input : numInts - //----------------------------------------------------------------------------- -inline void CVarBitVecBase::ReallocInts( int numInts ) +template +inline void CVarBitVecBase::ReallocInts( int numInts ) { Assert( Base() ); if ( numInts == 0) @@ -1376,7 +1588,8 @@ inline void CVarBitVecBase::ReallocInts( int numInts ) //----------------------------------------------------------------------------- // Purpose: Free storage allocated with AllocInts //----------------------------------------------------------------------------- -inline void CVarBitVecBase::FreeInts( void ) +template +inline void CVarBitVecBase::FreeInts( void ) { if ( m_numInts > 1 ) { diff --git a/public/inetchannelinfo.h b/public/inetchannelinfo.h index 12b0abbe6..8474fcfd2 100644 --- a/public/inetchannelinfo.h +++ b/public/inetchannelinfo.h @@ -40,8 +40,16 @@ class INetChannelInfo MOVE, // client move cmds STRINGCMD, // string command SIGNON, // various signondata + PAINTMAP, // paintmap data + ENCRYPTED, // encrypted data TOTAL, // must be last and is not a real group }; + + enum ENetworkEventType_t + { + k_ENetworkEventType_Generic = 0, // Generic network event + k_ENetworkEventType_TimedOut = 1, // Network connection timed out + }; virtual const char *GetName( void ) const = 0; // get channel name virtual const char *GetAddress( void ) const = 0; // get channel IP address as string @@ -66,7 +74,7 @@ class INetChannelInfo virtual bool IsValidPacket( int flow, int frame_number ) const = 0; // true if packet was not lost/dropped/chocked/flushed virtual float GetPacketTime( int flow, int frame_number ) const = 0; // time when packet was send virtual int GetPacketBytes( int flow, int frame_number, int group ) const = 0; // group size of this packet - virtual bool GetStreamProgress( int flow, int *received, int *total ) const = 0; // TCP progress if transmitting + virtual bool GetStreamProgress( int flow, int *received, int *total ) const = 0; // TCP progress if transmitting virtual float GetTimeSinceLastReceived( void ) const = 0; // get time since last recieved packet in seconds virtual float GetCommandInterpolationAmount( int flow, int frame_number ) const = 0; virtual void GetPacketResponseLatency( int flow, int frame_number, int *pnLatencyMsecs, int *pnChoke ) const = 0; diff --git a/public/networkvar.h b/public/networkvar.h index 51724d845..af468a647 100644 --- a/public/networkvar.h +++ b/public/networkvar.h @@ -12,6 +12,7 @@ #include "tier0/dbg.h" +#include "tier0/platform.h" #include "convar.h" #if defined( CLIENT_DLL ) || defined( GAME_DLL ) @@ -22,18 +23,35 @@ #pragma warning( disable : 4284 ) // warning C4284: return type for 'CNetworkVarT::operator ->' is 'int *' (ie; not a UDT or reference to a UDT. Will produce errors if applied using infix notation) #endif -#define MyOffsetOf( type, var ) ( (int)&((type*)0)->var ) +#define MyOffsetOf( type, var ) ( (int)(intp)&((type*)0)->var ) #ifdef _DEBUG #undef new extern bool g_bUseNetworkVars; #define CHECK_USENETWORKVARS if(g_bUseNetworkVars) + + // Use START_ and END_CHECK_USENETWORKVARS if your code is longer than one line. + #define START_CHECK_USENETWORKVARS if(g_bUseNetworkVars) { + #define END_CHECK_USENETWORKVARS } #else #define CHECK_USENETWORKVARS // don't check for g_bUseNetworkVars + #define START_CHECK_USENETWORKVARS + #define END_CHECK_USENETWORKVARS #endif +// +// Networkvar flags. +// +#define NETWORKVAR_IS_A_VECTOR 0x0001 // Is it any type of network vector? +#define NETWORKVAR_VECTOR_XYZ_FLAG 0x0002 // Is it a CNetworkVectorXYZ? +#define NETWORKVAR_VECTOR_XY_SEPARATEZ_FLAG 0x0004 // Is it a CNetworkVectorXY_SeparateZ? + +#define NETWORKVAR_ALL_FLAGS ( NETWORKVAR_IS_A_VECTOR | NETWORKVAR_VECTOR_XYZ_FLAG | NETWORKVAR_VECTOR_XY_SEPARATEZ_FLAG ) + + + // network vars use memcmp when fields are set. To ensure proper behavior your // object's memory should be initialized to zero. This happens for entities automatically // use this for other classes. @@ -189,43 +207,116 @@ static inline void DispatchNetworkStateChanged( T *pObj, void *pVar ) CHECK_USENETWORKVARS pObj->NetworkStateChanged( pVar ); } - #define DECLARE_EMBEDDED_NETWORKVAR() \ - template friend int ServerClassInit(T *); \ - template friend int ClientClassInit(T *); \ - virtual void NetworkStateChanged() {} virtual void NetworkStateChanged( void *pProp ) {} + template friend int ServerClassInit( T * ); \ + template friend int ClientClassInit( T * ); \ + virtual void NetworkStateChanged() {} virtual void NetworkStateChanged( void *pProp ) {} + +template < typename T, typename ContainingClass, typename GetOffset > +class NetworkVarEmbedded : public T +{ +private: + // NOTE: Assignment operator is disabled because it doesn't call copy constructors of scalar types within the aggregate, so they are not marked changed + template< class U > NetworkVarEmbedded& operator=( U &&val ) = delete; + + T& Data() { return *this; } + const T& Data() const { return *this; } + +public: + operator const T& () const { return Data(); } + + // $$$REI TODO: Why doesn't this implementation work as an assignment operator? + template< typename U > + void CopyFrom( U&& src ) { + Data() = Forward( src ); + NetworkStateChanged(); + } + + const T* Get( void ) const { return &Data(); } + T* Get( void ) { return &Data(); } + + ContainingClass* GetEmbeddedVarOuterPtr() { return ( ContainingClass* )( ( ( char* )this ) - GetOffset::Get() ); } + virtual void NetworkStateChanged() + { + ContainingClass *pOuter = GetEmbeddedVarOuterPtr(); + DispatchNetworkStateChanged( pOuter ); + } + + virtual void NetworkStateChanged( void *pVar ) + { + ContainingClass *pOuter = GetEmbeddedVarOuterPtr(); + DispatchNetworkStateChanged( pOuter, pVar ); + } +}; -// NOTE: Assignment operator is disabled because it doesn't call copy constructors of scalar types within the aggregate, so they are not marked changed #define CNetworkVarEmbedded( type, name ) \ - class NetworkVar_##name; \ - friend class NetworkVar_##name; \ - static inline int GetOffset_##name() { return MyOffsetOf(ThisClass,name); } \ - typedef ThisClass ThisClass_##name; \ - class NetworkVar_##name : public type \ - { \ - template< class T > NetworkVar_##name& operator=( const T &val ) { *((type*)this) = val; return *this; } \ - public: \ - void CopyFrom( const type &src ) { *((type *)this) = src; NetworkStateChanged(); } \ - type & GetForModify( void ) { NetworkStateChanged(); return *((type *)this); } \ - virtual void NetworkStateChanged() \ - { \ - DispatchNetworkStateChanged( (ThisClass_##name*)( ((char*)this) - GetOffset_##name() ) ); \ - } \ - virtual void NetworkStateChanged( void *pVar ) \ - { \ - DispatchNetworkStateChanged( (ThisClass_##name*)( ((char*)this) - GetOffset_##name() ), pVar ); \ - } \ - }; \ - NetworkVar_##name name; + struct GetOffset_##name{ static FORCEINLINE int Get() { return MyOffsetOf( ThisClass, name ); } }; \ + typedef NetworkVarEmbedded< type, ThisClass, GetOffset_##name > NetworkVar_##name; \ + friend class NetworkVarEmbedded< type, ThisClass, GetOffset_##name >; \ + NetworkVar_##name name; + +// Zero the object -- necessary for CNetworkVar and possibly other cases. +template +FORCEINLINE void EnsureValidValue( T &x ) { x = T(0); } +// For types that cannot compile the line above (such as QAngle, Vector, etc.) +// define a EnsureValidValue overload in the appropriate header files. + +class CNetworkVarFlagsBase +{ +public: + // A combination of NETWORKVAR_ flags. SendProps store these for additional information + // about what kind of CNetworkVar the SendProp is linked to. + static FORCEINLINE int GetNetworkVarFlags() { return 0; } +}; +// when we box ints into floats, the comparison of floats doesn't work + +template +inline bool NetworkParanoidUnequal( const T &a, const T &b ) +{ + return a != b; +} + +template <> +inline bool NetworkParanoidUnequal( const float &a, const float &b ) +{ + //return 0 != memcmp( &a, &b, sizeof( a ) ); + union + { + float f32; + uint32 u32; + } p, q; + p.f32 = a; + q.f32 = b; + return p.u32 != q.u32; +} + +template <> +inline bool NetworkParanoidUnequal( const double &a, const double &b ) +{ + // return 0 != memcmp( &a, &b, sizeof( a ) ); + union + { + double f64; + uint64 u64; + } p, q; + p.f64 = a; + q.f64 = b; + return p.u64 != q.u64; +} template< class Type, class Changer > -class CNetworkVarBase +class CNetworkVarBase : public CNetworkVarFlagsBase { public: CNetworkVarBase() { + // Always initialize m_Value. Not doing this means that the initial behavior + // is random. For float types this random behavior can persist arbitrarily + // long because of our non-IEEE comparisons which may make NaN be equal to + // everything. + EnsureValidValue( m_Value ); } FORCEINLINE explicit CNetworkVarBase( Type val ) @@ -243,7 +334,7 @@ class CNetworkVarBase FORCEINLINE const Type& Set( const Type &val ) { - if ( m_Value != val ) + if ( NetworkParanoidUnequal( m_Value, val ) ) { NetworkStateChanged(); m_Value = val; @@ -360,8 +451,19 @@ class CNetworkVarBase { Changer::NetworkStateChanged( this ); } + + FORCEINLINE void NetworkStateChanged( void *pVar ) + { + Changer::NetworkStateChanged( this, pVar ); + } }; + + +#include "networkvar_vector.h" + + + template< class Type, class Changer > class CNetworkColor32Base : public CNetworkVarBase< Type, Changer > { @@ -383,7 +485,7 @@ class CNetworkColor32Base : public CNetworkVarBase< Type, Changer > const Type& operator=( const Type &val ) { - return Set( val ); + return this->Set( val ); } const Type& operator=( const CNetworkColor32Base &val ) @@ -412,90 +514,7 @@ class CNetworkColor32Base : public CNetworkVarBase< Type, Changer > }; -// Network vector wrapper. -template< class Type, class Changer > -class CNetworkVectorBase : public CNetworkVarBase< Type, Changer > -{ - typedef CNetworkVarBase< Type, Changer > base; -public: - FORCEINLINE void Init( float ix=0, float iy=0, float iz=0 ) - { - base::Set( Type( ix, iy, iz ) ); - } - - FORCEINLINE const Type& operator=( const Type &val ) - { - return base::Set( val ); - } - - FORCEINLINE const Type& operator=( const CNetworkVectorBase &val ) - { - return base::Set( val.m_Value ); - } - - FORCEINLINE float GetX() const { return this->m_Value.x; } - FORCEINLINE float GetY() const { return this->m_Value.y; } - FORCEINLINE float GetZ() const { return this->m_Value.z; } - FORCEINLINE float operator[]( int i ) const { return this->m_Value[i]; } - - FORCEINLINE void SetX( float val ) { DetectChange( this->m_Value.x, val ); } - FORCEINLINE void SetY( float val ) { DetectChange( this->m_Value.y, val ); } - FORCEINLINE void SetZ( float val ) { DetectChange( this->m_Value.z, val ); } - FORCEINLINE void Set( int i, float val ) { DetectChange( this->m_Value[i], val ); } - - FORCEINLINE bool operator==( const Type &val ) const - { - return this->m_Value == (Type)val; - } - - FORCEINLINE bool operator!=( const Type &val ) const - { - return this->m_Value != (Type)val; - } - - FORCEINLINE const Type operator+( const Type &val ) const - { - return this->m_Value + val; - } - - FORCEINLINE const Type operator-( const Type &val ) const - { - return this->m_Value - val; - } - - FORCEINLINE const Type operator*( const Type &val ) const - { - return this->m_Value * val; - } - - FORCEINLINE const Type& operator*=( float val ) - { - return base::Set( this->m_Value * val ); - } - - FORCEINLINE const Type operator*( float val ) const - { - return this->m_Value * val; - } - - FORCEINLINE const Type operator/( const Type &val ) const - { - return this->m_Value / val; - } - -private: - FORCEINLINE void DetectChange( float &out, float in ) - { - if ( out != in ) - { - this->NetworkStateChanged(); - out = in; - } - } -}; - - -// Network vector wrapper. +// Network quaternion wrapper. template< class Type, class Changer > class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > { @@ -530,12 +549,12 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > bool operator==( const Type &val ) const { - return this->m_Value == (Type)val; + return !NetworkParanoidUnequal( this->m_Value, ( Type )val ); } bool operator!=( const Type &val ) const { - return this->m_Value != (Type)val; + return NetworkParanoidUnequal( this->m_Value, val ); } const Type operator+( const Type &val ) const @@ -571,7 +590,7 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > private: inline void DetectChange( float &out, float in ) { - if ( out != in ) + if ( NetworkParanoidUnequal( out, in ) ) { this->NetworkStateChanged(); out = in; @@ -664,11 +683,6 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > NETWORK_VAR_START( type, name ) \ NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged_##name ) - #define CNetworkVectorForDerived( name ) \ - virtual void NetworkStateChanged_##name() {} \ - virtual void NetworkStateChanged_##name( void *pVar ) {} \ - CNetworkVectorInternal( Vector, name, NetworkStateChanged_##name ) - #define CNetworkHandleForDerived( type, name ) \ virtual void NetworkStateChanged_##name() {} \ virtual void NetworkStateChanged_##name( void *pVar ) {} \ @@ -700,14 +714,6 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > -// Vectors + some convenient helper functions. -#define CNetworkVector( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged ) -#define CNetworkQAngle( name ) CNetworkVectorInternal( QAngle, name, NetworkStateChanged ) - -#define CNetworkVectorInternal( type, name, stateChangedFn ) \ - NETWORK_VAR_START( type, name ) \ - NETWORK_VAR_END( type, name, CNetworkVectorBase, stateChangedFn ) - #define CNetworkQuaternion( name ) \ NETWORK_VAR_START( Quaternion, name ) \ NETWORK_VAR_END( Quaternion, name, CNetworkQuaternionBase, NetworkStateChanged ) @@ -722,7 +728,7 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > class NetworkVar_##name; \ friend class NetworkVar_##name; \ typedef ThisClass MakeANetworkVar_##name; \ - class NetworkVar_##name \ + class NetworkVar_##name : public CNetworkVarFlagsBase\ { \ public: \ operator const char*() const { return m_Value; } \ @@ -735,11 +741,15 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > protected: \ inline void NetworkStateChanged() \ { \ - CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->NetworkStateChanged(); \ + START_CHECK_USENETWORKVARS \ + ThisClass *pThis = ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name))); \ + pThis->NetworkStateChanged(); \ + END_CHECK_USENETWORKVARS \ } \ private: \ char m_Value[length]; \ }; \ + typedef NetworkVar_##name NetworkVarType_##name; \ NetworkVar_##name name; @@ -788,7 +798,10 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > protected: \ inline void NetworkStateChanged( int index ) \ { \ - CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->stateChangedFn( &m_Value[index] ); \ + START_CHECK_USENETWORKVARS \ + ThisClass *pThis = ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name))); \ + pThis->stateChangedFn( &m_Value[index] ); \ + END_CHECK_USENETWORKVARS \ } \ }; \ NetworkVar_##name name; @@ -798,24 +811,33 @@ class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > // Internal macros used in definitions of network vars. -#define NETWORK_VAR_START( type, name ) \ - class NetworkVar_##name; \ - friend class NetworkVar_##name; \ - typedef ThisClass MakeANetworkVar_##name; \ - class NetworkVar_##name \ +#define NETWORK_VAR_START( networkVarType, networkVarName ) \ + class NetworkVar_##networkVarName; \ + friend class NetworkVar_##networkVarName; \ + typedef ThisClass MakeANetworkVar_##networkVarName; \ + class NetworkVar_##networkVarName \ { \ public: \ template friend int ServerClassInit(T *); -#define NETWORK_VAR_END( type, name, base, stateChangedFn ) \ +// pChangedPtr could be the Y or Z component of a vector, so possibly != to pNetworkVar. +#define NETWORK_VAR_END( networkVarType, networkVarName, baseClassName, stateChangedFn ) \ public: \ - static inline void NetworkStateChanged( void *ptr ) \ + static inline void NetworkStateChanged( void *pNetworkVar, void *pChangedPtr ) \ + { \ + START_CHECK_USENETWORKVARS \ + ThisClass *pThis = ( (ThisClass*)(((char*)pNetworkVar) - MyOffsetOf(ThisClass,networkVarName)) ); \ + pThis->stateChangedFn( pChangedPtr ); \ + END_CHECK_USENETWORKVARS \ + } \ + static inline void NetworkStateChanged( void *pNetworkVar ) \ { \ - CHECK_USENETWORKVARS ((ThisClass*)(((char*)ptr) - MyOffsetOf(ThisClass,name)))->stateChangedFn( ptr ); \ + NetworkStateChanged( pNetworkVar, pNetworkVar ); \ } \ }; \ - base< type, NetworkVar_##name > name; + typedef baseClassName< networkVarType, NetworkVar_##networkVarName > NetworkVarType_##networkVarName; \ + baseClassName< networkVarType, NetworkVar_##networkVarName > networkVarName; diff --git a/public/networkvar_vector.h b/public/networkvar_vector.h new file mode 100644 index 000000000..745ce186b --- /dev/null +++ b/public/networkvar_vector.h @@ -0,0 +1,341 @@ +//===================== Copyright (c) Valve Corporation. All Rights Reserved. ====================== +// +// Included by networkvar.h +// +//================================================================================================== + +#ifndef NETWORKVAR_VECTOR_H +#define NETWORKVAR_VECTOR_H +#ifdef _WIN32 +#pragma once +#endif + + +// This is the normal case.. you've got a SendPropVector to match your CNetworkVector +#define CNetworkVector( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged, CNetworkVectorBase ) + +// This variant of a CNetworkVector should be used if you want to use SendPropFloat +// on each individual component of the vector. +#define CNetworkVectorXYZ( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged, CNetworkVectorXYZBase ) + +// This variant of a CNetworkVector should be used if you want to use SendPropVectorXY +// for the XY components and SendPropFloat for the Z component. +#define CNetworkVectorXY_SeparateZ( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged, CNetworkVectorXY_SeparateZBase ) + + + +// This is the normal case.. you've got a SendPropQAngle to match your CNetworkQAngle +#define CNetworkQAngle( name ) CNetworkVectorInternal( QAngle, name, NetworkStateChanged, CNetworkVectorBase ) + +// This variant of a CNetworkQAngle should be used if you want to use SendPropFloat +// on each individual component of the vector. +#define CNetworkQAngleXYZ( name ) CNetworkVectorInternal( QAngle, name, NetworkStateChanged, CNetworkVectorXYZBase ) + + + +// +// Use these variants if you want the networkvar to not trigger a change in the baseclass +// version but you might want it to trigger changes in derived classes that do network that variable. +// +#define CNetworkVectorForDerived( name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + CNetworkVectorInternal( Vector, name, NetworkStateChanged_##name, CNetworkVectorBase ) + +#define CNetworkVectorXYZForDerived( name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + CNetworkVectorInternal( Vector, name, NetworkStateChanged_##name, CNetworkVectorXYZBase ) + + + + +#define CNetworkVectorInternal( type, name, stateChangedFn, baseClass ) \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, baseClass, stateChangedFn ) + + + +// Network vector wrapper. +// +// The common base is shared between all CNetworkVectors. +// It includes everything but the Set() and operator=() functions, +// because the behavior of each of those is different for each vector type. +template< class Type, class Changer > +class CNetworkVectorCommonBase : public CNetworkVarBase< Type, Changer > +{ + typedef CNetworkVarBase< Type, Changer > base; +public: + + FORCEINLINE void Init( float ix=0, float iy=0, float iz=0 ) + { + base::Set( Type( ix, iy, iz ) ); + } + + FORCEINLINE float GetX() const { return this->m_Value.x; } + FORCEINLINE float GetY() const { return this->m_Value.y; } + FORCEINLINE float GetZ() const { return this->m_Value.z; } + FORCEINLINE float operator[]( int i ) const { return this->m_Value[i]; } + + FORCEINLINE bool operator==( const Type &val ) const + { + return this->m_Value == (Type)val; + } + + FORCEINLINE bool operator!=( const Type &val ) const + { + return this->m_Value != (Type)val; + } + + FORCEINLINE const Type operator+( const Type &val ) const + { + return this->m_Value + val; + } + + FORCEINLINE const Type operator-( const Type &val ) const + { + return this->m_Value - val; + } + + FORCEINLINE const Type operator*( const Type &val ) const + { + return this->m_Value * val; + } + + FORCEINLINE const Type& operator*=( float val ) + { + return base::Set( this->m_Value * val ); + } + + FORCEINLINE const Type operator*( float val ) const + { + return this->m_Value * val; + } + + FORCEINLINE const Type operator/( const Type &val ) const + { + return this->m_Value / val; + } + +protected: + FORCEINLINE void DetectChange( float &out, float in ) + { + if ( out != in ) + { + this->NetworkStateChanged(); + out = in; + } + } +}; + + + +// +// This is for a CNetworkVector that only generates one change offset. +// It should only ever be used with SendPropVector/QAngle. +// +// Single-component things like SendPropFloat should never refer to it because +// they require the network var to report an offset for each component. +// +template< class Type, class Changer > +class CNetworkVectorBase : public CNetworkVectorCommonBase< Type, Changer > +{ + typedef CNetworkVarBase< Type, Changer > base; +public: + static FORCEINLINE int GetNetworkVarFlags() { return NETWORKVAR_IS_A_VECTOR; } + + FORCEINLINE const Type& operator=( const Type &val ) + { + return base::Set( val ); + } + + FORCEINLINE const Type& operator=( const CNetworkVectorBase &val ) + { + return base::Set( val.m_Value ); + } + FORCEINLINE void SetX( float val ) { this->DetectChange( this->m_Value.x, val ); } + FORCEINLINE void SetY( float val ) { this->DetectChange( this->m_Value.y, val ); } + FORCEINLINE void SetZ( float val ) { this->DetectChange( this->m_Value.z, val ); } + FORCEINLINE void Set( int i, float val ) { this->DetectChange( this->m_Value[i], val ); } + + FORCEINLINE const Type& operator*=( float val ) + { + return base::Set( this->m_Value * val ); + } +}; + + +// +// This variant of a CNetworkVector should be used if you want to use SendPropFloat +// on each individual component of the vector. +// +template< class Type, class Changer > +class CNetworkVectorXYZBase : public CNetworkVectorCommonBase< Type, Changer > +{ + typedef CNetworkVectorCommonBase< Type, Changer > base; +public: + + static FORCEINLINE int GetNetworkVarFlags() { return NETWORKVAR_IS_A_VECTOR | NETWORKVAR_VECTOR_XYZ_FLAG; } + + FORCEINLINE const Type& operator=( const Type &val ) + { + return Set( val ); + } + + FORCEINLINE const Type& operator=( const CNetworkVectorBase &val ) + { + return Set( val.m_Value ); + } + + FORCEINLINE const Type& Set( const Type &val ) + { + SetX( val.x ); + SetY( val.y ); + SetZ( val.z ); + + return this->m_Value; + } + + FORCEINLINE Type& GetForModify() + { + this->NetworkStateChanged( &((float*)this)[0] ); + this->NetworkStateChanged( &((float*)this)[1] ); + this->NetworkStateChanged( &((float*)this)[2] ); + return this->m_Value; + } + + FORCEINLINE const Type& SetDirect( const Type &val ) + { + GetForModify() = val; + return this->m_Value; + } + + FORCEINLINE void SetX( float val ) { DetectChange( 0, val ); } + FORCEINLINE void SetY( float val ) { DetectChange( 1, val ); } + FORCEINLINE void SetZ( float val ) { DetectChange( 2, val ); } + FORCEINLINE void Set( int i, float val ) { DetectChange( i, val ); } + + FORCEINLINE const Type& operator+=( const Type &val ) + { + return Set( this->m_Value + val ); + } + + FORCEINLINE const Type& operator-=( const Type &val ) + { + return Set( this->m_Value - val ); + } + + FORCEINLINE const Type& operator*=( float val ) + { + return Set( this->m_Value * val ); + } + + FORCEINLINE const Type& operator/=( float val ) + { + return Set( this->m_Value / val ); + } + +private: + FORCEINLINE void DetectChange( int nComponent, float in ) + { + float *pVar = &((float*)this)[nComponent]; + if ( *pVar != in ) + { + if ( pVar != &((float*)this)[0] ) + { + this->NetworkStateChanged( &((float*)this)[0] ); // Always mark the start of the vector as changed + } + this->NetworkStateChanged( pVar ); + *pVar = in; + } + } +}; + + + +// +// This variant of a CNetworkVector should be used if you want to use SendPropVectorXY +// for the XY components and SendPropFloat for the Z component. +// +template< class Type, class Changer > +class CNetworkVectorXY_SeparateZBase : public CNetworkVectorCommonBase< Type, Changer > +{ + typedef CNetworkVectorCommonBase< Type, Changer > base; +public: + + static FORCEINLINE int GetNetworkVarFlags() { return NETWORKVAR_IS_A_VECTOR | NETWORKVAR_VECTOR_XY_SEPARATEZ_FLAG; } + + FORCEINLINE const Type& operator=( const Type &val ) + { + return Set( val ); + } + + FORCEINLINE const Type& operator=( const CNetworkVectorBase &val ) + { + return Set( val.m_Value ); + } + + FORCEINLINE const Type& Set( const Type &val ) + { + SetX( val.x ); + SetY( val.y ); + SetZ( val.z ); + + return this->m_Value; + } + + FORCEINLINE Type& GetForModify() + { + this->NetworkStateChanged( &((float*)this)[0] ); // Mark the offset of our XY SendProp as changed. + this->NetworkStateChanged( &((float*)this)[2] ); // Mark the offset of our Z SendProp as changed. + return this->m_Value; + } + + FORCEINLINE const Type& SetDirect( const Type &val ) + { + GetForModify() = val; + return this->m_Value; + } + + FORCEINLINE void SetX( float val ) { DetectChange( 0, val ); } + FORCEINLINE void SetY( float val ) { DetectChange( 1, val ); } + FORCEINLINE void SetZ( float val ) { DetectChange( 2, val ); } + FORCEINLINE void Set( int i, float val ) { DetectChange( i, val ); } + + FORCEINLINE const Type& operator+=( const Type &val ) + { + return Set( this->m_Value + val ); + } + + FORCEINLINE const Type& operator-=( const Type &val ) + { + return Set( this->m_Value - val ); + } + + FORCEINLINE const Type& operator*=( float val ) + { + return Set( this->m_Value * val ); + } + + FORCEINLINE const Type& operator/=( float val ) + { + return Set( this->m_Value / val ); + } + +private: + FORCEINLINE void DetectChange( int nComponent, float in ) + { + float *pVar = &((float*)this)[nComponent]; + if ( *pVar != in ) + { + this->NetworkStateChanged( &((float*)this)[0] ); // Mark the offset of our XY SendProp as changed. + this->NetworkStateChanged( &((float*)this)[2] ); // Mark the offset of our Z SendProp as changed. + + *pVar = in; + } + } +}; + + +#endif // NETWORKVAR_VECTOR_H + diff --git a/public/tier0/annotations.h b/public/tier0/annotations.h new file mode 100644 index 000000000..d10e530e1 --- /dev/null +++ b/public/tier0/annotations.h @@ -0,0 +1,116 @@ +#ifndef ANALYSIS_ANNOTATIONS_H +#define ANALYSIS_ANNOTATIONS_H + +#if _MSC_VER >= 1600 // VS 2010 and above. +//----------------------------------------------------------------------------- +// Upgrading important helpful warnings to errors +//----------------------------------------------------------------------------- +#pragma warning(error : 4789 ) // warning C4789: destination of memory copy is too small + +// Suppress some code analysis warnings +#ifdef _PREFAST_ +// Include the annotation header file. +#include + +// /Analyze warnings can only be suppressed when using a compiler that supports them, which VS 2010 +// Professional does not. + +// We don't care about these warnings because they are bugs that can only occur during resource +// exhaustion or other unexpected API failure, which we are nowhere near being able to handle. +#pragma warning(disable : 6308) // warning C6308: 'realloc' might return null pointer: assigning null pointer to 's_ppTestCases', which is passed as an argument to 'realloc', will cause the original memory block to be leaked +#pragma warning(disable : 6255) // warning C6255: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead +#pragma warning(disable : 6387) // warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'GetProcAddress' +#pragma warning(disable : 6309) // warning C6309: Argument '1' is null: this does not adhere to function specification of 'GetProcAddress' +#pragma warning(disable : 6011) // warning C6011: Dereferencing NULL pointer 'm_ppTestCases' +#pragma warning(disable : 6211) // warning C6211: Leaking memory 'newKeyValue' due to an exception. Consider using a local catch block to clean up memory +#pragma warning(disable : 6031) // warning C6031: Return value ignored: '_getcwd' + +// These warnings are because /analyze doesn't like our use of constants, especially things like IsPC() +#pragma warning(disable : 6326) // warning C6326: Potential comparison of a constant with another constant +#pragma warning(disable : 6239) // warning C6239: ( && ) always evaluates to the result of . Did you intend to use the bitwise-and operator? +#pragma warning(disable : 6285) // warning C6285: ( || ) is always a non-zero constant. Did you intend to use the bitwise-and operator? +#pragma warning(disable : 6237) // warning C6237: ( && ) is always zero. is never evaluated and might have side effects +#pragma warning(disable : 6235) // warning C6235: ( || ) is always a non-zero constant +#pragma warning(disable : 6240) // warning C6240: ( && ) always evaluates to the result of . Did you intend to use the bitwise-and operator? + +// These warnings aren't really important: +#pragma warning(disable : 6323) // warning C6323: Use of arithmetic operator on Boolean type(s) + +// Miscellaneous other /analyze warnings. We should consider fixing these at some point. +//#pragma warning(disable : 6204) // warning C6204: Possible buffer overrun in call to 'memcpy': use of unchecked parameter 'src' +//#pragma warning(disable : 6262) // warning C6262: Function uses '16464' bytes of stack: exceeds /analyze:stacksize'16384'. Consider moving some data to heap +// This is a serious warning. Don't suppress it. +//#pragma warning(disable : 6263) // warning C6263: Using _alloca in a loop: this can quickly overflow stack +// 6328 is also used for passing __int64 to printf when int is expected so we can't suppress it. +//#pragma warning(disable : 6328) // warning C6328: 'char' passed as parameter '1' when 'unsigned char' is required in call to 'V_isdigit' +// /analyze doesn't like GCOMPILER_ASSERT's implementation of compile-time asserts +#pragma warning(disable : 6326) // warning C6326: Potential comparison of a constant with another constant +#pragma warning(disable : 6335) // warning C6335: Leaking process information handle 'pi.hThread' +#pragma warning(disable : 6320) // warning C6320: Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled +#pragma warning(disable : 6250) // warning C6250: Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks +#pragma warning(disable : 6384) // ientity2_class_h_schema_gen.cpp(76): warning C6384: Dividing sizeof a pointer by another value + +// For temporarily suppressing warnings -- the warnings are suppressed for the next source line. +#define ANALYZE_SUPPRESS(wnum) __pragma(warning(suppress: wnum)) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) __pragma(warning(supress: wnum1 wnum2)) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) __pragma(warning(suppress: wnum1 wnum2 wnum3)) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) __pragma(warning(suppress: wnum1 wnum2 wnum3 wnum4)) + +// Tag all printf style format strings with this +#define PRINTF_FORMAT_STRING _Printf_format_string_ +#define SCANF_FORMAT_STRING _Scanf_format_string_impl_ +// Various macros for specifying the capacity of the buffer pointed +// to by a function parameter. Variations include in/out/inout, +// CAP (elements) versus BYTECAP (bytes), and null termination or +// not (_Z). +#define IN_Z _In_z_ +#define IN_CAP(x) _In_count_(x) +#define IN_BYTECAP(x) _In_bytecount_(x) +#define OUT_Z_CAP(x) _Out_z_cap_(x) +#define OUT_CAP(x) _Out_cap_(x) +#define OUT_BYTECAP(x) _Out_bytecap_(x) +#define OUT_Z_BYTECAP(x) _Out_z_bytecap_(x) +#define INOUT_BYTECAP(x) _Inout_bytecap_(x) +#define INOUT_Z_CAP(x) _Inout_z_cap_(x) +#define INOUT_Z_BYTECAP(x) _Inout_z_bytecap_(x) +// These macros are use for annotating array reference parameters, typically used in functions +// such as V_strcpy_safe. Because they are array references the capacity is already known. +#if _MSC_VER >= 1700 +#define IN_Z_ARRAY _Pre_z_ +#define OUT_Z_ARRAY _Post_z_ +#define INOUT_Z_ARRAY _Prepost_z_ +#else +#define IN_Z_ARRAY _Deref_pre_z_ +#define OUT_Z_ARRAY _Deref_post_z_ +#define INOUT_Z_ARRAY _Deref_prepost_z_ +#endif // _MSC_VER >= 1700 +// Use the macros above to annotate string functions that fill buffers as shown here, +// in order to give VS's /analyze more opportunities to find bugs. +// void V_wcsncpy( OUT_Z_BYTECAP(maxLenInBytes) wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ); +// int V_snwprintf( OUT_Z_CAP(maxLenInCharacters) wchar_t *pDest, int maxLenInCharacters, PRINTF_FORMAT_STRING const wchar_t *pFormat, ... ); + +#endif // _PREFAST_ +#endif // _MSC_VER >= 1600 // VS 2010 and above. + +#ifndef ANALYZE_SUPPRESS +#define ANALYZE_SUPPRESS(wnum) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) +#define PRINTF_FORMAT_STRING +#define SCANF_FORMAT_STRING +#define IN_Z +#define IN_CAP(x) +#define IN_BYTECAP(x) +#define OUT_Z_CAP(x) +#define OUT_CAP(x) +#define OUT_BYTECAP(x) +#define OUT_Z_BYTECAP(x) +#define INOUT_BYTECAP(x) +#define INOUT_Z_CAP(x) +#define INOUT_Z_BYTECAP(x) +#define OUT_Z_ARRAY +#define INOUT_Z_ARRAY +#endif + +#endif // ANALYSIS_ANNOTATIONS_H diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h index cad625b83..fa7bc486b 100644 --- a/public/tier0/dbg.h +++ b/public/tier0/dbg.h @@ -1,13 +1,16 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ========// +//===== Copyright (c) Valve Corporation, All rights reserved. ========// // -// Purpose: +// Purpose: // // $NoKeywords: $ // -//===========================================================================// +//====================================================================// #ifndef DBG_H #define DBG_H +#if !defined(__SPU__) + + #ifdef _WIN32 #pragma once #endif @@ -20,14 +23,20 @@ #include #include -#ifdef POSIX -#define __cdecl -#endif - //----------------------------------------------------------------------------- // dll export stuff //----------------------------------------------------------------------------- +#ifdef TIER0_DLL_EXPORT +#define DBG_INTERFACE DLL_EXPORT +#define DBG_OVERLOAD DLL_GLOBAL_EXPORT +#define DBG_CLASS DLL_CLASS_EXPORT +#else +#define DBG_INTERFACE DLL_IMPORT +#define DBG_OVERLOAD DLL_GLOBAL_IMPORT +#define DBG_CLASS DLL_CLASS_IMPORT +#endif + class Color; @@ -83,6 +92,13 @@ class Color; //----------------------------------------------------------------------------- PLATFORM_INTERFACE void _ExitOnFatalAssert( const tchar* pFile, int line ); + +#if defined( DBGFLAG_STRINGS_STRIP ) +#define DbgFlagMacro_ExitOnFatalAssert( pFile, line ) _ExitOnFatalAssert( "", 0 ) +#else +#define DbgFlagMacro_ExitOnFatalAssert( pFile, line ) _ExitOnFatalAssert( pFile, line ) +#endif + PLATFORM_INTERFACE bool ShouldUseNewAssertDialog(); PLATFORM_INTERFACE bool SetupWin32ConsoleIO(); @@ -90,23 +106,68 @@ PLATFORM_INTERFACE bool SetupWin32ConsoleIO(); // Returns true if they want to break in the debugger. PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const tchar *pExpression ); +#if defined( DBGFLAG_STRINGS_STRIP ) +#define DbgFlagMacro_DoNewAssertDialog( pFile, line, pExpression ) DoNewAssertDialog( "", 0, "" ) +#else +#define DbgFlagMacro_DoNewAssertDialog( pFile, line, pExpression ) DoNewAssertDialog( pFile, line, pExpression ) +#endif + +// Allows the assert dialogs to be turned off from code +PLATFORM_INTERFACE bool AreAllAssertsDisabled(); +PLATFORM_INTERFACE void SetAllAssertsDisabled( bool bAssertsEnabled ); + +PLATFORM_INTERFACE bool IsAssertDialogDisabled(); +PLATFORM_INTERFACE void SetAssertDialogDisabled( bool bAssertDialogDisabled ); + +// Provides a callback that is called on asserts regardless of spew levels +typedef void (*AssertFailedNotifyFunc_t)( const char *pchFile, int nLine, const char *pchMessage ); +PLATFORM_INTERFACE void SetAssertFailedNotifyFunc( AssertFailedNotifyFunc_t func ); +PLATFORM_INTERFACE void CallAssertFailedNotifyFunc( const char *pchFile, int nLine, const char *pchMessage ); + +#if defined( LINUX ) +PLATFORM_INTERFACE void SetAssertDialogParent( struct SDL_Window *window ); +PLATFORM_INTERFACE struct SDL_Window * GetAssertDialogParent(); +#endif + /* Used to define macros, never use these directly. */ -#define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) \ - do { \ - if (!(_exp)) \ - { \ - LoggingResponse_t ret = Log_Assert( "%s (%d) : %s\n", __TFILE__, __LINE__, _msg ); \ - _executeExp; \ - if ( ret == LR_DEBUGGER ) \ - { \ - if ( !ShouldUseNewAssertDialog() || DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ - DebuggerBreak(); \ - if ( _bFatal ) \ - _ExitOnFatalAssert( __TFILE__, __LINE__ ); \ - } \ - } \ - } while (0) +#ifdef _PREFAST_ + // When doing /analyze builds define _AssertMsg to be __analysis_assume. This tells + // the compiler to assume that the condition is true, which helps to suppress many + // warnings. This define is done in debug and release builds. + // The unfortunate !! is necessary because otherwise /analyze is incapable of evaluating + // all of the logical expressions that the regular compiler can handle. + // Include _msg in the macro so that format errors in it are detected. + #define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) do { __analysis_assume( !!(_exp) ); _msg; } while (0) + #define _AssertMsgOnce( _exp, _msg, _bFatal ) do { __analysis_assume( !!(_exp) ); _msg; } while (0) + // Force asserts on for /analyze so that we get a __analysis_assume of all of the constraints. + #define DBGFLAG_ASSERT + #define DBGFLAG_ASSERTFATAL + #define DBGFLAG_ASSERTDEBUG + + // Define the Q_ASSERT macro to override the QT assert macro so that its asserts + // suppress warnings instead of causing them. + #define Q_ASSERT( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) +#else + #define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) \ + do { \ + if (!(_exp)) \ + { \ + LoggingResponse_t ret = Log_Assert( "%s (%d) : %s\n", __TFILE__, __LINE__, static_cast( _msg ) ); \ + CallAssertFailedNotifyFunc( __TFILE__, __LINE__, _msg ); \ + _executeExp; \ + if ( ret == LR_DEBUGGER ) \ + { \ + if ( ShouldUseNewAssertDialog() ) \ + { \ + if ( DbgFlagMacro_DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ + DebuggerBreak(); \ + } \ + if ( _bFatal ) \ + DbgFlagMacro_ExitOnFatalAssert( __TFILE__, __LINE__ ); \ + } \ + } \ + } while (0) #define _AssertMsgOnce( _exp, _msg, _bFatal ) \ do { \ @@ -116,6 +177,7 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t _AssertMsg( _exp, _msg, (fAsserted = true), _bFatal ); \ } \ } while (0) +#endif /* Spew macros... */ @@ -135,6 +197,7 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) AssertFatalMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) #define VerifyFatal( _exp ) AssertFatal( _exp ) #define VerifyEqualsFatal( _exp, _expectedValue ) AssertFatalEquals( _exp, _expectedValue ) +#define DbgVerifyFatal( _exp ) AssertFatal( _exp ) #define AssertFatalMsg1( _exp, _msg, a1 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1 ))) #define AssertFatalMsg2( _exp, _msg, a1, a2 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2 ))) @@ -158,6 +221,7 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) ((void)0) #define VerifyFatal( _exp ) (_exp) #define VerifyEqualsFatal( _exp, _expectedValue ) (_exp) +#define DbgVerifyFatal( _exp ) (_exp) #define AssertFatalMsg1( _exp, _msg, a1 ) ((void)0) #define AssertFatalMsg2( _exp, _msg, a1, a2 ) ((void)0) @@ -172,6 +236,27 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #endif // DBGFLAG_ASSERTFATAL +// lightweight assert macros: in theory, can be run in release without slowing it down +#if defined(_CERT) || defined(_RETAIL) +#define AssertAligned(PTR) +#define AssertAlignedWidth(PTR, width) +#define AssertAlignedConsole(PTR) +#else +# if defined( _X360 ) +# define AssertAlignedWidth( PTR, width ) __twnei( intp(PTR) & ( width - 1 ), 0 ) // trap if not equal to immediate value (from width mask); unsigned comparison +# define AssertAligned( PTR ) AssertAlignedWidth( PTR, 16 ) // Call above with 16 width defined +# define AssertAlignedConsole( PTR ) AssertAlignedWidth( PTR, 4 ) // Call above with 4 width defined (xbox only for now) +# elif defined( DBGFLAG_ASSERT ) +# define AssertAlignedWidth( adr, width ) Assert( ( ( ( intp ) ( adr ) ) & ( width - 1 ) ) == 0 ) +# define AssertAligned( adr ) AssertAlignedWidth( adr, 16 ) +# define AssertAlignedConsole(adr) // XBox only for now. +# else +# define AssertAlignedWidth(PTR, width) +# define AssertAligned(PTR) +# define AssertAlignedConsole(PTR) +# endif +#endif + // Assert macros // Assert is used to detect an important but survivable error. // It's only turned on when DBGFLAG_ASSERT is true. @@ -179,15 +264,29 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #ifdef DBGFLAG_ASSERT #define Assert( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) -#define AssertAligned( adr ) Assert( ( ( ( intp ) ( adr ) ) & 0xf ) == 0 ) #define AssertMsg_( _exp, _msg ) _AssertMsg( _exp, _msg, ((void)0), false ) #define AssertOnce( _exp ) _AssertMsgOnce( _exp, _T("Assertion Failed: ") _T(#_exp), false ) #define AssertMsgOnce( _exp, _msg ) _AssertMsgOnce( _exp, _msg, false ) #define AssertFunc( _exp, _f ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), _f, false ) #define AssertEquals( _exp, _expectedValue ) AssertMsg2( (_exp) == (_expectedValue), _T("Expected %d but got %d!"), (_expectedValue), (_exp) ) #define AssertFloatEquals( _exp, _expectedValue, _tol ) AssertMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) -#define Verify( _exp ) Assert( _exp ) +#define Verify( _exp ) ( _exp ) #define VerifyEquals( _exp, _expectedValue ) AssertEquals( _exp, _expectedValue ) +#ifndef DbgVerify +#define DbgVerify( _exp ) Assert( _exp ) +#endif + +#ifdef _DEBUG +#define DbgAssert( _exp ) Assert( _exp ) +#else +#define DbgAssert( _exp ) ((void)0) +#endif + +#ifdef _DEBUG +#define DbgAssert( _exp ) Assert( _exp ) +#else +#define DbgAssert( _exp ) ((void)0) +#endif #define AssertMsg( _exp, _msg ) AssertMsg_( _exp, _T( _msg ) ) #define AssertMsg1( _exp, _msg, a1 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1 )) ) @@ -203,7 +302,6 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #else // DBGFLAG_ASSERT #define Assert( _exp ) ((void)0) -#define AssertAligned( ptr ) ((void)0) #define AssertOnce( _exp ) ((void)0) #define AssertMsg( _exp, _msg ) ((void)0) #define AssertMsgOnce( _exp, _msg ) ((void)0) @@ -212,6 +310,10 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #define AssertFloatEquals( _exp, _expectedValue, _tol ) ((void)0) #define Verify( _exp ) (_exp) #define VerifyEquals( _exp, _expectedValue ) (_exp) +#ifndef DbgVerify +#define DbgVerify( _exp ) (_exp) +#endif +#define DbgAssert( _exp ) ((void)0) #define AssertMsg1( _exp, _msg, a1 ) ((void)0) #define AssertMsg2( _exp, _msg, a1, a2 ) ((void)0) @@ -226,17 +328,33 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t #endif // DBGFLAG_ASSERT -#define FILE_LINE_FUNCTION_STRINGIFY(x) #x -#define FILE_LINE_FUNCTION_TOSTRING(x) FILE_LINE_FUNCTION_STRINGIFY(x) -#define FILE_LINE_FUNCTION_STRING __FILE__ "(" FILE_LINE_FUNCTION_TOSTRING(__LINE__) "):" __FUNCTION__ ":" +// Source2 compatibility macro +#define AssertDbg( X ) DbgAssert( X ) + + +// Use AssertAnalyze when the main purpose is to work around /analyze bugs by +// telling the compiler that a condition is impossible. If DBGFLAG_ASSERT is set +// then these will still be Asserts (just in case). The use of a different macro +// is in order to indicate their purpose. +#define AssertAnalyze( _exp ) Assert( _exp ) +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +// The Always version of the assert macros are defined even when DBGFLAG_ASSERT is not, +// so they will be available even in release. +#define AssertAlways( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) +#define AssertMsgAlways( _exp, _msg ) _AssertMsg( _exp, _msg, ((void)0), false ) -#define FILE_LINE_STRINGIFY(x) #x -#define FILE_LINE_TOSTRING(x) FILE_LINE_STRINGIFY(x) -#define FILE_LINE_STRING __FILE__ "(" FILE_LINE_TOSTRING(__LINE__) "):" -#define FUNCTION_LINE_STRINGIFY(x) #x -#define FUNCTION_LINE_TOSTRING(x) FUNCTION_LINE_STRINGIFY(x) -#define FUNCTION_LINE_STRING __FUNCTION__ "(" FUNCTION_LINE_TOSTRING(__LINE__) "): " +#define FILE_LINE_FUNCTION_STRING __FILE__ "(" STRINGIFY(__LINE__) "):" __FUNCTION__ ":" +#define FILE_LINE_STRING __FILE__ "(" STRINGIFY(__LINE__) "):" +#define FUNCTION_LINE_STRING __FUNCTION__ "(" STRINGIFY(__LINE__) "): " + + +// Handy define for inserting clickable messages into the build output. +// Use like this: +// #pragma MESSAGE("Some message") +#define MESSAGE(msg) message(__FILE__ "(" FUNCTION_LINE_TOSTRING(__LINE__) "): " msg) ////////////////////////////////////////////////////////////////////////// // Legacy Logging System @@ -245,7 +363,8 @@ PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const t // Channels which map the legacy logging system to the new system. // Channel for all default Msg/Warning/Error commands. -DECLARE_LOGGING_CHANNEL( LOG_GENERAL ); +PLATFORM_INTERFACE LoggingChannelID_t LOG_GENERAL; + // Channel for all asserts. DECLARE_LOGGING_CHANNEL( LOG_ASSERT ); // Channel for all ConMsg and ConColorMsg commands. @@ -259,29 +378,58 @@ DECLARE_LOGGING_CHANNEL( LOG_DEVELOPER_VERBOSE ); // Legacy logging functions -PLATFORM_INTERFACE void Msg( const tchar* pMsg, ... ); -PLATFORM_INTERFACE void Warning( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); -PLATFORM_INTERFACE void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); -PLATFORM_INTERFACE void Error( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); -PLATFORM_INTERFACE void Error_SpewCallStack( int iMaxCallStackLength, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +// These functions do not return. +PLATFORM_INTERFACE void Error( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Error_SpewCallStack( int iMaxCallStackLength, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +#define Plat_FatalError( ... ) do { Log_Error( LOG_GENERAL, ##__VA_ARGS__ ); Plat_ExitProcess( EXIT_FAILURE ); } while( 0 ) -// @TODO: these callstack spew functions are currently disabled in the new logging system. Need to add support for these if desired. -PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Enable( bool bEnable ); -PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); +#if defined( DBGFLAG_STRINGS_STRIP ) -PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Enable( bool bEnable ); -PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); +#define Msg( ... ) ((void)0) +#define Warning( ... ) ((void)0) +#define Warning_SpewCallStack( ... ) ((void)0) +#define DevMsg( ... ) ((void)0) +#define DevWarning( ... ) ((void)0) +#define ConColorMsg( ... ) ((void)0) +#define ConMsg( ... ) ((void)0) +#define ConDMsg( ... ) ((void)0) +#define COM_TimestampedLog( ... ) ((void)0) -PLATFORM_INTERFACE void DevMsg( int level, const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); -PLATFORM_INTERFACE void DevWarning( int level, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +#else // DBGFLAG_STRINGS_STRIP -PLATFORM_OVERLOAD void DevMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); -PLATFORM_OVERLOAD void DevWarning( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Msg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Warning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Warning_SpewCallStack( int iMaxCallStackLength, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); -PLATFORM_OVERLOAD void ConColorMsg( const Color& clr, const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); -PLATFORM_OVERLOAD void ConMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +#ifdef _PS3 -PLATFORM_INTERFACE void ConDMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_OVERLOAD void DevMsg( int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_OVERLOAD void DevWarning( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +PLATFORM_INTERFACE void DevMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void DevWarning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +PLATFORM_INTERFACE void ConColorMsg( const Color& clr, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_INTERFACE void ConMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + +#else // !_PS3 + +PLATFORM_INTERFACE void DevMsg( int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_INTERFACE void DevWarning( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +PLATFORM_OVERLOAD void DevMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_OVERLOAD void DevWarning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +PLATFORM_OVERLOAD void ConColorMsg( const Color& clr, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_OVERLOAD void ConMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + +#endif // _PS3 + +PLATFORM_INTERFACE void ConDMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + +PLATFORM_INTERFACE void COM_TimestampedLog( PRINTF_FORMAT_STRING char const *fmt, ... ) FMTFUNCTION( 1, 2 ); + +#endif // DBGFLAG_STRINGS_STRIP // You can use this macro like a runtime assert macro. // If the condition fails, then Error is called with the message. This macro is called @@ -302,7 +450,13 @@ PLATFORM_INTERFACE void ConDMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); #define DebugMsg(...) #endif -PLATFORM_INTERFACE void COM_TimestampedLog( char const *fmt, ... ) FMTFUNCTION( 1, 2 ); +// @TODO: these callstack spew functions are currently disabled in the new logging system. Need to add support for these if desired. +PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Enable( bool bEnable ); +PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); + +PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Enable( bool bEnable ); +PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); + /* Code macros, debugger interface */ @@ -322,16 +476,6 @@ PLATFORM_INTERFACE void COM_TimestampedLog( char const *fmt, ... ) FMTFUNCTION( #endif /* _DEBUG */ -//----------------------------------------------------------------------------- -// Macro to assist in asserting constant invariants during compilation - -#ifdef _DEBUG -#define COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} -#define ASSERT_INVARIANT( pred ) static void UNIQUE_ID() { COMPILE_TIME_ASSERT( pred ) } -#else -#define COMPILE_TIME_ASSERT( pred ) -#define ASSERT_INVARIANT( pred ) -#endif #ifdef _DEBUG template @@ -351,14 +495,23 @@ inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE* pSource) PLATFORM_INTERFACE void _AssertValidReadPtr( void* ptr, int count = 1 ); PLATFORM_INTERFACE void _AssertValidWritePtr( void* ptr, int count = 1 ); PLATFORM_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count = 1 ); +PLATFORM_INTERFACE void _AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ); -PLATFORM_INTERFACE void _AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ); +#ifdef DBGFLAG_ASSERT +inline void AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ) { _AssertValidStringPtr( ptr, maxchar ); } template inline void AssertValidReadPtr( T* ptr, int count = 1 ) { _AssertValidReadPtr( (void*)ptr, count ); } template inline void AssertValidWritePtr( T* ptr, int count = 1 ) { _AssertValidWritePtr( (void*)ptr, count ); } template inline void AssertValidReadWritePtr( T* ptr, int count = 1 ) { _AssertValidReadWritePtr( (void*)ptr, count ); } +#define AssertValidThis() AssertValidReadWritePtr(this,sizeof(*this)) +#else -#define AssertValidThis() AssertValidReadWritePtr(this,sizeof(*this)) +inline void AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ) { } +template inline void AssertValidReadPtr( T* ptr, int count = 1 ) { } +template inline void AssertValidWritePtr( T* ptr, int count = 1 ) { } +template inline void AssertValidReadWritePtr( T* ptr, int count = 1 ) { } +#define AssertValidThis() +#endif //----------------------------------------------------------------------------- // Macro to protect functions that are not reentrant @@ -410,7 +563,7 @@ class CReentryGuard class CDbgFmtMsg { public: - CDbgFmtMsg(const tchar *pszFormat, ...) + CDbgFmtMsg(PRINTF_FORMAT_STRING const tchar *pszFormat, ...) { va_list arg_ptr; @@ -641,4 +794,59 @@ class CHardwareBreakPointScopeGuard #endif // IS_WINDOWS_PC //----------------------------------------------------------------------------- + +#else //#if !defined(__SPU__) + +// void these for now + +#define Assert( _exp ) ((void)0) +#define AssertOnce( _exp ) ((void)0) +#define AssertMsg( _exp, _msg ) ((void)0) +#define AssertMsgOnce( _exp, _msg ) ((void)0) +#define AssertFunc( _exp, _f ) ((void)0) +#define AssertEquals( _exp, _expectedValue ) ((void)0) +#define AssertFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define Verify( _exp ) (_exp) +#define VerifyEquals( _exp, _expectedValue ) (_exp) + +#define AssertMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#define COMPILE_TIME_ASSERT( pred ) +#define ASSERT_INVARIANT( pred ) + +#define AssertFatal( _exp ) ((void)0) +#define AssertFatalOnce( _exp ) ((void)0) +#define AssertFatalMsg( _exp, _msg ) ((void)0) +#define AssertFatalMsgOnce( _exp, _msg ) ((void)0) +#define AssertFatalFunc( _exp, _f ) ((void)0) +#define AssertFatalEquals( _exp, _expectedValue ) ((void)0) +#define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define VerifyFatal( _exp ) (_exp) +#define VerifyEqualsFatal( _exp, _expectedValue ) (_exp) + +#define AssertFatalMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertFatalMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertFatalMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertFatalMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertFatalMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertFatalMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertFatalMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertFatalMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#define AssertAligned(PTR) + +#endif + + #endif /* DBG_H */ diff --git a/public/tier0/dbgflag.h b/public/tier0/dbgflag.h index caa6969e9..aa726cf86 100644 --- a/public/tier0/dbgflag.h +++ b/public/tier0/dbgflag.h @@ -34,7 +34,7 @@ //----------------------------------------------------------------------------- // Default flags for debug builds //----------------------------------------------------------------------------- -#ifdef _DEBUG +#if defined( _DEBUG ) && !defined( PS3MEMOVERRIDEWRAP ) #define DBGFLAG_MEMORY #ifdef _SERVER // only enable new & delete tracking for server; on client it conflicts with CRT mem leak tracking @@ -61,4 +61,8 @@ #endif // _DEBUG +#if defined( _CERT ) +#define DBGFLAG_STRINGS_STRIP +#endif + #endif // DBGFLAG_H diff --git a/public/tier0/platform.h b/public/tier0/platform.h index b5e03e6ac..d5dd7a2be 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -1,53 +1,199 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // -// $NoKeywords: $ +// $NoKeywords: $ // //===========================================================================// #ifndef PLATFORM_H #define PLATFORM_H +#if defined(__x86_64__) || defined(_WIN64) +#define PLATFORM_64BITS 1 +#endif + +#if defined( LINUX ) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406 +// based on some Jonathan Wakely macros on the net... +#define GCC_DIAG_STR(s) #s +#define GCC_DIAG_JOINSTR(x,y) GCC_DIAG_STR(x ## y) +#define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x) +#define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x) + +#define GCC_DIAG_PUSH_OFF(x) GCC_DIAG_PRAGMA(push) GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x)) +#define GCC_DIAG_POP() GCC_DIAG_PRAGMA(pop) +#else +#define GCC_DIAG_PUSH_OFF(x) +#define GCC_DIAG_POP() +#endif + +#ifdef LINUX +#pragma GCC diagnostic ignored "-Wconversion-null" // passing NULL to non-pointer argument 1 +#pragma GCC diagnostic ignored "-Wpointer-arith" // NULL used in arithmetic. Ie, vpanel == NULL where VPANEL is uint. +#endif + +#ifdef _DEBUG +#if !defined( PLAT_COMPILE_TIME_ASSERT ) +#define PLAT_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} +#endif +#else +#if !defined( PLAT_COMPILE_TIME_ASSERT ) +#define PLAT_COMPILE_TIME_ASSERT( pred ) +#endif +#endif + +#ifdef SN_TARGET_PS3 + +//#define NO_STEAM +#define NO_STEAM_PS3_OVERLAY + +#define _PS3 1 +#define COMPILER_PS3 1 +#define PLATFORM_PS3 1 + +// There are 2 compilers for the PS3: GCC and the SN Systems compiler. +// They are mostly similar, but in a few places we need to distinguish between the two. +#if defined( __SNC__ ) +#define COMPILER_SNC 1 +#elif defined( __GCC__ ) +#define COMPILER_GCC 1 +#else +#error "Unrecognized PS3 compiler; either __SNC__ or __GCC__ must be defined" +#endif + +#endif // SN_TARGET_PS3 + +#ifdef __GCC__ +#define COMPILER_GCC 1 +#endif + +#if defined( _X360 ) || defined( _PS3 ) +#define PLATFORM_PPC 1 +#endif + + #ifdef COMPILER_MSVC #pragma once #endif -#if defined( _X360 ) -#define NO_STEAM -#define NO_VOICE -// for the 360, the ppc platform and the rtos are tightly coupled -// setup the 360 environment here !once! for much less leaf module include wackiness -// these are critical order and purposely appear *before* anything else -#define _XBOX -#include -#include -#include -#include -#include -#include -#undef _XBOX +#if defined (_PS3) + +#if defined( __SPU__ ) + #include + #include + #include +#else + #include + + // We want to force the assert to be redefined, because the STD assert might have been + // included and redefined. ps3_assert.h will do a check for assert being redefined. + // #include "ps3/ps3_assert.h" + #ifndef COMPILER_PS3 + #error "for PS3, VPC must define COMPILER_PS3 macro just like it does for COMPILER_MSVCX360 macro" + #endif + #if !defined( COMPILER_SNC ) && !defined( COMPILER_GCC ) + #error "for PS3, VPC must define COMPILER_SNC or COMPILER_GCC macro, depending on the target compiler, just like it does for COMPILER_MSVCX360 macro" + #endif +#endif + +#elif defined( _X360 ) + #define NO_STEAM + #define NO_VOICE + // for the 360, the ppc platform and the rtos are tightly coupled + // setup the 360 environment here !once! for much less leaf module include wackiness + // these are critical order and purposely appear *before* anything else + #define _XBOX + #include + #include + #include + #include + #include + #include + #undef _XBOX #endif #include "wchartypes.h" #include "tier0/valve_off.h" -#ifdef OSX -#include -#else -#include + +#ifdef _PS3 + + #include "ps3/ps3_platform.h" + + // symbol redefinition was the source of many PS3 compile warnings +#if !defined( NO_STEAM_GAMECOORDINATOR ) + #define NO_STEAM_GAMECOORDINATOR #endif -#include -#include -#include -#include -#ifdef COMPILER_GCC -#include #else -#include + +// No game coordinator for Partner depot +#if !defined( NO_STEAM_GAMECOORDINATOR ) + #define NO_STEAM_GAMECOORDINATOR #endif + #include + #include + #include + #include + #include + #include +#if defined( OSX ) || defined ( _LINUX ) + #include + #include +#endif + +#endif + +// This macro +#if defined( _PS3 ) && defined ( COMPILER_SNC ) + +// There are known bugs in the PS3 optimizer. The following macros allow us to lower optimization for a subset of a file +// If you run into build problems with optimization on, try turning off optimization for the selected file. If that +// fixes the problem, use process of elimination and the below macros to find the bare minimum that needs to be +// unoptimized and report the compiler issue to Sony as well. +// +// The correlation between optimization levels and numbers passed to the _Pragma xopt and postopt calls is as follows: +// See: Control-group reference tables / -Xshow +// .... xopt +// -O1 0 +// -O2 5 +// -O3 5 +// +// These macros MUST be used in pairs - Otherwise, the compiler will barf 'At end of source: error 67: expected a "}"' + +// xopt disables some of the miscellaneous optimizations +#if __option(xopt) +#define SN_OPT_DISABLE extern "C++" { _Pragma("control %push xopt=0") +#define SN_OPT_ENABLE _Pragma("control %pop xopt") } +#else // !__option(xopt) +#define SN_OPT_DISABLE +#define SN_OPT_ENABLE +#endif // !__option(xopt) + +// postopt disables the main optimizer +#if __option(postopt) > 0 +#define SN_MAIN_OPT_DISABLE extern "C++" { _Pragma("control %push postopt=0") +#define SN_MAIN_OPT_ENABLE _Pragma("control %pop postopt") } +#else // !__option(postopt) > 0 +#define SN_MAIN_OPT_DISABLE +#define SN_MAIN_OPT_ENABLE +#endif // !__option(postopt) > 0 + +#else // ! ( _PS3 && COMPILER_SNC ) +#define SN_OPT_DISABLE +#define SN_OPT_ENABLE +#define SN_MAIN_OPT_DISABLE +#define SN_MAIN_OPT_ENABLE +#endif // ! ( _PS3 && COMPILER_SNC ) + +#ifdef __cplusplus +#if defined( COMPILER_GCC ) || defined( COMPILER_PS3 ) + #include +#else + #include +#endif +#endif //----------------------------------------------------------------------------- // Old-school defines we don't want to use moving forward @@ -59,13 +205,30 @@ #if !defined( _X360 ) #define SUPPORT_PACKED_STORE #endif -#define BINK_ENABLED_FOR_X360 +#if defined( BINK_VIDEO ) && ( defined( _X360 ) || defined( _PS3 ) ) +#define BINK_ENABLED_FOR_CONSOLE +#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 + +#if _MSC_VER >= 1800 +#define VECTORCALL __vectorcall +#else +#define VECTORCALL +#endif -// Deprecating, infavor of IsX360() which will revert to IsXbox() -// after confidence of xbox 1 code flush -#define IsXbox() false // C functions for external declarations that call the appropriate C++ methods #ifndef EXPORT @@ -76,15 +239,11 @@ #endif #endif -#ifdef PLATFORM_POSIX -typedef unsigned int DWORD; -typedef unsigned short WORD; -typedef void * HINSTANCE; -#define _MAX_PATH PATH_MAX -#endif - #endif // CROSS_PLATFORM_VERSION < 1 +#if defined(_STATIC_LINKED) +#include "staticlink/system.h" +#endif //----------------------------------------------------------------------------- // NOTE: All compiler defines are set up in the base VPC scripts @@ -100,73 +259,137 @@ typedef void * HINSTANCE; // Set up platform defines. //----------------------------------------------------------------------------- #ifdef _WIN32 - #define IsPlatformLinux() false - #define IsPlatformPosix() false - #define IsPlatformOSX() false - #define IsPlatformPS3() false - #define IsPlatformWindows() true - #ifndef PLATFORM_WINDOWS + #define IsPlatformLinux() 0 + #define IsPlatformPosix() 0 + #define IsPlatformOSX() 0 + #define IsOSXOpenGL() 0 + #define IsPlatformPS3() 0 + #define IsPlatformPS3_PPU() 0 + #define IsPlatformPS3_SPU() 0 #define PLATFORM_WINDOWS 1 - #endif + #define PLATFORM_OPENGL 0 #ifndef _X360 - #define IsPlatformX360() false - #define IsPlatformWindowsPC() true + #define IsPlatformX360() 0 + #define IsPlatformWindowsPC() 1 #define PLATFORM_WINDOWS_PC 1 #ifdef _WIN64 - #define IsPlatformWindowsPC64() true - #define IsPlatformWindowsPC32() false + #define IsPlatformWindowsPC64() 1 + #define IsPlatformWindowsPC32() 0 #define PLATFORM_WINDOWS_PC64 1 #else - #define IsPlatformWindowsPC64() false - #define IsPlatformWindowsPC32() true + #define IsPlatformWindowsPC64() 0 + #define IsPlatformWindowsPC32() 1 #define PLATFORM_WINDOWS_PC32 1 #endif #else // _X360 - #define IsPlatformWindowsPC() false - #define IsPlatformWindowsPC64() false - #define IsPlatformWindowsPC32() false - #define IsPlatformX360() true + #define IsPlatformWindowsPC() 0 + #define IsPlatformWindowsPC64() 0 + #define IsPlatformWindowsPC32() 0 + #define IsPlatformX360() 1 #define PLATFORM_X360 1 #endif // _X360 +#elif defined(_PS3) + +// Adding IsPlatformOpenGL() to help fix a bunch of code that was using IsPosix() to infer if the DX->GL translation layer was being used. +#if defined( DX_TO_GL_ABSTRACTION ) +#define IsPlatformOpenGL() true +#else +#define IsPlatformOpenGL() false +#endif +#define IsPlatformX360() 0 +#define IsPlatformPS3() 1 +#ifdef SPU +#define IsPlatformPS3_PPU() 0 +#define IsPlatformPS3_SPU() 1 +#else +#define IsPlatformPS3_PPU() 1 +#define IsPlatformPS3_SPU() 0 +#endif +#define IsPlatformWindowsPC() 0 +#define IsPlatformWindowsPC64() 0 +#define IsPlatformWindowsPC32() 0 +#define IsPlatformPosix() 1 +#define PLATFORM_POSIX 1 +#define PLATFORM_OPENGL 0 + +#define IsPlatformLinux() 0 +#define IsPlatformOSX() 0 +#define IsOSXOpenGL() 0 + + #elif defined(POSIX) - #define IsPlatformX360() false - #define IsPlatformPS3() false - #define IsPlatformWindows() false - #define IsPlatformWindowsPC() false - #define IsPlatformWindowsPC64() false - #define IsPlatformWindowsPC32() false - #define IsPlatformPosix() true - #ifndef PLATFORM_POSIX + #define IsPlatformX360() 0 + #define IsPlatformPS3() 0 + #define IsPlatformPS3_PPU() 0 + #define IsPlatformPS3_SPU() 0 + #define IsPlatformWindowsPC() 0 + #define IsPlatformWindowsPC64() 0 + #define IsPlatformWindowsPC32() 0 + #define IsPlatformPosix() 1 #define PLATFORM_POSIX 1 - #endif - #if defined( LINUX ) - #define IsPlatformLinux() true - #define IsPlatformOSX() false - #ifndef PLATFORM_LINUX + #if defined( LINUX ) && !defined( OSX ) // for havok we define both symbols, so don't let the osx build wander down here + #define IsPlatformLinux() 1 + #define IsPlatformOSX() 0 + #define IsOSXOpenGL() 0 + #define PLATFORM_OPENGL 0 #define PLATFORM_LINUX 1 - #endif #elif defined ( OSX ) - #define IsPlatformLinux() false - #define IsPlatformOSX() true - #ifndef PLATFORM_OSX + #define IsPlatformLinux() 0 + #define IsPlatformOSX() 1 + #define IsOSXOpenGL() 1 #define PLATFORM_OSX 1 - #endif + #define PLATFORM_OPENGL 1 #else - #define IsPlatformLinux() false - #define IsPlatformOSX() false + #define IsPlatformLinux() 0 + #define IsPlatformOSX() 0 + #define IsOSXOpenGL() 0 + #define PLATFORM_OPENGL 0 #endif #else #error #endif +// IsXXXX platform pseudo-functions +#if ( defined( PLATFORM_WINDOWS ) && ( PLATFORM_WINDOWS ) ) +#define IsPlatformWindows() 1 +#else +#define IsPlatformWindows() 0 +#endif + +#ifndef _PS3 +//#include +//#include +#else +#include // For malloc() +#include // for alloca() +#define _alloca alloca + #ifdef __cplusplus + #include + #endif +#endif + + + +#ifndef _PS3 +//#include +//#include +#else +#include // For malloc() +#include // for alloca() +#define _alloca alloca + #ifdef __cplusplus + #include + #endif +#endif + //----------------------------------------------------------------------------- // Old-school defines we're going to support since much code uses them @@ -198,19 +421,26 @@ typedef void * HINSTANCE; #endif // CROSS_PLATFORM_VERSION < 2 +// VXConsole is enabled for... +#if defined(_X360) || defined(_PS3) +#define USE_VXCONSOLE 1 +#define HasVxConsole() 1 +#else +#define HasVxConsole() 0 +#endif //----------------------------------------------------------------------------- // Set up platform type defines. //----------------------------------------------------------------------------- -#ifdef PLATFORM_X360 - #ifndef _CONSOLE - #define _CONSOLE +#if defined( PLATFORM_X360 ) || defined( _PS3 ) + #ifndef _GAMECONSOLE + #define _GAMECONSOLE #endif - #define IsPC() false - #define IsConsole() true + #define IsPC() 0 + #define IsGameConsole() 1 #else - #define IsPC() true - #define IsConsole() false + #define IsPC() 1 + #define IsGameConsole() 0 #endif @@ -219,23 +449,23 @@ typedef void * HINSTANCE; // Set up build configuration defines. //----------------------------------------------------------------------------- #ifdef _CERT -#define IsCert() true +#define IsCert() 1 #else -#define IsCert() false +#define IsCert() 0 #endif #ifdef _DEBUG -#define IsRelease() false -#define IsDebug() true +#define IsRelease() 0 +#define IsDebug() 1 #else -#define IsRelease() true -#define IsDebug() false +#define IsRelease() 1 +#define IsDebug() 0 #endif #ifdef _RETAIL -#define IsRetail() true +#define IsRetail() 1 #else -#define IsRetail() false +#define IsRetail() 0 #endif @@ -266,6 +496,13 @@ typedef signed char int8; #define __m128 __vector4 #endif + // Use this to specify that a function is an override of a virtual function. + // This lets the compiler catch cases where you meant to override a virtual + // function but you accidentally changed the function signature and created + // an overloaded function. Usage in function declarations is like this: + // int GetData() const OVERRIDE; + #define OVERRIDE override + #else // !COMPILER_MSVC typedef short int16; @@ -283,71 +520,207 @@ typedef signed char int8; #endif typedef void *HWND; + // [u]int64 are actually defined as 'long long' and gcc 64-bit + // doesn't automatically consider them the same as 'long int'. + // Changing the types for [u]int64 is complicated by + // there being many definitions, so we just + // define a 'long int' here and use it in places that would + // otherwise confuse the compiler. + typedef long int lint64; + typedef unsigned long int ulint64; + + #ifndef OVERRIDE // suppress redifinition warning (because we don't have CROSS_PLATFORM_VERSION defined) + #define OVERRIDE + #endif #endif // else COMPILER_MSVC -typedef float float32; -typedef double float64; +#if defined(_PS3) && !defined(NO_SIMD) +typedef union __attribute__ ((aligned (16))) +{ + float m128_f32[4]; +} l_m128; -// for when we don't care about how many bits we use -typedef unsigned int uint; +typedef __vector float __vector4; +typedef __vector4 __m128; +const __m128 VMX_ZERO=(vector float)(0.0f); +const __m128 VMX_ONE_HALF=(vector float)(0.5f); +const __m128 VMX_ONE=(vector float)(1.0f); -// Maximum and minimum representable values -#if !defined(PLATFORM_OSX) && !defined(__STDC_LIMIT_MACROS) +// Syntaxic sugar for multiply +inline __attribute__ ((always_inline)) __m128 __vec_mul(const __m128 a, const __m128 b) +{ + return vec_madd(a,b,VMX_ZERO); +} -#ifndef INT8_MAX -#define INT8_MAX SCHAR_MAX -#endif -#ifndef INT16_MAX -#define INT16_MAX SHRT_MAX -#endif -#ifndef INT32_MAX -#define INT32_MAX LONG_MAX -#endif -#ifndef INT64_MAX -#define INT64_MAX (((int64)~0) >> 1) -#endif +// Refined reciprocal function +inline __attribute__ ((always_inline)) __m128 __vec_rec(const __m128 a) +{ + //Get the reciprocal estimate + vector float estimate = vec_re( a ); -#ifndef INT8_MIN -#define INT8_MIN SCHAR_MIN -#endif -#ifndef INT16_MIN -#define INT16_MIN SHRT_MIN + //One round of Newton-Raphson refinement + return vec_madd( vec_nmsub( estimate, a, VMX_ONE ), estimate, estimate ); +} + +// refined reciprocal square root +inline __attribute__ ((always_inline)) __m128 __vec_rsqrt(const __m128 a) +{ + //Get the square root reciprocal estimate + __m128 estimate = vec_rsqrte( a ); + + //One round of Newton-Raphson refinement + __m128 estimateSquared = __vec_mul( estimate, estimate); + __m128 halfEstimate = __vec_mul( estimate, VMX_ONE_HALF); + return vec_madd( vec_nmsub( a, estimateSquared, VMX_ONE ), halfEstimate, estimate ); +} + +// refined square root +inline __attribute__ ((always_inline)) __m128 __vec_sqrt(const __m128 a) +{ + return __vec_mul( a, __vec_rsqrt( a )); +} + +// estimate square root +inline __attribute__ ((always_inline)) __m128 __vec_sqrtest(const __m128 a) +{ + return __vec_mul( a, vec_rsqrte( a )); +} + +// Syntaxic sugar for multiply +inline __attribute__ ((always_inline)) __m128 __vec_div(const __m128 a, const __m128 b) +{ + return __vec_mul( a, __vec_rec( b )); +} + +// load an unaligned array of float in a vector of floats +#if defined( __SPU__ ) +inline __attribute__ ((always_inline)) __m128 __vec_ld_unaligned(float* in) +#else +inline __attribute__ ((always_inline)) __m128 __vec_ld_unaligned(const float* in) #endif -#ifndef INT32_MIN -#define INT32_MIN LONG_MIN +{ + return vec_perm(vec_ld(0,in), + vec_ld(sizeof(__m128),in), + vec_lvsl( 0, in )); +} + +// load an unaligned array of 3 floats in a vector of floats, last member being 0. +#if defined( __SPU__ ) +inline __attribute__ ((always_inline)) __m128 __vec_ld_unaligned3(float* in) +#else +inline __attribute__ ((always_inline)) __m128 __vec_ld_unaligned3(const float* in) #endif -#ifndef INT64_MIN -#define INT64_MIN (((int64)1) << 63) +{ + return vec_and(__vec_ld_unaligned(in),(__m128)(vector unsigned int)(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF ,0)); +} + +// stores a vector of floats in an unaligned array of float +inline __attribute__ ((always_inline)) void __vec_st_unaligned(__m128 in, float* out) +{ + __m128 temp0 = vec_ld(0,out); + __m128 temp1 = vec_ld(16,out); + vector unsigned char align = vec_lvsr(0,out); + vector unsigned char mask = vec_perm ((vector unsigned char)(0), (vector unsigned char)(0xFF), align); + + in = vec_perm ( in, in, align); +#if defined(__SPU__) + temp0 = vec_sel ( temp0, in, (vec_bint4)mask); + temp1 = vec_sel ( in, temp1, (vec_bint4)mask); +#else + temp0 = vec_sel ( temp0, in, (vector bool)mask); + temp1 = vec_sel ( in, temp1, (vector bool)mask); #endif + vec_st ( temp0, 0, out); + vec_st ( temp1, 16, out); +} -#ifndef UINT8_MAX -#define UINT8_MAX ((uint8)~0) +// stores x,y,z from a vector of floats in an unaligned array of 3 floats +inline __attribute__ ((always_inline)) void __vec_st_unaligned3(__m128 in, float* out) +{ + __m128 temp0 = vec_ld(0,out); + __m128 temp1 = vec_ld(16,out); + vector unsigned char align = vec_lvsr(0,out); + vector unsigned char mask = vec_perm ((vector unsigned char)(0), + (vector unsigned char)(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0), + align); + + in = vec_perm ( in, in, align); +#if defined(__SPU__) + temp0 = vec_sel ( temp0, in, (vec_bint4)mask); + temp1 = vec_sel ( in, temp1, (vec_bint4)mask); +#else + temp0 = vec_sel ( temp0, in, (vector bool)mask); + temp1 = vec_sel ( in, temp1, (vector bool)mask); #endif -#ifndef UINT16_MAX -#define UINT16_MAX ((uint16)~0) + vec_st ( temp0, 0, out); + vec_st ( temp1, 16, out); +} + +#endif // defined(NO_SIMD) + + +typedef float float32; +typedef double float64; + +// for when we don't care about how many bits we use +typedef unsigned int uint; + +#ifdef PLATFORM_POSIX +#ifndef _PS3 +typedef unsigned int DWORD; +typedef unsigned int *LPDWORD; #endif -#ifndef UINT32_MAX -#define UINT32_MAX ((uint32)~0) +typedef unsigned short WORD; +typedef void * HINSTANCE; +#define _MAX_PATH PATH_MAX #endif -#ifndef UINT16_MAX -#define UINT64_MAX ((uint64)~0) + +// MSVC CRT uses 0x7fff while gcc uses MAX_INT, leading to mismatches between platforms +// As a result, we pick the least common denominator here. This should be used anywhere +// you might typically want to use RAND_MAX +#define VALVE_RAND_MAX 0x7fff + +// Maximum and minimum representable values +#ifndef PLATFORM_POSIX + +#if _MSC_VER >= 1800 // VS 2013 or higher + // Copied from stdint.h + #define INT8_MIN (-127i8 - 1) + #define INT16_MIN (-32767i16 - 1) + #define INT32_MIN (-2147483647i32 - 1) + #define INT64_MIN (-9223372036854775807i64 - 1) + #define INT8_MAX 127i8 + #define INT16_MAX 32767i16 + #define INT32_MAX 2147483647i32 + #define INT64_MAX 9223372036854775807i64 + #define UINT8_MAX 0xffui8 + #define UINT16_MAX 0xffffui16 + #define UINT32_MAX 0xffffffffui32 + #define UINT64_MAX 0xffffffffffffffffui64 +#else // _MSC_VER + #define INT8_MAX SCHAR_MAX + #define INT16_MAX SHRT_MAX + #define INT32_MAX LONG_MAX + #define INT64_MAX ((int64)0x7fffffffffffffffll) + + #define INT8_MIN SCHAR_MIN + #define INT16_MIN SHRT_MIN + #define INT32_MIN LONG_MIN + #define INT64_MIN (((int64)1) << 63) + + #define UINT8_MAX ((uint8)~0) + #define UINT16_MAX ((uint16)~0) + #define UINT32_MAX ((uint32)~0) + #define UINT64_MAX ((uint64)~0) #endif -#ifndef UINT8_MIN #define UINT8_MIN 0 -#endif -#ifndef UINT16_MIN #define UINT16_MIN 0 -#endif -#ifndef UINT32_MIN #define UINT32_MIN 0 -#endif -#ifndef UINT64_MIN #define UINT64_MIN 0 -#endif -#endif // !PLATFORM_OSX && !__STDC_LIMIT_MACROS +#endif // PLATFORM_POSIX #ifndef UINT_MIN #define UINT_MIN UINT32_MIN @@ -358,8 +731,9 @@ typedef unsigned int uint; #ifdef GNUC #undef offsetof +// Note: can't use builtin offsetof because many use cases (esp. in templates) wouldn't compile due to restrictions on the builtin offsetof //#define offsetof( type, var ) __builtin_offsetof( type, var ) -#define offsetof(s,m) (size_t)&(((s *)0)->m) +#define offsetof(s,m) ( (size_t)&(((s *)0x1000000)->m) - 0x1000000u ) #else #include #undef offsetof @@ -453,9 +827,12 @@ typedef unsigned int uint; #define DEFAULT_VC_WARNING( x ) __pragma(warning(default:4310) ) -#elif defined ( COMPILER_GCC ) +#elif defined ( COMPILER_GCC ) || defined( COMPILER_SNC ) - #if (CROSS_PLATFORM_VERSION >= 1) && !defined( PLATFORM_64BITS ) + #if defined( COMPILER_SNC ) || defined( PLATFORM_64BITS ) + #define STDCALL + #define __stdcall + #elif (CROSS_PLATFORM_VERSION >= 1) && !defined( PLATFORM_64BITS ) && !defined( COMPILER_PS3 ) #define STDCALL __attribute__ ((__stdcall__)) #else #define STDCALL @@ -466,7 +843,13 @@ typedef unsigned int uint; #ifdef _LINUX_DEBUGGABLE #define FORCEINLINE #else - #define FORCEINLINE inline + #ifdef _PS3 + // [IESTYN 7/29/2010] As of SDK 3.4.0, this causes bad code generation in NET_Tick::ReadFromBuffer in netmessages.cpp, + // which caused (seeming) random network packet corruption. It probably causes other bugs too. + #define FORCEINLINE inline /* __attribute__ ((always_inline)) */ + #else + #define FORCEINLINE inline __attribute__ ((always_inline)) + #endif #endif // GCC 3.4.1 has a bug in supporting forced inline of templated functions @@ -479,26 +862,44 @@ typedef unsigned int uint; #define NULLTERMINATED +#if defined( COMPILER_SNC ) + #define TEMPLATE_STATIC static +#else #define TEMPLATE_STATIC +#endif // Used for dll exporting and importing - #define DLL_EXPORT extern "C" __attribute__ ((visibility("default"))) - #define DLL_IMPORT extern "C" + #ifdef COMPILER_SNC + #define DLL_DECLARATION_DEFAULT_VISIBILITY + #else + #define DLL_DECLARATION_DEFAULT_VISIBILITY __attribute__ ((visibility("default"))) + #endif + #define DLL_EXPORT extern "C" DLL_DECLARATION_DEFAULT_VISIBILITY + #define DLL_IMPORT extern "C" // Can't use extern "C" when DLL exporting a class - #define DLL_CLASS_EXPORT __attribute__ ((visibility("default"))) +#if !defined( _PS3 ) && !defined( LINUX ) && !defined( PLATFORM_64BITS ) + #define __stdcall __attribute__ ((__stdcall__)) +#endif + #define DLL_CLASS_EXPORT DLL_DECLARATION_DEFAULT_VISIBILITY #define DLL_CLASS_IMPORT // Can't use extern "C" when DLL exporting a global - #define DLL_GLOBAL_EXPORT __attribute__((visibility("default"))) + #define DLL_GLOBAL_EXPORT DLL_DECLARATION_DEFAULT_VISIBILITY #define DLL_GLOBAL_IMPORT extern - #define HINT(THE_HINT) 0 + #define HINT(THE_HINT) __builtin_expect( THE_HINT, 1 ) #define DECL_ALIGN(x) __attribute__( ( aligned( x ) ) ) #define CONSTRUCT_EARLY __attribute__((init_priority(101))) #define SELECTANY __attribute__((weak)) +#if defined(__clang__) + // [will] - clang is very strict about restrict, and we have a bunch of core functions that use the keyword which have issues with it. + // This seemed to be a cleaner solution for now so we don't have to fill core code with tons of #ifdefs. #define RESTRICT - #define RESTRICT_FUNC +#else + #define RESTRICT __restrict__ +#endif + #define RESTRICT_FUNC RESTRICT_FUNC_NOT_YET_DEFINED_FOR_THIS_COMPILER #define FMTFUNCTION( fmtargnumber, firstvarargnumber ) __attribute__ (( format( printf, fmtargnumber, firstvarargnumber ))) #define NOINLINE __attribute__ ((noinline)) @@ -516,7 +917,7 @@ typedef unsigned int uint; #endif -#if defined( GNUC ) +#if defined( GNUC ) && !defined( COMPILER_PS3 ) // use pre-align on PS3 // gnuc has the align decoration at the end #define ALIGN4 #define ALIGN8 @@ -524,6 +925,7 @@ typedef unsigned int uint; #define ALIGN32 #define ALIGN128 +#undef ALIGN16_POST #define ALIGN4_POST DECL_ALIGN(4) #define ALIGN8_POST DECL_ALIGN(8) #define ALIGN16_POST DECL_ALIGN(16) @@ -531,6 +933,7 @@ typedef unsigned int uint; #define ALIGN128_POST DECL_ALIGN(128) #else // MSVC has the align at the start of the struct +// PS3 SNC supports both #define ALIGN4 DECL_ALIGN(4) #define ALIGN8 DECL_ALIGN(8) #define ALIGN16 DECL_ALIGN(16) @@ -545,6 +948,30 @@ typedef unsigned int uint; #endif +//----------------------------------------------------------------------------- +// Macro to assist in asserting constant invariants during compilation + +// This implementation of compile time assert has zero cost (so it can safely be +// included in release builds) and can be used at file scope or function scope. +#ifdef __GNUC__ + #define COMPILE_TIME_ASSERT( pred ) typedef int UNIQUE_ID[ (pred) ? 1 : -1 ] +#else + #if _MSC_VER >= 1600 + // If available use static_assert instead of weird language tricks. This + // leads to much more readable messages when compile time assert constraints + // are violated. + #define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) + #else + // Due to gcc bugs this can in rare cases (some template functions) cause redeclaration + // errors when used multiple times in one scope. Fix by adding extra scoping. + #define COMPILE_TIME_ASSERT( pred ) typedef char compile_time_assert_type[(pred) ? 1 : -1]; + #endif +#endif +// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at global +// scope. However the new COMPILE_TIME_ASSERT macro supports that by default. +#define ASSERT_INVARIANT( pred ) COMPILE_TIME_ASSERT( pred ) + + // This can be used to declare an abstract (interface only) class. // Classes marked abstract should not be instantiated. If they are, and access violation will occur. // @@ -605,6 +1032,7 @@ typedef unsigned int uint; #pragma warning(disable : 4786) // Disable warnings about long symbol names #pragma warning(disable : 4250) // 'X' : inherits 'Y::Z' via dominance #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union +#pragma warning(disable : 4481) // warning C4481: nonstandard extension used: override specifier 'override' #if _MSC_VER >= 1300 #pragma warning(disable : 4511) // Disable warnings about private copy constructors @@ -624,14 +1052,43 @@ typedef unsigned int uint; #pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size #endif #endif +#elif defined( COMPILER_SNC ) +#pragma diag_suppress=1700 // warning 1700: class "%s" has virtual functions but non-virtual destructor +// Uncomment the following line if you want to investigate a specific compiler remark without all the noise: +// #pragma diag_suppress=1700, 83, 162, 182, 192, 194, 229, 238, 262, 341, 382, 401, 402, 403, 481, 817, 828, 833, 1363, 1771, 1774, 1779, 1780, 1783, 1785, 1786, 1788 +#endif +// Pull in the /analyze code annotations. +#include "annotations.h" +#ifdef POSIX +#pragma GCC diagnostic ignored "-Wswitch-enum" // enumeration values not handled in switch +#pragma GCC diagnostic ignored "-Wparentheses" // using the result of an assignment as a condition without parentheses #endif +#ifdef OSX +#pragma GCC diagnostic ignored "-Wconversion-null" // passing NULL to non-pointer argument 1 +#pragma GCC diagnostic ignored "-Wnull-arithmetic" // NULL used in arithmetic. Ie, vpanel == NULL where VPANEL is uint. +#pragma GCC diagnostic ignored "-Wlogical-op-parentheses" // '&&' within '||' (wants parenthesis) +#pragma GCC diagnostic ignored "-Wconstant-conversion" // implicit truncation from x to y (where y is smaller size than x) changes value +#pragma GCC diagnostic ignored "-Wformat-security" // format string is not a string literal (potentially insecure) +#pragma GCC diagnostic ignored "-Wreturn-type-c-linkage" // C-linkage specified, but returns user-defined type +#pragma GCC diagnostic ignored "-Wswitch" // enumeration values not handled in switch +#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" // virtual functions but non-virtual destructor +#pragma GCC diagnostic ignored "-Wformat" // type conversion, format/argument conflict +#pragma GCC diagnostic ignored "-Wbool-conversions" // type conversion +#endif + +//----------------------------------------------------------------------------- +// Convert int<-->pointer, avoiding 32/64-bit compiler warnings: +//----------------------------------------------------------------------------- +#define INT_TO_POINTER( i ) (void *)( ( i ) + (char *)NULL ) +#define POINTER_TO_INT( p ) ( (int)(uint64)( p ) ) + //----------------------------------------------------------------------------- // Stack-based allocation related helpers //----------------------------------------------------------------------------- -#if defined( COMPILER_GCC ) +#if defined( COMPILER_GCC ) || defined( COMPILER_SNC ) #define stackalloc( _size ) alloca( ALIGN_VALUE( _size, 16 ) ) @@ -648,8 +1105,14 @@ typedef unsigned int uint; #endif -#define stackfree( _p ) 0 +#define stackalloc_aligned( _size, _align ) (void*)( ( ((uintp)alloca( ALIGN_VALUE( ( _size ) + (_align ), ( _align ) ) )) + ( _align ) ) & ~_align ) + +// We should probably always just align to 16 bytes, stackalloc just causes too many problems without this behavior. Source2 does it already. +// #define stackalloc( _size ) stackalloc_aligned( _size, 16 ) +#define stackfree( _p ) 0 +// two-argument ( type, #elements) stackalloc +#define StackAlloc( typ, nelements ) ( ( typ * ) stackalloc_aligned( ( nelements ) * sizeof(typ), 16 ) ) //----------------------------------------------------------------------------- // Used to break into the debugger @@ -660,14 +1123,43 @@ typedef unsigned int uint; #define DebuggerBreak() __asm { int 3 } #elif COMPILER_MSVCX360 #define DebuggerBreak() DebugBreak() -#elif COMPILER_GCC - #if defined( PLATFORM_CYGWIN ) || defined( PLATFORM_POSIX ) +#elif COMPILER_GCC + #if defined( _PS3 ) + #if defined( __SPU__ ) + #define DebuggerBreak() __asm volatile ("stopd $0,$0,$0") + #else + #define DebuggerBreak() { __asm volatile ("tw 31,1,1"); } + #endif + #elif defined( OSX ) + #define DebuggerBreak() if ( Plat_IsInDebugSession() ) asm( "int3" ); else { raise(SIGTRAP); } + #elif defined( PLATFORM_CYGWIN ) || defined( PLATFORM_POSIX ) #define DebuggerBreak() __asm__( "int $0x3;") #else - #define DebuggerBreak() asm( "int3" ) + #define DebuggerBreak() raise(SIGTRAP) #endif +#elif defined( COMPILER_SNC ) && defined( COMPILER_PS3 ) +static bool sPS3_SuppressAssertsInThisFile = false; // you can throw this in the debugger to temporarily disable asserts inside any particular .cpp module. + #define DebuggerBreak() if (!sPS3_SuppressAssertsInThisFile) __builtin_snpause(); // from SNC Migration Guide, tw 31,1,1 +#else +#error DebuggerBreak() is not defined for this platform! #endif +#if defined( _X360 ) || defined( _PS3 ) + #if defined( fsel ) + #error + #endif +#else + +FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) +{ + return fComparand >= 0 ? fValGE : fLT; +} +FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) +{ + return fComparand >= 0 ? fValGE : fLT; +} + +#endif //----------------------------------------------------------------------------- // DLL export for platform utilities @@ -692,11 +1184,10 @@ typedef unsigned int uint; #endif // BUILD_AS_DLL - //----------------------------------------------------------------------------- // Returns true if debugger attached, false otherwise //----------------------------------------------------------------------------- -#if defined( PLATFORM_WINDOWS ) +#if defined( PLATFORM_WINDOWS ) || defined( _PS3 ) PLATFORM_INTERFACE void Plat_DebugString( const tchar * ); #else #define Plat_DebugString(s) ((void)0) @@ -706,7 +1197,6 @@ PLATFORM_INTERFACE bool Plat_IsInDebugSession(); #define DebuggerBreakIfDebugging() if ( !Plat_IsInDebugSession() ) ; else DebuggerBreak() - //----------------------------------------------------------------------------- // Message Box //----------------------------------------------------------------------------- @@ -738,18 +1228,51 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa #define _putenv putenv #define _chdir chdir #define _access access -#define _strtoi64 strtoll + +#define strcmpi stricmp +#define stricmp strcasecmp +#define _alloca alloca +#define GetProcAddress dlsym +#define _chdir chdir +#ifndef _PS3 +#define _strnicmp strnicmp +#endif +#define strnicmp strncasecmp +#define _snwprintf swprintf +#define swprintf_s swprintf +#define wcsicmp _wcsicmp +#define _wcsicmp wcscmp +#define _tempnam tempnam +#define strtok_s strtok_r +#define _mkdir(dir) mkdir( dir, S_IRWXU | S_IRWXG | S_IRWXO ) +#define _wtoi(arg) wcstol(arg, NULL, 10) +#define _wtoi64(arg) wcstoll(arg, NULL, 10) + +#ifndef _PS3 +typedef uintp HMODULE; +#endif +typedef void *HANDLE; +#define __cdecl #if !defined( _snprintf ) // some vpc's define this on the command line #define _snprintf snprintf #endif +#if !defined( __SPU__ ) #include #include // get unlink #include +#endif + #endif // PLATFORM_POSIX +#ifdef PLATFORM_WINDOWS +#ifndef SOCKLEN_T +#define SOCKLEN_T +typedef int socklen_t; +#endif +#endif //----------------------------------------------------------------------------- // Generally useful platform-independent macros (move to another file?) @@ -769,11 +1292,20 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa // as little code as possible, and throw an assertion in debug. #define NO_DEFAULT default: UNREACHABLE(); +#define MAX_FILEPATH 512 + // Defines MAX_PATH #ifndef MAX_PATH #define MAX_PATH 260 #endif +#ifdef _WIN32 +#define MAX_UNICODE_PATH 32767 +#else +#define MAX_UNICODE_PATH MAX_PATH +#endif + +#define MAX_UNICODE_PATH_IN_UTF8 MAX_UNICODE_PATH*4 //----------------------------------------------------------------------------- // FP exception handling @@ -829,13 +1361,40 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa #elif defined ( COMPILER_GCC ) +// Works for PS3 inline void SetupFPUControlWord() { +#ifdef _PS3 +// TODO: PS3 compiler spits out the following errors: +// C:/tmp/ccIN0aaa.s: Assembler messages: +// C:/tmp/ccIN0aaa.s(80): Error: Unrecognized opcode: `fnstcw' +// C:/tmp/ccIN0aaa.s(93): Error: Unrecognized opcode: `fldcw' +#else __volatile unsigned short int __cw; __asm __volatile ("fnstcw %0" : "=m" (__cw)); __cw = __cw & 0x0FCC0; // keep infinity control, keep rounding mode __cw = __cw | 0x023F; // set 53-bit, no exceptions __asm __volatile ("fldcw %0" : : "m" (__cw)); +#endif + } + +#elif defined ( COMPILER_SNC ) + +// Works for PS3 + inline void SetupFPUControlWord() + { +#ifdef _PS3 +// TODO: PS3 compiler spits out the following errors: +// C:/tmp/ccIN0aaa.s: Assembler messages: +// C:/tmp/ccIN0aaa.s(80): Error: Unrecognized opcode: `fnstcw' +// C:/tmp/ccIN0aaa.s(93): Error: Unrecognized opcode: `fldcw' +#else + __volatile unsigned short int __cw; + __asm __volatile ("fnstcw %0" : "=m" (__cw)); + __cw = __cw & 0x0FCC0; // keep infinity control, keep rounding mode + __cw = __cw | 0x023F; // set 53-bit, no exceptions + __asm __volatile ("fldcw %0" : : "m" (__cw)); +#endif } #elif defined( COMPILER_MSVCX360 ) @@ -853,7 +1412,7 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa return ( pResult[1] == 1 ); } #else - #define IsFPUControlWordSet() true + #define IsFPUControlWordSet() 1 #endif inline void SetupFPUControlWord() @@ -864,9 +1423,9 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa // Favour compatibility over speed (make sure the VPU set to Java-compliant mode) // NOTE: the VPU *always* uses round-to-nearest - __vector4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; - a; // Avoid compiler warning - __asm + __vector4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; + a; // Avoid compiler warning + __asm { mtvscr a; // Clear the Vector Status & Control Register to zero } @@ -874,6 +1433,30 @@ PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessa #endif // COMPILER_MSVCX360 +//----------------------------------------------------------------------------- +// Portability casting +//----------------------------------------------------------------------------- +template < typename Tdst, typename Tsrc > FORCEINLINE Tdst size_cast( Tsrc val ) +{ + static_assert( sizeof( Tdst ) <= sizeof( uint64 ) && sizeof( Tsrc ) <= sizeof( uint64 ), "Okay in my defense there weren't any types larger than 64-bits when this code was written." ); + +#ifdef DEBUG + if ( sizeof ( Tdst ) < sizeof ( Tsrc ) ) + { + Tdst cmpValDst = ( Tdst )val; + + // If this fails, the source value didn't actually fit in the destination value--you'll need to + // change the return type's size to match the source type in the calling code. + if ( val != ( Tsrc )cmpValDst ) + { + // Can't use assert here, and if this happens when running on a machine internally we should crash + // in preference to missing the problem ( so not DebuggerBreakIfDebugging() ). + DebuggerBreak(); + } + } +#endif + return ( Tdst )val; +} //----------------------------------------------------------------------------- // Purpose: Standard functions for handling endian-ness @@ -888,6 +1471,8 @@ inline T WordSwapC( T w ) { uint16 temp; + PLAT_COMPILE_TIME_ASSERT( sizeof( T ) == sizeof(uint16) ); + temp = ((*((uint16 *)&w) & 0xff00) >> 8); temp |= ((*((uint16 *)&w) & 0x00ff) << 8); @@ -899,6 +1484,8 @@ inline T DWordSwapC( T dw ) { uint32 temp; + PLAT_COMPILE_TIME_ASSERT( sizeof( T ) == sizeof(uint32) ); + temp = *((uint32 *)&dw) >> 24; temp |= ((*((uint32 *)&dw) & 0x00FF0000) >> 8); temp |= ((*((uint32 *)&dw) & 0x0000FF00) << 8); @@ -907,6 +1494,28 @@ inline T DWordSwapC( T dw ) return *((T*)&temp); } +template +inline T QWordSwapC( T dw ) +{ + // Assert sizes passed to this are already correct, otherwise + // the cast to uint64 * below is unsafe and may have wrong results + // or even crash. + PLAT_COMPILE_TIME_ASSERT( sizeof( dw ) == sizeof(uint64) ); + + uint64 temp; + + temp = *((uint64 *)&dw) >> 56; + temp |= ((*((uint64 *)&dw) & 0x00FF000000000000ull) >> 40); + temp |= ((*((uint64 *)&dw) & 0x0000FF0000000000ull) >> 24); + temp |= ((*((uint64 *)&dw) & 0x000000FF00000000ull) >> 8); + temp |= ((*((uint64 *)&dw) & 0x00000000FF000000ull) << 8); + temp |= ((*((uint64 *)&dw) & 0x0000000000FF0000ull) << 24); + temp |= ((*((uint64 *)&dw) & 0x000000000000FF00ull) << 40); + temp |= ((*((uint64 *)&dw) & 0x00000000000000FFull) << 56); + + return *((T*)&temp); +} + //------------------------------------- // Fast swaps //------------------------------------- @@ -969,11 +1578,14 @@ inline T DWordSwapC( T dw ) #endif +// No ASM implementation for this yet +#define QWordSwap QWordSwapC + //------------------------------------- // The typically used methods. //------------------------------------- -#if defined( _SGI_SOURCE ) || defined( PLATFORM_X360 ) +#if defined( _SGI_SOURCE ) || defined( PLATFORM_X360 ) || defined( _PS3 ) #define PLAT_BIG_ENDIAN 1 #else #define PLAT_LITTLE_ENDIAN 1 @@ -989,10 +1601,13 @@ inline T DWordSwapC( T dw ) #define BigWord( val ) WordSwap( val ) #define BigLong( val ) DWordSwap( val ) #define BigDWord( val ) DWordSwap( val ) +#define BigQWord( val ) QWordSwap( val ) #define LittleShort( val ) ( val ) #define LittleWord( val ) ( val ) #define LittleLong( val ) ( val ) #define LittleDWord( val ) ( val ) +#define LittleQWord( val ) ( val ) + #define SwapShort( val ) BigShort( val ) #define SwapWord( val ) BigWord( val ) #define SwapLong( val ) BigLong( val ) @@ -1009,10 +1624,12 @@ inline T DWordSwapC( T dw ) #define BigWord( val ) ( val ) #define BigLong( val ) ( val ) #define BigDWord( val ) ( val ) +#define BigQWord( val ) ( val ) #define LittleShort( val ) WordSwap( val ) #define LittleWord( val ) WordSwap( val ) #define LittleLong( val ) DWordSwap( val ) #define LittleDWord( val ) DWordSwap( val ) +#define LittleQWord( val ) QWordSwap( val ) #define SwapShort( val ) LittleShort( val ) #define SwapWord( val ) LittleWord( val ) #define SwapLong( val ) LittleLong( val ) @@ -1032,10 +1649,12 @@ inline short BigShort( short val ) { int test = 1; return ( *(char *)&test == 1 inline uint16 BigWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? WordSwap( val ) : val; } inline long BigLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } inline uint32 BigDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } +inline uint64 BigQWord( uint64 val ) { int test = 1; return ( *(char *)&test == 1 ) ? QWordSwap( val ) : val; } inline short LittleShort( short val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } inline uint16 LittleWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } inline long LittleLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } inline uint32 LittleDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } +inline uint64 LittleQWord( uint64 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : QWordSwap( val ); } inline short SwapShort( short val ) { return WordSwap( val ); } inline uint16 SwapWord( uint16 val ) { return WordSwap( val ); } inline long SwapLong( long val ) { return DWordSwap( val ); } @@ -1048,25 +1667,48 @@ inline void SwapFloat( float *pOut, const float *pIn ) { SafeSwapFloat( pOut, p #endif -#if COMPILER_MSVCX360 - inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) - { - return __loadwordbytereverse( dwordIndex<<2, base ); - } +#if !defined( __SPU__ ) - inline void StoreLittleDWord( uint32 *base, unsigned int dwordIndex, uint32 dword ) - { - __storewordbytereverse( dword, dwordIndex<<2, base ); - } - inline uint64 LoadLittleInt64( uint64 *base, unsigned int nWordIndex ) - { - return __loaddoublewordbytereverse( nWordIndex<<2, base ); - } +#if PLAT_BIG_ENDIAN + #if defined( _PS3 ) + inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) + { + return __lwbrx( base + dwordIndex ); + } - inline void StoreLittleInt64( uint64 *base, unsigned int nWordIndex, uint64 nWord ) - { - __storedoublewordbytereverse( nWord, nWordIndex<<2, base ); - } + inline void StoreLittleDWord( uint32 *base, unsigned int dwordIndex, uint32 dword ) + { + __stwbrx( base + dwordIndex, dword ); + } + inline uint64 LoadLittleInt64( uint64 *base, unsigned int nWordIndex ) + { + return __ldbrx( base + nWordIndex ); + } + + inline void StoreLittleInt64( uint64 *base, unsigned int nWordIndex, uint64 nWord ) + { + __stdbrx( base + nWordIndex, nWord ); + } + #else + inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) + { + return __loadwordbytereverse( dwordIndex<<2, base ); + } + + inline void StoreLittleDWord( uint32 *base, unsigned int dwordIndex, uint32 dword ) + { + __storewordbytereverse( dword, dwordIndex<<2, base ); + } + inline uint64 LoadLittleInt64( uint64 *base, unsigned int nWordIndex ) + { + return __loaddoublewordbytereverse( nWordIndex<<2, base ); + } + + inline void StoreLittleInt64( uint64 *base, unsigned int nWordIndex, uint64 nWord ) + { + __storedoublewordbytereverse( nWord, nWordIndex<<2, base ); + } + #endif #else inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) { @@ -1079,6 +1721,17 @@ inline void SwapFloat( float *pOut, const float *pIn ) { SafeSwapFloat( pOut, p } #endif +// Silences a number of warnings on 360 compiles. +inline uint64 CastPtrToUint64( const void *p ) +{ + return (uint64)( (uintp)p ); +} + +inline int64 CastPtrToInt64( const void *p ) +{ + return (int64)( (uintp)p ); +} + // When in benchmark mode, the timer returns a simple incremented value each time you call it. // @@ -1087,10 +1740,15 @@ inline void SwapFloat( float *pOut, const float *pIn ) { SafeSwapFloat( pOut, p PLATFORM_INTERFACE void Plat_SetBenchmarkMode( bool bBenchmarkMode ); PLATFORM_INTERFACE bool Plat_IsInBenchmarkMode(); +// Same as time() +PLATFORM_INTERFACE uint64 Plat_GetTime(); PLATFORM_INTERFACE double Plat_FloatTime(); // Returns time in seconds since the module was loaded. PLATFORM_INTERFACE uint32 Plat_MSTime(); // Time in milliseconds. +PLATFORM_INTERFACE uint64 Plat_USTime(); // Time in microseconds. PLATFORM_INTERFACE uint64 Plat_GetClockStart(); // Snapshot of the clock when app started. +PLATFORM_INTERFACE int32 Plat_timezone( void ); +PLATFORM_INTERFACE int32 Plat_daylight( void ); // Get the local calendar time. // Same as time() followed by localtime(), but non-crash-prone and threadsafe. @@ -1098,19 +1756,84 @@ PLATFORM_INTERFACE void Plat_GetLocalTime( struct tm *pNow ); // Convert a time_t (specified in nTime - seconds since Jan 1, 1970 UTC) to a local calendar time in a threadsafe and non-crash-prone way. PLATFORM_INTERFACE void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow ); +PLATFORM_INTERFACE struct tm * Plat_localtime( const time_t *timep, struct tm *result ); // Get a time string (same as ascstring, but threadsafe). PLATFORM_INTERFACE void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes ); // converts a time_t to a struct tm without the local time conversion of ConvertToLocalTime -PLATFORM_INTERFACE void Plat_gmtime( uint64 nTime, struct tm *pTime ); +PLATFORM_INTERFACE void Platform_gmtime( uint64 nTime, struct tm *pTime ); +PLATFORM_INTERFACE time_t Plat_timegm( struct tm *timeptr ); +// Compatibility definition: +inline struct tm * Plat_gmtime( const time_t *timep, struct tm *result ) { Platform_gmtime( *timep, result ); return result; } +// Other time functions +PLATFORM_INTERFACE char *Plat_ctime( const time_t *timep, char *buf, size_t bufsize ); + +typedef class CSysModule* PlatModule_t; +#define PLAT_MODULE_INVALID ((PlatModule_t)0) // Get the process' executable filename. PLATFORM_INTERFACE void Plat_GetModuleFilename( char *pOut, int nMaxBytes ); PLATFORM_INTERFACE void Plat_ExitProcess( int nCode ); +//called to exit the process due to a fatal error. This allows for the application to handle providing a hook as well which can be called +//before exiting +PLATFORM_INTERFACE void Plat_ExitProcessWithError( int nCode, bool bGenerateMinidump = false ); + +//sets the callback that will be triggered by Plat_ExitProcessWithError. NULL is valid. The return value true indicates that +//the exit has been handled and no further processing should be performed. False will cause a minidump to be generated, and the process +//to be terminated +typedef bool (*ExitProcessWithErrorCBFn)( int nCode ); +PLATFORM_INTERFACE void Plat_SetExitProcessWithErrorCB( ExitProcessWithErrorCBFn pfnCB ); + +// If OSX or Linux have 2GB of address space for 32-bit apps, then return true here when that case is detected +#if defined( OSX ) +// make memory tradeoffs for low-fragmentation (compact memory, use different patterns, etc) +inline bool Plat_NeedsLowFragmentation() { return true; } +#else +inline bool Plat_NeedsLowFragmentation() { return false; } +#endif + +PLATFORM_INTERFACE int Plat_chmod(const char *filename, int pmode); +PLATFORM_INTERFACE bool Plat_FileExists(const char *pFileName); +PLATFORM_INTERFACE size_t Plat_FileSize(const char *pFileName); +PLATFORM_INTERFACE bool Plat_IsDirectory(const char *pFilepath); +PLATFORM_INTERFACE bool Plat_FileIsReadOnly(const char *pFileName); + +#if defined( _WIN32 ) && defined( _MSC_VER ) && ( _MSC_VER >= 1400 ) +extern "C" unsigned __int64 __rdtsc(); +#pragma intrinsic(__rdtsc) +#endif + +inline uint64 Plat_Rdtsc() +{ +#if defined( _X360 ) + return ( uint64 )__mftb32(); +#elif defined( _WIN64 ) + return ( uint64 )__rdtsc(); +#elif defined( _WIN32 ) +#if defined( _MSC_VER ) && ( _MSC_VER >= 1400 ) + return ( uint64 )__rdtsc(); +#else + __asm rdtsc; + __asm ret; +#endif +#elif defined( __i386__ ) + uint64 val; + __asm__ __volatile__ ( "rdtsc" : "=A" (val) ); + return val; +#elif defined( __x86_64__ ) + uint32 lo, hi; + __asm__ __volatile__ ( "rdtsc" : "=a" (lo), "=d" (hi)); + return ( ( ( uint64 )hi ) << 32 ) | lo; +#else +#error +#endif +} + + // b/w compatibility #define Sys_FloatTime Plat_FloatTime @@ -1134,6 +1857,9 @@ struct CPUInformation { int m_Size; // Size of this structure, for forward compatability. + uint8 m_nLogicalProcessors; // Number op logical processors. + uint8 m_nPhysicalProcessors; // Number of physical processors + bool m_bRDTSC : 1, // Is RDTSC supported? m_bCMOV : 1, // Is CMOV supported? m_bFCMOV : 1, // Is FCMOV supported? @@ -1143,18 +1869,27 @@ struct CPUInformation m_bMMX : 1, // Is MMX supported? m_bHT : 1; // Is HyperThreading supported? - uint8 m_nLogicalProcessors; // Number op logical processors. - uint8 m_nPhysicalProcessors; // Number of physical processors - + bool m_bSSE3 : 1, m_bSSSE3 : 1, m_bSSE4a : 1, m_bSSE41 : 1, - m_bSSE42 : 1; + m_bSSE42 : 1, + m_bAVX : 1; // Is AVX supported? int64 m_Speed; // In cycles per second. tchar* m_szProcessorID; // Processor vendor Identification. + tchar* m_szProcessorBrand; // Processor brand string, if available + + uint32 m_nModel; + uint32 m_nFeatures[ 3 ]; + uint32 m_nL1CacheSizeKb; + uint32 m_nL1CacheDesc; + uint32 m_nL2CacheSizeKb; + uint32 m_nL2CacheDesc; + uint32 m_nL3CacheSizeKb; + uint32 m_nL3CacheDesc; CPUInformation(): m_Size(0){} }; @@ -1171,7 +1906,6 @@ PLATFORM_INTERFACE const CPUInformation& GetCPUInformation(); #pragma clang diagnostic pop #endif - PLATFORM_INTERFACE void GetCurrentDate( int *pDay, int *pMonth, int *pYear ); PLATFORM_INTERFACE void GetCurrentDayOfTheWeek( int *pDay ); // 0 = Sunday PLATFORM_INTERFACE void GetCurrentDayOfTheYear( int *pDay ); // 0 = Jan 1 @@ -1204,13 +1938,21 @@ PLATFORM_INTERFACE bool Plat_VerifyHardwareKeyPrompt(); // like much. PLATFORM_INTERFACE bool Plat_FastVerifyHardwareKey(); +//----------------------------------------------------------------------------- +// The following are low-level OS-independent wrappers around actual OS file calls. +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_getwd( char *pWorkingDirectory, size_t nBufLen ); + //----------------------------------------------------------------------------- // Just logs file and line to simple.log //----------------------------------------------------------------------------- PLATFORM_INTERFACE void* Plat_SimpleLog( const tchar* file, int line ); -#if _X360 +#endif // #if !defined( __SPU__ ) + + +#if defined( _X360 ) #define Plat_FastMemset XMemSet #define Plat_FastMemcpy XMemCpy #else @@ -1218,12 +1960,6 @@ PLATFORM_INTERFACE void* Plat_SimpleLog( const tchar* file, int line ); #define Plat_FastMemcpy memcpy #endif -//----------------------------------------------------------------------------- -// Returns true if running on a 64 bit (windows) OS -//----------------------------------------------------------------------------- -PLATFORM_INTERFACE bool Is64BitOS(); - - //----------------------------------------------------------------------------- // XBOX Components valid in PC compilation space //----------------------------------------------------------------------------- @@ -1257,10 +1993,19 @@ PLATFORM_INTERFACE bool Is64BitOS(); #define WM_XMP_STATECHANGED (WM_USER + 121) #define WM_XMP_PLAYBACKBEHAVIORCHANGED (WM_USER + 122) #define WM_XMP_PLAYBACKCONTROLLERCHANGED (WM_USER + 123) +#define WM_SYS_SHUTDOWNREQUEST (WM_USER + 124) + +#if defined( _PS3 ) +#define PLATFORM_EXT ".ps3" +#elif defined( PLATFORM_X360 ) +#define PLATFORM_EXT ".360" +#else +#define PLATFORM_EXT "" +#endif inline const char *GetPlatformExt( void ) { - return IsPlatformX360() ? ".360" : ""; + return PLATFORM_EXT; } // flat view, 6 hw threads @@ -1284,6 +2029,68 @@ inline const char *GetPlatformExt( void ) //----------------------------------------------------------------------------- #if defined( PLATFORM_X360 ) #include "xbox/xbox_core.h" +#elif defined( PLATFORM_PS3 ) +#include "ps3/ps3_core.h" +#endif + + +//----------------------------------------------------------------------------- +// There is no requirement that a va_list be usable in multiple calls, +// but the Steam code does this. Linux64 does not support reuse, whereas +// Windows does, so Linux64 breaks on code that was written and working +// on Windows. Fortunately Linux has va_copy, which provides a simple +// way to let a va_list be used multiple times. Unfortunately Windows +// does not have va_copy, so here we provide things to hide the difference. +//----------------------------------------------------------------------------- + +class CReuseVaList +{ +public: + CReuseVaList( va_list List ) + { +#if defined(POSIX) || defined(OSX) + va_copy( m_ReuseList, List ); +#else + m_ReuseList = List; +#endif + } + ~CReuseVaList() + { +#if defined(POSIX) || defined(OSX) + va_end( m_ReuseList ); +#endif + } + + va_list m_ReuseList; +}; + +//----------------------------------------------------------------------------- +// C++11 helpers +//----------------------------------------------------------------------------- +#define VALVE_CPP11 1 + +#if VALVE_CPP11 +template struct C11RemoveReference { typedef T Type; }; +template struct C11RemoveReference { typedef T Type; }; +template struct C11RemoveReference { typedef T Type; }; + +template +inline typename C11RemoveReference::Type&& Move( T&& obj ) +{ + return static_cast< typename C11RemoveReference::Type&& >( obj ); +} + +template +inline T&& Forward( typename C11RemoveReference::Type& obj ) +{ + return static_cast< T&& >( obj ); +} + +template +inline T&& Forward( typename C11RemoveReference::Type&& obj ) +{ + return static_cast< T&& >( obj ); +} #endif //----------------------------------------------------------------------------- @@ -1332,6 +2139,24 @@ inline T* CopyConstruct( T* pMemory, T const& src ) return ::new( pMemory ) T(src); } +template +inline T* MoveConstruct( T* pMemory, T&& src ) +{ + return ::new( pMemory ) T( Move(src) ); +} + +// [will] - Fixing a clang compile: unable to create a pseudo-destructor (aka a destructor that does nothing) for float __attribute__((__vector_size__(16))) +// Fixed by specializing the Destroy function to not call destructor for that type. +#if defined( __clang__ ) || defined (LINUX) + +template +inline void Destruct( T* pMemory ); + +template <> +inline void Destruct( float __attribute__((__vector_size__(16)))* pMemory ); + +#endif // __clang__ + template inline void Destruct( T* pMemory ) { @@ -1342,6 +2167,19 @@ inline void Destruct( T* pMemory ) #endif } +// [will] - Fixing a clang compile: unable to create a pseudo-destructor (aka a destructor that does nothing) for float __attribute__((__vector_size__(16))) +// Fixed by specializing the Destroy function to not call destructor for that type. +#if defined( __clang__ ) || defined (LINUX) + +template <> +inline void Destruct( float __attribute__((__vector_size__(16)))* pMemory ) +{ +#ifdef _DEBUG + memset( pMemory, 0xDD, sizeof( float __attribute__((__vector_size__(16))) ) ); +#endif +} + +#endif // __clang__ // // GET_OUTER() @@ -1477,37 +2315,6 @@ RETURN_TYPE FASTCALL __Function_##NAME::Run ARGS } -//----------------------------------------------------------------------------- -// Dynamic libs support -//----------------------------------------------------------------------------- -#if defined( PLATFORM_WINDOWS ) - -PLATFORM_INTERFACE void *Plat_GetProcAddress( const char *pszModule, const char *pszName ); - -template -class CDynamicFunction -{ -public: - CDynamicFunction( const char *pszModule, const char *pszName, FUNCPTR_TYPE pfnFallback = NULL ) - { - m_pfn = pfnFallback; - void *pAddr = Plat_GetProcAddress( pszModule, pszName ); - if ( pAddr ) - { - m_pfn = (FUNCPTR_TYPE)pAddr; - } - } - - operator bool() { return m_pfn != NULL; } - bool operator !() { return !m_pfn; } - operator FUNCPTR_TYPE() { return m_pfn; } - -private: - FUNCPTR_TYPE m_pfn; -}; -#endif - - //----------------------------------------------------------------------------- // What OS version are we? //----------------------------------------------------------------------------- @@ -1518,9 +2325,15 @@ enum PlatOSVersion_t // X360-specific versions PLAT_OS_VERSION_XBOX360 = 0, - // PC-specific OS versions - PLAT_OS_VERSION_XP = 5, - PLAT_OS_VERSION_VISTA = 6, + // PC-specific OS versions (single byte) + PLAT_OS_VERSION_WIN2K = 50, + PLAT_OS_VERSION_XP = 51, + PLAT_OS_VERSION_WIN2003 = 52, + PLAT_OS_VERSION_VISTA = 60, + PLAT_OS_VERSION_WIN7 = 61, + PLAT_OS_VERSION_WIN8 = 62, + PLAT_OS_VERSION_WIN81 = 63, + PLAT_OS_VERSION_WIN10 = 100, }; PLATFORM_INTERFACE PlatOSVersion_t Plat_GetOSVersion(); @@ -1533,7 +2346,7 @@ PLATFORM_INTERFACE PlatOSVersion_t Plat_GetOSVersion(); // under linux right now. It should be possible to implement this functionality in windows via a // thread, if desired. -#ifdef _POSIX +#if defined( POSIX ) && !defined( _PS3 ) PLATFORM_INTERFACE void BeginWatchdogTimer( int nSecs ); PLATFORM_INTERFACE void EndWatchdogTimer( void ); @@ -1625,12 +2438,13 @@ FORCEINLINE uint64 RotateBitsRight64( uint64 nValue, int nRotateBits ) return ( nValue >> nRotateBits ) | ( nValue << ( ( - nRotateBits ) & 63 ) ); } #endif - +PLATFORM_INTERFACE const char * GetPlatformSpecificFileName(const char * FileName); #include "tier0/valve_on.h" #if defined(TIER0_DLL_EXPORT) -extern int V_tier0_stricmp(const char *s1, const char *s2 ); +#undef stricmp +#undef strcmpi #define stricmp(s1,s2) V_tier0_stricmp( s1, s2 ) #define strcmpi(s1,s2) V_tier0_stricmp( s1, s2 ) #else @@ -1645,15 +2459,120 @@ int V_strncasecmp (const char *s1, const char *s2, int n); // returns <0 if strings do not match even in a case-insensitive way int _V_stricmp_NegativeForUnequal ( const char *s1, const char *s2 ); -#ifndef stricmp +#undef stricmp +#undef strcmpi #define stricmp(s1,s2) _V_stricmp(s1, s2) -#endif -#ifndef strcmpi #define strcmpi(s1,s2) _V_stricmp(s1, s2) +#undef strnicmp +#define strnicmp V_strncasecmp #endif -#ifndef strnicmp -#define strnicmp V_strncasecmp + +// Use AlignedByteArray_t if you need an appropriately aligned array of T with no constructor (e.g CUtlMemoryFixed): +// - usage: AlignedByteArray_t< NUM, T > +// - same as: byte[ NUM*sizeof(T) ] +// - BUT: avoids calling T's constructor +// - AND: has same alignment as T +// [ Thanks to CygnusX1: http://stackoverflow.com/questions/5134217/aligning-data-on-the-stack-c ] + +#if defined( GNUC ) +// gnuc has the align decoration at the end +#define ALIGN4 +#define ALIGN8 +#define ALIGN16 +#define ALIGN32 +#define ALIGN128 +#define ALIGN_N( _align_ ) + +#undef ALIGN16_POST +#define ALIGN4_POST DECL_ALIGN(4) +#define ALIGN8_POST DECL_ALIGN(8) +#define ALIGN16_POST DECL_ALIGN(16) +#define ALIGN32_POST DECL_ALIGN(32) +#define ALIGN128_POST DECL_ALIGN(128) +#define ALIGN_N_POST( _align_ ) DECL_ALIGN( _align_ ) +#else +// MSVC has the align at the start of the struct +// PS3 SNC supports both +#define ALIGN4 DECL_ALIGN(4) +#define ALIGN8 DECL_ALIGN(8) +#define ALIGN16 DECL_ALIGN(16) +#define ALIGN32 DECL_ALIGN(32) +#define ALIGN128 DECL_ALIGN(128) +#define ALIGN_N( _align_ ) DECL_ALIGN( _align_ ) + +#define ALIGN4_POST +#define ALIGN8_POST +#define ALIGN16_POST +#define ALIGN32_POST +#define ALIGN128_POST +#define ALIGN_N_POST( _align_ ) #endif + +// !!! NOTE: if you get a compile error here, you are using VALIGNOF on an abstract type :NOTE !!! +#define VALIGNOF_PORTABLE( type ) ( sizeof( AlignOf_t ) - sizeof( type ) ) + +#if defined( COMPILER_GCC ) || defined( COMPILER_MSVC ) +#define VALIGNOF( type ) __alignof( type ) +#define VALIGNOF_TEMPLATE_SAFE( type ) VALIGNOF_PORTABLE( type ) +#else +#error "PORT: Code only tested with MSVC! Must validate with new compiler, and use built-in keyword if available." #endif +// Use ValidateAlignment to sanity-check alignment usage when allocating arrays of an aligned type +#define ALIGN_ASSERT( pred ) { COMPILE_TIME_ASSERT( pred ); } +template< class T, int ALIGN > +inline void ValidateAlignmentExplicit(void) +{ + // Alignment must be a power of two + ALIGN_ASSERT((ALIGN & (ALIGN - 1)) == 0); + // Alignment must not imply gaps in the array (which the CUtlMemory pattern does not allow for) + ALIGN_ASSERT(ALIGN <= sizeof(T)); + // Alignment must be a multiple of the size of the object type, or elements will *NOT* be aligned! + ALIGN_ASSERT((sizeof(T) % ALIGN) == 0); + // Alignment should be a multiple of the base alignment of T +// ALIGN_ASSERT((ALIGN % VALIGNOF(T)) == 0); +} +template< class T > inline void ValidateAlignment(void) { ValidateAlignmentExplicit(); } + +// Portable alternative to __alignof +template struct AlignOf_t { AlignOf_t(){} AlignOf_t & operator=(const AlignOf_t &) { return *this; } byte b; T t; }; + +template < size_t NUM, class T, int ALIGN > struct AlignedByteArrayExplicit_t{}; +template < size_t NUM, class T > struct AlignedByteArray_t : public AlignedByteArrayExplicit_t< NUM, T, VALIGNOF_TEMPLATE_SAFE(T) > {}; + +#define DECLARE_ALIGNED_BYTE_ARRAY( ALIGN ) \ + template < size_t NUM, class T > \ + struct ALIGN_N( ALIGN ) AlignedByteArrayExplicit_t< NUM, T, ALIGN > \ + { \ + /* NOTE: verify alignment in the constructor (which may be wrong if this is heap-allocated, for ALIGN > MEMALLOC_MAX_AUTO_ALIGN) */ \ + AlignedByteArrayExplicit_t() { if ( (ALIGN-1) & (size_t)this ) DebuggerBreakIfDebugging(); } \ + T * Base( void ) { ValidateAlignmentExplicit(); return (T *)&m_Data; } \ + const T * Base( void ) const { ValidateAlignmentExplicit(); return (const T *)&m_Data; } \ + private: \ + byte m_Data[ NUM*sizeof( T ) ]; \ + } ALIGN_N_POST( ALIGN ); + +DECLARE_ALIGNED_BYTE_ARRAY(1); +DECLARE_ALIGNED_BYTE_ARRAY(2); +DECLARE_ALIGNED_BYTE_ARRAY(4); +DECLARE_ALIGNED_BYTE_ARRAY(8); +DECLARE_ALIGNED_BYTE_ARRAY(16); +DECLARE_ALIGNED_BYTE_ARRAY(32); +DECLARE_ALIGNED_BYTE_ARRAY(64); +DECLARE_ALIGNED_BYTE_ARRAY(128); + +// Tier0 uses this for faster stricmp. +PLATFORM_INTERFACE int V_tier0_stricmp( const char *a, const char *b ); + +PLATFORM_INTERFACE void V_tier0_strncpy( char *a, const char *b, int n ); +PLATFORM_INTERFACE char *V_tier0_strncat( char *a, const char *b, int n, int m = -1 ); +PLATFORM_INTERFACE int V_tier0_vsnprintf( char *a, int n, PRINTF_FORMAT_STRING const char *f, va_list l ) FMTFUNCTION( 3, 0 ); +PLATFORM_INTERFACE int V_tier0_snprintf( char *a, int n, PRINTF_FORMAT_STRING const char *f, ... ) FMTFUNCTION( 3, 4 ); + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE char const * Plat_GetEnv(char const *pEnvVarName); + +PLATFORM_INTERFACE bool Plat_GetExecutablePath(char* pBuff, size_t nBuff); + #endif /* PLATFORM_H */ diff --git a/public/tier0/platform_override.h b/public/tier0/platform_override.h new file mode 100644 index 000000000..0c8dceebf --- /dev/null +++ b/public/tier0/platform_override.h @@ -0,0 +1,33 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// This file is a force-include override + +#ifdef PLATFORM_OVERRIDE_MATERIALSYSTEM +#define PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3 g_pMemAllocInternalPS3Override_MaterialSystem +#endif + +#ifdef PLATFORM_OVERRIDE_TIER0 +#define PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3 g_pMemAllocInternalPS3Override_Tier0 +#endif + +#ifdef PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3 + +class IMemAlloc; +struct IMemAlloc_CachedInterface_t +{ + IMemAlloc_CachedInterface_t(); + IMemAlloc *m_pMemAllocCached; +}; +extern IMemAlloc_CachedInterface_t PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3; +#define PLATFORM_INTERFACE_MEM_ALLOC_INTERNAL_PS3_OVERRIDE PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3.m_pMemAllocCached + +#define PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL \ + IMemAlloc_CachedInterface_t PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3 CONSTRUCT_EARLY; \ + IMemAlloc_CachedInterface_t::IMemAlloc_CachedInterface_t() \ + { \ + m_pMemAllocCached = g_pMemAllocInternalPS3; \ + } + +#endif + +// Force-include delegation to platform.h +#include "platform.h" diff --git a/public/tier1/murmurhash3.h b/public/tier1/murmurhash3.h new file mode 100644 index 000000000..8f477cdc2 --- /dev/null +++ b/public/tier1/murmurhash3.h @@ -0,0 +1,100 @@ +//======= Copyright © Valve Corporation, All rights reserved. ================= +// +// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose +// hash with a 32-bit output. References: +// http://code.google.com/p/smhasher/ (home of MurmurHash3) +// https://sites.google.com/site/murmurhash/avalanche +// http://www.strchr.com/hash_functions +// +//============================================================================= + +#ifndef MURMURHASH3_H +#define MURMURHASH3_H + +#if defined(_WIN32) +#pragma once +#endif + +uint32 MurmurHash3_32( const void *key, size_t len, uint32 seed, bool bCaselessStringVariant = false ); + +inline uint32 MurmurHash3String( const char *pszKey, size_t len ) +{ + return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, false ); +} + +inline uint32 MurmurHash3StringCaseless( const char *pszKey, size_t len ) +{ + return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, true ); +} + +inline uint32 MurmurHash3String( const char *pszKey ) +{ + return MurmurHash3String( pszKey, strlen( pszKey ) ); +} + +inline uint32 MurmurHash3StringCaseless( const char *pszKey ) +{ + return MurmurHash3StringCaseless( pszKey, strlen( pszKey ) ); +} + +template +inline uint32 MurmurHash3Item( const T &item ) +{ + return MurmurHash3_32( &item, sizeof(item), 1047 ); +} + +inline uint32 MurmurHash3Int( uint32 h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + + +template <> +inline uint32 MurmurHash3Item( const uint32 &item ) +{ + return MurmurHash3Int( item ); +} + +template <> +inline uint32 MurmurHash3Item( const int32 &item ) +{ + return MurmurHash3Int( item ); +} + + +template +struct MurmurHash3Functor +{ + typedef uint32 TargetType; + TargetType operator()(const T &key) const + { + return MurmurHash3Item( key ); + } +}; + +template<> +struct MurmurHash3Functor +{ + typedef uint32 TargetType; + TargetType operator()(const char *key) const + { + return MurmurHash3String( key ); + } +}; + +template<> +struct MurmurHash3Functor +{ + typedef uint32 TargetType; + TargetType operator()(const char *key) const + { + return MurmurHash3String( key ); + } +}; + +#endif // MURMURHASH3_H diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h index 91265a0b2..4df7b1c5d 100644 --- a/public/tier1/utlbuffer.h +++ b/public/tier1/utlbuffer.h @@ -1,4 +1,4 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +//====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // @@ -41,7 +41,7 @@ class CUtlCharConversion struct ConversionArray_t { char m_nActualChar; - const char *m_pReplacementString; + char *m_pReplacementString; }; CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); @@ -60,7 +60,7 @@ class CUtlCharConversion struct ConversionInfo_t { int m_nLength; - const char *m_pReplacementString; + char *m_pReplacementString; }; char m_nEscapeChar; @@ -68,8 +68,8 @@ class CUtlCharConversion int m_nDelimiterLength; int m_nCount; int m_nMaxConversionLength; - char m_pList[255]; - ConversionInfo_t m_pReplacements[255]; + char m_pList[256]; + ConversionInfo_t m_pReplacements[256]; }; #define BEGIN_CHAR_CONVERSION( _name, _delimiter, _escapeChar ) \ @@ -109,16 +109,23 @@ typedef unsigned short ushort; template < class A > static const char *GetFmtStr( int nRadix = 10, bool bPrint = true ) { Assert( 0 ); return ""; } - -template <> inline const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } -template <> inline const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } -template <> inline const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } -template <> inline const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } -template <> inline const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } -template <> inline const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } -template <> inline const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 - - +#if defined( LINUX ) || defined( __clang__ ) || ( defined( _MSC_VER ) && _MSC_VER >= 1900 ) +template <> const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } +template <> const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } +template <> const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } +template <> const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } +template <> const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } +template <> const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 +#else +template <> static const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } +template <> static const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> static const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } +template <> static const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } +template <> static const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } +template <> static const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } +template <> static const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 +#endif //----------------------------------------------------------------------------- // Command parsing.. //----------------------------------------------------------------------------- @@ -164,7 +171,19 @@ class CUtlBuffer CUtlBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 ); CUtlBuffer( const void* pBuffer, int size, int nFlags = 0 ); // This one isn't actually defined so that we catch contructors that are trying to pass a bool in as the third param. - CUtlBuffer( const void *pBuffer, int size, bool crap ); + CUtlBuffer( const void *pBuffer, int size, bool crap ) = delete; + + // UtlBuffer objects should not be copyable; we do a slow copy if you use this but it asserts. + // (REI: I'd like to delete these but we have some python bindings that currently rely on being able to copy these objects) + CUtlBuffer( const CUtlBuffer& ); // = delete; + CUtlBuffer& operator= ( const CUtlBuffer& ); // = delete; + +#if VALVE_CPP11 + // UtlBuffer is non-copyable (same as CUtlMemory), but it is moveable. We would like to declare these with '= default' + // but unfortunately VS2013 isn't fully C++11 compliant, so we have to manually declare these in the boilerplate way. + CUtlBuffer( CUtlBuffer&& moveFrom ); // = default; + CUtlBuffer& operator= ( CUtlBuffer&& moveFrom ); // = default; +#endif unsigned char GetFlags() const; @@ -185,9 +204,17 @@ class CUtlBuffer void *Detach(); void* DetachMemory(); + // copies data from another buffer + void CopyBuffer( const CUtlBuffer &buffer ); + void CopyBuffer( const void *pubData, int cubData ); + + void Swap( CUtlBuffer &buf ); + void Swap( CUtlMemory &mem ); + + FORCEINLINE void ActivateByteSwappingIfBigEndian( void ) { - if ( IsX360() ) + if ( ( IsX360() || IsPS3() ) ) ActivateByteSwapping( true ); } @@ -219,12 +246,13 @@ class CUtlBuffer int64 GetInt64( ); unsigned int GetIntHex( ); unsigned int GetUnsignedInt( ); + uint64 GetUnsignedInt64( ); float GetFloat( ); double GetDouble( ); void * GetPtr(); - void GetString( char* pString, int nMaxChars = 0 ); - void Get( void* pMem, int size ); - void GetLine( char* pLine, int nMaxChars = 0 ); + void GetString( char* pString, int nMaxChars ); + bool Get( void* pMem, int size ); + void GetLine( char* pLine, int nMaxChars ); // Used for getting objects that have a byteswap datadesc defined template void GetObjects( T *dest, int count = 1 ); @@ -255,7 +283,7 @@ class CUtlBuffer int PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActualSize = true ); // Just like scanf, but doesn't work in binary mode - int Scanf( const char* pFmt, ... ); + int Scanf( SCANF_FORMAT_STRING const char* pFmt, ... ); int VaScanf( const char* pFmt, va_list list ); // Eats white space, advances Get index @@ -293,6 +321,7 @@ class CUtlBuffer void PutInt( int i ); void PutInt64( int64 i ); void PutUnsignedInt( unsigned int u ); + void PutUnsignedInt64( uint64 u ); void PutFloat( float f ); void PutDouble( double d ); void PutPtr( void * ); // Writes the pointer, not the pointed to @@ -308,7 +337,7 @@ class CUtlBuffer void PutDelimitedChar( CUtlCharConversion *pConv, char c ); // Just like printf, writes a terminating zero in binary mode - void Printf( const char* pFmt, ... ) FMTFUNCTION( 2, 3 ); + void Printf( PRINTF_FORMAT_STRING const char* pFmt, ... ) FMTFUNCTION( 2, 3 ); void VaPrintf( const char* pFmt, va_list list ); // What am I writing (put)/reading (get)? @@ -366,6 +395,12 @@ class CUtlBuffer // Temporarily disables pretty print void EnableTabs( bool bEnable ); +#if !defined( _GAMECONSOLE ) + // Swap my internal memory with another buffer, + // and copy all of its other members + void SwapCopy( CUtlBuffer &other ) ; +#endif + protected: // error flags enum @@ -385,7 +420,9 @@ class CUtlBuffer bool CheckPut( int size ); bool CheckGet( int size ); - void AddNullTermination( ); + // NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately + // after modifying m_Put and this lets it stay in a register + void AddNullTermination( int nPut ); // Methods to help with pretty-printing bool WasLastCharacterCR(); @@ -424,6 +461,8 @@ class CUtlBuffer template void PutTypeBin( T src ); template void PutObject( T *src ); + // be sure to also update the copy constructor + // and SwapCopy() when adding members. CUtlMemory m_Memory; int m_Get; int m_Put; @@ -431,7 +470,7 @@ class CUtlBuffer unsigned char m_Error; unsigned char m_Flags; unsigned char m_Reserved; -#if defined( _X360 ) +#if defined( _GAMECONSOLE ) unsigned char pad; #endif @@ -671,7 +710,7 @@ inline void CUtlBuffer::GetTypeBin< float >( float &dest ) if ( CheckGet( sizeof( float ) ) ) { uintp pData = (uintp)PeekGet(); - if ( IsX360() && ( pData & 0x03 ) ) + if ( ( IsX360() || IsPS3() ) && ( pData & 0x03 ) ) { // handle unaligned read ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; @@ -702,7 +741,7 @@ inline void CUtlBuffer::GetTypeBin< double >( double &dest ) if ( CheckGet( sizeof( double ) ) ) { uintp pData = (uintp)PeekGet(); - if ( IsX360() && ( pData & 0x07 ) ) + if ( ( IsX360() || IsPS3() ) && ( pData & 0x07 ) ) { // handle unaligned read ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; @@ -778,20 +817,24 @@ inline uint32 StringToNumber( char *pString, char **ppEnd, int nRadix ) template <> inline int64 StringToNumber( char *pString, char **ppEnd, int nRadix ) { +#if defined(_PS3) || defined(POSIX) + return ( int64 )strtoll( pString, ppEnd, nRadix ); +#else // !_PS3 return ( int64 )_strtoi64( pString, ppEnd, nRadix ); +#endif // _PS3 } template <> inline float StringToNumber( char *pString, char **ppEnd, int nRadix ) { - // /*UNUSED*/( nRadix ); + NOTE_UNUSED( nRadix ); return ( float )strtod( pString, ppEnd ); } template <> inline double StringToNumber( char *pString, char **ppEnd, int nRadix ) { - // /*UNUSED*/( nRadix ); + NOTE_UNUSED( nRadix ); return ( double )strtod( pString, ppEnd ); } @@ -903,6 +946,14 @@ inline unsigned int CUtlBuffer::GetUnsignedInt( ) return i; } +inline uint64 CUtlBuffer::GetUnsignedInt64() +{ + uint64 i; + GetType( i ); + return i; +} + + inline float CUtlBuffer::GetFloat( ) { float f; @@ -921,7 +972,7 @@ inline void *CUtlBuffer::GetPtr( ) { void *p; // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal -#ifndef X64BITS +#if !defined(X64BITS) && !defined(PLATFORM_64BITS) p = ( void* )GetUnsignedInt(); #else p = ( void* )GetInt64(); @@ -992,7 +1043,7 @@ inline void CUtlBuffer::PutObject( T *src ) m_Byteswap.SwapFieldsToTargetEndian( (T*)PeekPut(), src ); } m_Put += sizeof(T); - AddNullTermination(); + AddNullTermination( m_Put ); } } @@ -1021,11 +1072,11 @@ inline void CUtlBuffer::PutTypeBin( T src ) m_Byteswap.SwapBufferToTargetEndian( (T*)PeekPut(), &src ); } m_Put += sizeof(T); - AddNullTermination(); + AddNullTermination( m_Put ); } } -#if defined( _X360 ) +#if defined( _GAMECONSOLE ) template <> inline void CUtlBuffer::PutTypeBin< float >( float src ) { @@ -1056,7 +1107,7 @@ inline void CUtlBuffer::PutTypeBin< float >( float src ) } m_Put += sizeof(float); - AddNullTermination(); + AddNullTermination( m_Put ); } } @@ -1094,7 +1145,7 @@ inline void CUtlBuffer::PutTypeBin< double >( double src ) } m_Put += sizeof(double); - AddNullTermination(); + AddNullTermination( m_Put ); } } #endif @@ -1211,6 +1262,12 @@ inline void CUtlBuffer::PutUnsignedInt( unsigned int u ) PutType( u ); } +inline void CUtlBuffer::PutUnsignedInt64( uint64 i ) +{ + PutType( i ); +} + + inline void CUtlBuffer::PutFloat( float f ) { PutType( f ); @@ -1308,7 +1365,7 @@ inline void CUtlBuffer::Clear() m_Error = 0; m_nOffset = 0; m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } inline void CUtlBuffer::Purge() @@ -1349,11 +1406,43 @@ inline void CUtlBuffer::Spew( ) while( IsValid() && GetBytesRemaining() ) { V_memset( pTmpLine, 0, sizeof(pTmpLine) ); - Get( pTmpLine, MIN( (unsigned int)GetBytesRemaining(), sizeof(pTmpLine)-1 ) ); + Get( pTmpLine, MIN( ( size_t )GetBytesRemaining(), sizeof(pTmpLine)-1 ) ); Msg( _T( "%s" ), pTmpLine ); } } +#if !defined(_GAMECONSOLE) +inline void CUtlBuffer::SwapCopy( CUtlBuffer &other ) +{ + m_Get = other.m_Get; + m_Put = other.m_Put; + m_Error = other.m_Error; + m_Flags = other.m_Flags; + m_Reserved = other.m_Reserved; + m_nTab = other.m_nTab; + m_nMaxPut = other.m_nMaxPut; + m_nOffset = other.m_nOffset; + m_GetOverflowFunc = other.m_GetOverflowFunc; + m_PutOverflowFunc = other.m_PutOverflowFunc; + m_Byteswap = other.m_Byteswap; + + m_Memory.Swap( other.m_Memory ); +} +#endif + +inline void CUtlBuffer::CopyBuffer( const CUtlBuffer &buffer ) +{ + CopyBuffer( buffer.Base(), buffer.TellPut() ); +} + +inline void CUtlBuffer::CopyBuffer( const void *pubData, int cubData ) +{ + Clear(); + if ( cubData ) + { + Put( pubData, cubData ); + } +} #endif // UTLBUFFER_H diff --git a/public/tier1/utlhashmaplarge.h b/public/tier1/utlhashmaplarge.h new file mode 100644 index 000000000..1f289a536 --- /dev/null +++ b/public/tier1/utlhashmaplarge.h @@ -0,0 +1,694 @@ +//========= Copyright Valve Corporation, All rights reserved. =================// +// +// Purpose: index-based hash map container well suited for large and growing +// datasets. It uses less memory than other hash maps and incrementally +// rehashes to reduce reallocation spikes. +// +//=============================================================================// + +#ifndef UTLHASHMAPLARGE_H +#define UTLHASHMAPLARGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "bitvec.h" +#include "utlmap.h" +#include "murmurhash3.h" + +// fast mod for power of 2 numbers +namespace basetypes +{ +template +inline bool IsPowerOf2(T n) +{ + return n > 0 && (n & (n-1)) == 0; +} + +template +inline T2 ModPowerOf2(T1 a, T2 b) +{ + return T2(a) & (b-1); +} +} + +// default comparison operator +template +class CDefEquals +{ +public: + CDefEquals() {} + CDefEquals( int i ) {} + inline bool operator()( const T &lhs, const T &rhs ) const { return ( lhs == rhs ); } + inline bool operator!() const { return false; } +}; + + +// Specialization to compare pointers +template +class CDefEquals +{ +public: + CDefEquals() {} + CDefEquals( int i ) {} + inline bool operator()( const T *lhs, const T *rhs ) const + { + if ( lhs == rhs ) + return true; + else if ( NULL == lhs || NULL == rhs ) + return false; + else + return ( *lhs == *rhs ); + } + inline bool operator!() const { return false; } +}; + + +// Hash specialization for CUtlStrings +template<> +struct MurmurHash3Functor +{ + typedef uint32 TargetType ; + TargetType operator()(const CUtlString &strKey) const + { + return MurmurHash3Functor()( strKey.String() ); + } +}; + +//hash 3 function for a general case sensitive string compares +struct MurmurHash3ConstCharPtr +{ + typedef uint32 TargetType ; + TargetType operator()( const char* pszKey ) const { return MurmurHash3Functor()( pszKey ); } +}; +struct CaseSensitiveStrEquals +{ + bool operator()( const char* pszLhs, const char* pszRhs ) const { return strcmp( pszLhs, pszRhs ) == 0; } +}; + +//----------------------------------------------------------------------------- +// +// Purpose: An associative container. Pretty much identical to CUtlMap without the ability to walk in-order +// This container is well suited for large and growing datasets. It uses less +// memory than other hash maps and incrementally rehashes to reduce reallocation spikes. +// However, it is slower (by about 20%) than CUtlHashTable +// +//----------------------------------------------------------------------------- +template , typename H = MurmurHash3Functor > +class CUtlHashMapLarge : public 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 CompileTimeCheck + { + IsUtlMap = 1 + }; + + typedef K KeyType_t; + typedef T ElemType_t; + typedef int IndexType_t; + typedef L EqualityFunc_t; + typedef H HashFunc_t; + + CUtlHashMapLarge() + { + m_cElements = 0; + m_nMaxElement = 0; + m_nMinRehashedBucket = InvalidIndex(); + m_nMaxRehashedBucket = InvalidIndex(); + m_iNodeFreeListHead = InvalidIndex(); + } + + CUtlHashMapLarge( int cElementsExpected ) + { + m_cElements = 0; + m_nMaxElement = 0; + m_nMinRehashedBucket = InvalidIndex(); + m_nMaxRehashedBucket = InvalidIndex(); + m_iNodeFreeListHead = InvalidIndex(); + EnsureCapacity( cElementsExpected ); + } + + ~CUtlHashMapLarge() + { + RemoveAll(); + } + + // gets particular elements + ElemType_t & Element( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; } + const ElemType_t & Element( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; } + ElemType_t & operator[]( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; } + const ElemType_t & operator[]( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; } + KeyType_t & Key( IndexType_t i ) { return m_memNodes.Element( i ).m_key; } + const KeyType_t & Key( IndexType_t i ) const { return m_memNodes.Element( i ).m_key; } + + // Num elements + IndexType_t Count() const { return m_cElements; } + + // Max "size" of the vector + IndexType_t MaxElement() const { return m_nMaxElement; } + + // Checks if a node is valid and in the map + bool IsValidIndex( IndexType_t i ) const { return i >= 0 && i < m_nMaxElement && !IsFreeNodeID( m_memNodes[i].m_iNextNode ); } + + // Invalid index + static IndexType_t InvalidIndex() { return -1; } + + // Insert method + IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); } + IndexType_t Insert( const KeyType_t &key ) { return InsertInternal( key, ElemType_t(), eInsert_UpdateExisting ); } + IndexType_t InsertWithDupes( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_CreateDupes ); } + IndexType_t FindOrInsert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_LeaveExisting ); } + IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); } + + + // Finds an element + IndexType_t Find( const KeyType_t &key ) const; + + // has an element + bool HasElement( const KeyType_t &key ) const + { + return Find( key ) != InvalidIndex(); + } + + void EnsureCapacity( int num ); + + void RemoveAt( IndexType_t i ); + bool Remove( const KeyType_t &key ) + { + int iMap = Find( key ); + if ( iMap != InvalidIndex() ) + { + RemoveAt( iMap ); + return true; + } + return false; + } + void RemoveAll(); + void Purge(); + void PurgeAndDeleteElements(); + + void Swap( CUtlHashMapLarge &rhs ) + { + m_vecHashBuckets.Swap( rhs.m_vecHashBuckets ); + V_swap( m_bitsMigratedBuckets, rhs.m_bitsMigratedBuckets ); + m_memNodes.Swap( rhs.m_memNodes ); + V_swap( m_iNodeFreeListHead, rhs.m_iNodeFreeListHead ); + V_swap( m_cElements, rhs.m_cElements ); + V_swap( m_nMaxElement, rhs.m_nMaxElement ); + V_swap( m_nMinRehashedBucket, rhs.m_nMinRehashedBucket ); + V_swap( m_nMaxRehashedBucket, rhs.m_nMaxRehashedBucket ); + V_swap( m_EqualityFunc, rhs.m_EqualityFunc ); + V_swap( m_HashFunc, rhs.m_HashFunc ); + } + +private: + enum EInsertPolicy { eInsert_UpdateExisting, eInsert_LeaveExisting, eInsert_CreateDupes }; + IndexType_t InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy ); + + inline IndexType_t FreeNodeIDToIndex( IndexType_t i ) const { return (0-i)-3; } + inline IndexType_t FreeNodeIndexToID( IndexType_t i ) const { return (-3)-i; } + inline bool IsFreeNodeID( IndexType_t i ) const { return i < InvalidIndex(); } + + int FindInBucket( int iBucket, const KeyType_t &key ) const; + int AllocNode(); + void RehashNodesInBucket( int iBucket ); + void LinkNodeIntoBucket( int iBucket, int iNewNode ); + void UnlinkNodeFromBucket( int iBucket, int iNewNode ); + bool RemoveNodeFromBucket( int iBucket, int iNodeToRemove ); + void IncrementalRehash(); + + struct HashBucket_t + { + IndexType_t m_iNode; + }; + CUtlVector m_vecHashBuckets; + + CLargeVarBitVec m_bitsMigratedBuckets; + + struct Node_t + { + KeyType_t m_key; + ElemType_t m_elem; + int m_iNextNode; + }; + CUtlMemory m_memNodes; + IndexType_t m_iNodeFreeListHead; + + IndexType_t m_cElements; + IndexType_t m_nMaxElement; + IndexType_t m_nMinRehashedBucket, m_nMaxRehashedBucket; + EqualityFunc_t m_EqualityFunc; + HashFunc_t m_HashFunc; +}; + + +//----------------------------------------------------------------------------- +// Purpose: inserts an item into the map +//----------------------------------------------------------------------------- +template +inline int CUtlHashMapLarge::InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy ) +{ + // make sure we have room in the hash table + if ( m_cElements >= m_vecHashBuckets.Count() ) + EnsureCapacity( MAX( 16, m_vecHashBuckets.Count() * 2 ) ); + if ( m_cElements >= m_memNodes.Count() ) + m_memNodes.Grow( m_memNodes.Count() * 2 ); + + // rehash incrementally + IncrementalRehash(); + + // hash the item + uint32 hash = m_HashFunc( key ); + + // migrate data forward, if necessary + int cBucketsToModAgainst = m_vecHashBuckets.Count() >> 1; + int iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst); + while ( iBucket >= m_nMinRehashedBucket + && !m_bitsMigratedBuckets.Get( iBucket ) ) + { + RehashNodesInBucket( iBucket ); + cBucketsToModAgainst >>= 1; + iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst); + } + + // prevent duplicates if necessary + if ( ( ePolicy != eInsert_CreateDupes ) && m_cElements ) + { + // look in the bucket to see if we have a conflict + int iBucket2 = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() ); + IndexType_t iNode = FindInBucket( iBucket2, key ); + if ( iNode != InvalidIndex() ) + { + // a duplicate - update in place (matching CUtlMap) + if( ePolicy == eInsert_UpdateExisting ) + { + m_memNodes[iNode].m_elem = insert; + } + return iNode; + } + } + + // make an item + int iNewNode = AllocNode(); + m_memNodes[iNewNode].m_iNextNode = InvalidIndex(); + CopyConstruct( &m_memNodes[iNewNode].m_key, key ); + CopyConstruct( &m_memNodes[iNewNode].m_elem, insert ); + + iBucket = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() ); + + // link ourselves in + // ::OutputDebugStr( CFmtStr( "insert %d into bucket %d\n", key, iBucket ).Access() ); + LinkNodeIntoBucket( iBucket, iNewNode ); + + // return the new node + return iNewNode; +} + +//----------------------------------------------------------------------------- +// Purpose: grows the map to fit the specified amount +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::EnsureCapacity( int amount ) +{ + m_memNodes.EnsureCapacity( amount ); + // ::OutputDebugStr( CFmtStr( "grown m_memNodes from %d to %d\n", m_cElements, m_memNodes.Count() ).Access() ); + + if ( amount <= m_vecHashBuckets.Count() ) + return; + int cBucketsNeeded = MAX( 16, m_vecHashBuckets.Count() ); + while ( cBucketsNeeded < amount ) + cBucketsNeeded *= 2; + + // ::OutputDebugStr( CFmtStr( "grown m_vecHashBuckets from %d to %d\n", m_vecHashBuckets.Count(), cBucketsNeeded ).Access() ); + + // grow the hash buckets + int grow = cBucketsNeeded - m_vecHashBuckets.Count(); + int iFirst = m_vecHashBuckets.AddMultipleToTail( grow ); + // clear all the new data to invalid bits + memset( &m_vecHashBuckets[iFirst], 0xFFFFFFFF, grow*sizeof(m_vecHashBuckets[iFirst]) ); + Assert( basetypes::IsPowerOf2( m_vecHashBuckets.Count() ) ); + + // we'll have to rehash, all the buckets that existed before growth + m_nMinRehashedBucket = 0; + m_nMaxRehashedBucket = iFirst; + if ( m_cElements > 0 ) + { + // remove all the current bits + m_bitsMigratedBuckets.Resize( 0 ); + // re-add new bits; these will all be reset to 0 + m_bitsMigratedBuckets.Resize( m_vecHashBuckets.Count() ); + } + else + { + // no elements - no rehashing + m_nMinRehashedBucket = m_vecHashBuckets.Count(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: gets a new node, from the free list if possible +//----------------------------------------------------------------------------- +template +inline int CUtlHashMapLarge::AllocNode() +{ + // if we're out of free elements, get the max + if ( m_cElements == m_nMaxElement ) + { + m_cElements++; + return m_nMaxElement++; + } + + // pull from the free list + Assert( m_iNodeFreeListHead != InvalidIndex() ); + int iNewNode = m_iNodeFreeListHead; + m_iNodeFreeListHead = FreeNodeIDToIndex( m_memNodes[iNewNode].m_iNextNode ); + m_cElements++; + return iNewNode; +} + + +//----------------------------------------------------------------------------- +// Purpose: takes a bucket of nodes and re-hashes them into a more optimal bucket +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::RehashNodesInBucket( int iBucketSrc ) +{ + // mark us as migrated + m_bitsMigratedBuckets.Set( iBucketSrc ); + + // walk the list of items, re-hashing them + IndexType_t iNode = m_vecHashBuckets[iBucketSrc].m_iNode; + while ( iNode != InvalidIndex() ) + { + IndexType_t iNodeNext = m_memNodes[iNode].m_iNextNode; + Assert( iNodeNext != iNode ); + + // work out where the node should go + const KeyType_t &key = m_memNodes[iNode].m_key; + uint32 hash = m_HashFunc( key ); + int iBucketDest = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() ); + + // if the hash bucket has changed, move it + if ( iBucketDest != iBucketSrc ) + { + // ::OutputDebugStr( CFmtStr( "moved key %d from bucket %d to %d\n", key, iBucketSrc, iBucketDest ).Access() ); + + // remove from this bucket list + UnlinkNodeFromBucket( iBucketSrc, iNode ); + + // link into new bucket list + LinkNodeIntoBucket( iBucketDest, iNode ); + } + iNode = iNodeNext; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: searches for an item by key, returning the index handle +//----------------------------------------------------------------------------- +template +inline int CUtlHashMapLarge::Find( const KeyType_t &key ) const +{ + if ( m_cElements == 0 ) + return InvalidIndex(); + + // hash the item + uint32 hash = m_HashFunc( key ); + + // find the bucket + int cBucketsToModAgainst = m_vecHashBuckets.Count(); + int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst ); + + // look in the bucket for the item + int iNode = FindInBucket( iBucket, key ); + if ( iNode != InvalidIndex() ) + return iNode; + + // not found? we may have to look in older buckets + cBucketsToModAgainst >>= 1; + while ( cBucketsToModAgainst >= m_nMinRehashedBucket ) + { + iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst ); + + if ( !m_bitsMigratedBuckets.Get( iBucket ) ) + { + int iNode2 = FindInBucket( iBucket, key ); + if ( iNode2 != InvalidIndex() ) + return iNode2; + } + + cBucketsToModAgainst >>= 1; + } + + return InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Purpose: searches for an item by key, returning the index handle +//----------------------------------------------------------------------------- +template +inline int CUtlHashMapLarge::FindInBucket( int iBucket, const KeyType_t &key ) const +{ + if ( m_vecHashBuckets[iBucket].m_iNode != InvalidIndex() ) + { + IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode; + Assert( iNode < m_nMaxElement ); + while ( iNode != InvalidIndex() ) + { + // equality check + if ( m_EqualityFunc( key, m_memNodes[iNode].m_key ) ) + return iNode; + + iNode = m_memNodes[iNode].m_iNextNode; + } + } + + return InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Purpose: links a node into a bucket +//----------------------------------------------------------------------------- +template +void CUtlHashMapLarge::LinkNodeIntoBucket( int iBucket, int iNewNode ) +{ + // add into the start of the bucket's list + m_memNodes[iNewNode].m_iNextNode = m_vecHashBuckets[iBucket].m_iNode; + m_vecHashBuckets[iBucket].m_iNode = iNewNode; +} + + +//----------------------------------------------------------------------------- +// Purpose: unlinks a node from the bucket +//----------------------------------------------------------------------------- +template +void CUtlHashMapLarge::UnlinkNodeFromBucket( int iBucket, int iNodeToUnlink ) +{ + int iNodeNext = m_memNodes[iNodeToUnlink].m_iNextNode; + + // if it's the first node, just update the bucket to point to the new place + int iNode = m_vecHashBuckets[iBucket].m_iNode; + if ( iNode == iNodeToUnlink ) + { + m_vecHashBuckets[iBucket].m_iNode = iNodeNext; + return; + } + + // walk the list to find where + while ( iNode != InvalidIndex() ) + { + if ( m_memNodes[iNode].m_iNextNode == iNodeToUnlink ) + { + m_memNodes[iNode].m_iNextNode = iNodeNext; + return; + } + iNode = m_memNodes[iNode].m_iNextNode; + } + + // should always be valid to unlink + Assert( false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: removes a single item from the map +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::RemoveAt( IndexType_t i ) +{ + if ( !IsValidIndex( i ) ) + { + Assert( false ); + return; + } + + // unfortunately, we have to re-hash to find which bucket we're in + uint32 hash = m_HashFunc( m_memNodes[i].m_key ); + int cBucketsToModAgainst = m_vecHashBuckets.Count(); + int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst ); + if ( RemoveNodeFromBucket( iBucket, i ) ) + return; + + // wasn't found; look in older buckets + cBucketsToModAgainst >>= 1; + while ( cBucketsToModAgainst >= m_nMinRehashedBucket ) + { + iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst ); + + if ( !m_bitsMigratedBuckets.Get( iBucket ) ) + { + if ( RemoveNodeFromBucket( iBucket, i ) ) + return; + } + + cBucketsToModAgainst >>= 1; + } + + // never found, container is busted + Assert( false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: removes a node from the bucket, return true if it was found +//----------------------------------------------------------------------------- +template +inline bool CUtlHashMapLarge::RemoveNodeFromBucket( IndexType_t iBucket, int iNodeToRemove ) +{ + IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode; + while ( iNode != InvalidIndex() ) + { + if ( iNodeToRemove == iNode ) + { + // found it, remove + UnlinkNodeFromBucket( iBucket, iNodeToRemove ); + Destruct( &m_memNodes[iNode].m_key ); + Destruct( &m_memNodes[iNode].m_elem ); + + // link into free list + m_memNodes[iNode].m_iNextNode = FreeNodeIndexToID( m_iNodeFreeListHead ); + m_iNodeFreeListHead = iNode; + m_cElements--; + if ( m_cElements == 0 ) + { + m_nMinRehashedBucket = m_vecHashBuckets.Count(); + } + return true; + } + + iNode = m_memNodes[iNode].m_iNextNode; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: removes all items from the hash map +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::RemoveAll() +{ + FOR_EACH_MAP_FAST( *this, i ) + { + Destruct( &m_memNodes[i].m_key ); + Destruct( &m_memNodes[i].m_elem ); + } + + m_cElements = 0; + m_nMaxElement = 0; + m_iNodeFreeListHead = InvalidIndex(); + m_nMinRehashedBucket = m_vecHashBuckets.Count(); + m_nMaxRehashedBucket = InvalidIndex(); + m_bitsMigratedBuckets.Resize( 0 ); + memset( m_vecHashBuckets.Base(), 0xFF, m_vecHashBuckets.Count() * sizeof(HashBucket_t) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: removes all items from the hash map and releases memory +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::Purge() +{ + FOR_EACH_MAP_FAST( *this, i ) + { + Destruct( &m_memNodes[i].m_key ); + Destruct( &m_memNodes[i].m_elem ); + } + + m_cElements = 0; + m_nMaxElement = 0; + m_iNodeFreeListHead = InvalidIndex(); + m_nMinRehashedBucket = InvalidIndex(); + m_nMaxRehashedBucket = InvalidIndex(); + + m_bitsMigratedBuckets.Resize( 0 ); + m_memNodes.Purge(); + m_vecHashBuckets.Purge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: removes and deletes all items from the hash map and releases memory +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::PurgeAndDeleteElements() +{ + FOR_EACH_MAP_FAST( *this, i ) + { + delete this->Element( i ); + } + + Purge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: rehashes buckets +//----------------------------------------------------------------------------- +template +inline void CUtlHashMapLarge::IncrementalRehash() +{ + if ( m_nMinRehashedBucket < m_nMaxRehashedBucket ) + { + while ( m_nMinRehashedBucket < m_nMaxRehashedBucket ) + { + // see if the bucket needs rehashing + if ( m_vecHashBuckets[m_nMinRehashedBucket].m_iNode != InvalidIndex() + && !m_bitsMigratedBuckets.Get(m_nMinRehashedBucket) ) + { + // rehash this bucket + RehashNodesInBucket( m_nMinRehashedBucket ); + // only actively do one - don't want to do it too fast since we may be on a rapid growth path + ++m_nMinRehashedBucket; + break; + } + + // nothing to rehash in that bucket - increment and look again + ++m_nMinRehashedBucket; + } + + if ( m_nMinRehashedBucket >= m_nMaxRehashedBucket ) + { + // we're done; don't need any bits anymore + m_nMinRehashedBucket = m_vecHashBuckets.Count(); + m_nMaxRehashedBucket = InvalidIndex(); + m_bitsMigratedBuckets.Resize( 0 ); + } + } +} + + +#endif // UTLHASHMAPLARGE_H diff --git a/public/tier1/utlmap.h b/public/tier1/utlmap.h index 6f933c81b..34e3c6fdf 100644 --- a/public/tier1/utlmap.h +++ b/public/tier1/utlmap.h @@ -24,33 +24,41 @@ // 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(); iteratorName != mapName.InvalidIndex(); iteratorName = mapName.NextInorder( 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; iteratorName < mapName.MaxElement(); ++iteratorName ) if ( !mapName.IsValidIndex( iteratorName ) ) continue; else + for ( int iteratorName = 0; (mapName).IsUtlMap && iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else -template -class CUtlMap + +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; - // Less func typedef - // Returns true if the first parameter is "less" than the second - typedef bool (*LessFunc_t)( const KeyType_t &, const KeyType_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, LessFunc_t lessfunc = 0 ) - : m_Tree( growSize, initSize, CKeyLess( lessfunc ) ) + 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 ) ) { @@ -104,6 +112,34 @@ class CUtlMap return m_Tree.Insert( node ); } + // API to macth src2 for Panormama + // Note in src2 straight Insert() calls will assert on duplicates + // Chosing 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 { @@ -111,6 +147,33 @@ class CUtlMap 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 ); } @@ -123,13 +186,27 @@ class CUtlMap 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 ) @@ -175,7 +252,7 @@ class CUtlMap class CKeyLess { public: - CKeyLess( LessFunc_t lessFunc ) : m_LessFunc(lessFunc) {} + CKeyLess( const LessFunc_t& lessFunc ) : m_LessFunc(lessFunc) {} bool operator!() const { @@ -200,4 +277,19 @@ class CUtlMap //----------------------------------------------------------------------------- +// 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/public/tier1/utlrbtree.h b/public/tier1/utlrbtree.h index 5b55004bc..a10fb6022 100644 --- a/public/tier1/utlrbtree.h +++ b/public/tier1/utlrbtree.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -16,7 +16,7 @@ // This is a useful macro to iterate from start to end in order in a map #define FOR_EACH_UTLRBTREE( treeName, iteratorName ) \ - for ( int iteratorName = treeName.FirstInorder(); iteratorName != treeName.InvalidIndex(); iteratorName = treeName.NextInorder( iteratorName ) ) + for ( int iteratorName = treeName.FirstInorder(); (treeName).IsUtlRBTree && iteratorName != treeName.InvalidIndex(); iteratorName = treeName.NextInorder( iteratorName ) ) //----------------------------------------------------------------------------- @@ -35,8 +35,30 @@ class CDefOps //------------------------------------- -inline bool StringLessThan( const char * const &lhs, const char * const &rhs) { return ( strcmp( lhs, rhs) < 0 ); } -inline bool CaselessStringLessThan( const char * const &lhs, const char * const &rhs ) { return ( stricmp( lhs, rhs) < 0 ); } +template +class CDefLess +{ +public: + CDefLess() {} + CDefLess( int i ) {} + inline bool operator()( const T &lhs, const T &rhs ) const { return ( lhs < rhs ); } + inline bool operator!() const { return false; } +}; + + +//------------------------------------- + +inline bool StringLessThan( const char * const &lhs, const char * const &rhs) { + if ( !lhs ) return false; + if ( !rhs ) return true; + return ( strcmp( lhs, rhs) < 0 ); +} + +inline bool CaselessStringLessThan( const char * const &lhs, const char * const &rhs ) { + if ( !lhs ) return false; + if ( !rhs ) return true; + return ( stricmp( lhs, rhs) < 0 ); +} // Same as CaselessStringLessThan, but it ignores differences in / and \. @@ -95,6 +117,17 @@ void SetDefLessFunc( RBTREE_T &RBTree ) RBTree.SetLessFunc( DefLessFunc( typename RBTREE_T::KeyType_t ) ); } +// For use with FindClosest +// Move these to a common area if anyone else ever uses them +enum CompareOperands_t +{ + k_EEqual = 0x1, + k_EGreaterThan = 0x2, + k_ELessThan = 0x4, + k_EGreaterThanOrEqualTo = k_EGreaterThan | k_EEqual, + k_ELessThanOrEqualTo = k_ELessThan | k_EEqual, +}; + //----------------------------------------------------------------------------- // A red-black binary search tree //----------------------------------------------------------------------------- @@ -122,6 +155,7 @@ class CUtlRBTree typedef T KeyType_t; typedef T ElemType_t; typedef I IndexType_t; + enum { IsUtlRBTree = true }; // Used to match this at compiletime // Less func typedef // Returns true if the first parameter is "less" than the second @@ -131,8 +165,8 @@ class CUtlRBTree // 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 - CUtlRBTree( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ); - CUtlRBTree( const LessFunc_t &lessfunc ); + explicit CUtlRBTree( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ); + explicit CUtlRBTree( const LessFunc_t &lessfunc ); ~CUtlRBTree( ); void EnsureCapacity( int num ); @@ -152,9 +186,9 @@ class CUtlRBTree unsigned int Count() const; // Max "size" of the vector - // it's not generally safe to iterate from index 0 to MaxElement()-1 - // it IS safe to do so when using CUtlMemory as the allocator, - // but we should really remove patterns using this anyways, for safety and generality + // it's not generally safe to iterate from index 0 to MaxElement()-1 (you could do this as a potential + // iteration optimization, IF CUtlMemory is the allocator, and IF IsValidIndex() is tested for each element... + // but this should be implemented inside the CUtlRBTree iteration API, if anywhere) I MaxElement() const; // Gets the children @@ -190,6 +224,8 @@ class CUtlRBTree I NewNode(); // Insert method (inserts in order) + // NOTE: the returned 'index' will be valid as long as the element remains in the tree + // (other elements being added/removed will not affect it) I Insert( T const &insert ); void Insert( const T *pArray, int nItems ); I InsertIfNotFound( T const &insert ); @@ -197,6 +233,12 @@ class CUtlRBTree // Find method I Find( T const &search ) const; + // FindFirst method ( finds first inorder if there are duplicates ) + I FindFirst( T const &search ) const; + + // First element >= key + I FindClosest( T const &search, CompareOperands_t eFindCriteria ) const; + // Remove methods void RemoveAt( I i ); bool Remove( T const &remove ); @@ -372,8 +414,8 @@ m_LastAlloc( m_Elements.InvalidIterator() ) template < class T, class I, typename L, class M > inline CUtlRBTree::CUtlRBTree( const LessFunc_t &lessfunc ) : -m_LessFunc( lessfunc ), m_Elements( 0, 0 ), +m_LessFunc( lessfunc ), m_Root( InvalidIndex() ), m_NumElements( 0 ), m_FirstFree( InvalidIndex() ), @@ -414,13 +456,15 @@ inline void CUtlRBTree::CopyFrom( const CUtlRBTree &othe template < class T, class I, typename L, class M > inline T &CUtlRBTree::Element( I i ) -{ +{ + Assert( IsValidIndex( i ) ); return m_Elements[i].m_Data; } template < class T, class I, typename L, class M > inline T const &CUtlRBTree::Element( I i ) const { + Assert( IsValidIndex( i ) ); return m_Elements[i].m_Data; } @@ -480,19 +524,19 @@ inline I CUtlRBTree::MaxElement() const template < class T, class I, typename L, class M > inline I CUtlRBTree::Parent( I i ) const { - return Links(i).m_Parent; + return i != InvalidIndex() ? m_Elements[i].m_Parent : InvalidIndex(); } template < class T, class I, typename L, class M > inline I CUtlRBTree::LeftChild( I i ) const { - return Links(i).m_Left; + return i != InvalidIndex() ? m_Elements[i].m_Left : InvalidIndex(); } template < class T, class I, typename L, class M > inline I CUtlRBTree::RightChild( I i ) const { - return Links(i).m_Right; + return i != InvalidIndex() ? m_Elements[i].m_Right : InvalidIndex(); } //----------------------------------------------------------------------------- @@ -597,19 +641,22 @@ template < class T, class I, typename L, class M > inline typename CUtlRBTree::Links_t const &CUtlRBTree::Links( I i ) const { // Sentinel node, makes life easier - static Links_t s_Sentinel = + static const Links_t s_Sentinel = { - InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree::BLACK + // Use M::INVALID_INDEX instead of InvalidIndex() so that this is + // a compile-time constant -- otherwise it is constructed on the first + // call! + M::INVALID_INDEX, M::INVALID_INDEX, M::INVALID_INDEX, CUtlRBTree::BLACK }; - return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : *(Links_t*)&s_Sentinel; + return (i != InvalidIndex()) ? m_Elements[i] : s_Sentinel; } template < class T, class I, typename L, class M > inline typename CUtlRBTree::Links_t &CUtlRBTree::Links( I i ) { Assert(i != InvalidIndex()); - return *(Links_t *)&m_Elements[i]; + return m_Elements[i]; } //----------------------------------------------------------------------------- @@ -619,13 +666,13 @@ inline typename CUtlRBTree::Links_t &CUtlRBTree::Links( template < class T, class I, typename L, class M > inline bool CUtlRBTree::IsRed( I i ) const { - return (Links(i).m_Tag == RED); + return Color( i ) == RED; } template < class T, class I, typename L, class M > inline bool CUtlRBTree::IsBlack( I i ) const { - return (Links(i).m_Tag == BLACK); + return Color( i ) == BLACK; } @@ -636,7 +683,7 @@ inline bool CUtlRBTree::IsBlack( I i ) const template < class T, class I, typename L, class M > inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color( I i ) const { - return (NodeColor_t)Links(i).m_Tag; + return (NodeColor_t)(i != InvalidIndex() ? m_Elements[i].m_Tag : BLACK); } template < class T, class I, typename L, class M > @@ -682,7 +729,7 @@ I CUtlRBTree::NewNode() else { elem = m_FirstFree; - m_FirstFree = Links( m_FirstFree ).m_Right; + m_FirstFree = RightChild( m_FirstFree ); } #ifdef _DEBUG @@ -1056,8 +1103,8 @@ void CUtlRBTree::Link( I elem ) { if ( elem != InvalidIndex() ) { - I parent; - bool leftchild; + I parent = InvalidIndex(); + bool leftchild = false; FindInsertionPosition( Element( elem ), parent, leftchild ); @@ -1165,8 +1212,9 @@ template < class T, class I, typename L, class M > I CUtlRBTree::FirstInorder() const { I i = m_Root; - while (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); + I left; + while ((left = LeftChild(i)) != InvalidIndex()) + i = left; return i; } @@ -1175,11 +1223,13 @@ I CUtlRBTree::NextInorder( I i ) const { Assert(IsValidIndex(i)); - if (RightChild(i) != InvalidIndex()) + I right; + if ((right = RightChild(i)) != InvalidIndex()) { - i = RightChild(i); - while (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); + i = right; + I left; + while ((left = LeftChild(i)) != InvalidIndex()) + i = left; return i; } @@ -1198,11 +1248,12 @@ I CUtlRBTree::PrevInorder( I i ) const { Assert(IsValidIndex(i)); - if (LeftChild(i) != InvalidIndex()) + I left, right; + if ((left = LeftChild(i)) != InvalidIndex()) { - i = LeftChild(i); - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); + i = left; + while ((right = RightChild(i)) != InvalidIndex()) + i = right; return i; } @@ -1220,8 +1271,9 @@ template < class T, class I, typename L, class M > I CUtlRBTree::LastInorder() const { I i = m_Root; - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); + I right; + while ((right = RightChild(i)) != InvalidIndex()) + i = right; return i; } @@ -1234,11 +1286,12 @@ I CUtlRBTree::FirstPreorder() const template < class T, class I, typename L, class M > I CUtlRBTree::NextPreorder( I i ) const { - if (LeftChild(i) != InvalidIndex()) - return LeftChild(i); + I left, right; + if ((left = LeftChild(i)) != InvalidIndex()) + return left; - if (RightChild(i) != InvalidIndex()) - return RightChild(i); + if ((right = RightChild(i)) != InvalidIndex()) + return right; I parent = Parent(i); while( parent != InvalidIndex()) @@ -1264,11 +1317,12 @@ I CUtlRBTree::LastPreorder() const I i = m_Root; while (1) { - while (RightChild(i) != InvalidIndex()) - i = RightChild(i); + I left, right; + while ((right = RightChild(i)) != InvalidIndex()) + i = right; - if (LeftChild(i) != InvalidIndex()) - i = LeftChild(i); + if ((left = LeftChild(i)) != InvalidIndex()) + i = left; else break; } @@ -1281,8 +1335,9 @@ I CUtlRBTree::FirstPostorder() const I i = m_Root; while (!IsLeaf(i)) { - if (LeftChild(i)) - i = LeftChild(i); + I left; + if ((left = LeftChild(i)) != InvalidIndex()) + i = left; else i = RightChild(i); } @@ -1305,8 +1360,9 @@ I CUtlRBTree::NextPostorder( I i ) const i = RightChild(parent); while (!IsLeaf(i)) { - if (LeftChild(i)) - i = LeftChild(i); + I left; + if ((left = LeftChild(i)) != InvalidIndex()) + i = left; else i = RightChild(i); } @@ -1454,7 +1510,7 @@ void CUtlRBTree::SetLessFunc( const typename CUtlRBTree: template < class T, class I, typename L, class M > void CUtlRBTree::FindInsertionPosition( T const &insert, I &parent, bool &leftchild ) { - Assert( m_LessFunc ); + Assert( !!m_LessFunc ); /* find where node belongs */ I current = m_Root; @@ -1478,8 +1534,8 @@ template < class T, class I, typename L, class M > I CUtlRBTree::Insert( T const &insert ) { // use copy constructor to copy it in - I parent; - bool leftchild; + I parent = InvalidIndex(); + bool leftchild = false; FindInsertionPosition( insert, parent, leftchild ); I newNode = InsertAt( parent, leftchild ); CopyConstruct( &Element( newNode ), insert ); @@ -1535,7 +1591,7 @@ I CUtlRBTree::InsertIfNotFound( T const &insert ) template < class T, class I, typename L, class M > I CUtlRBTree::Find( T const &search ) const { - Assert( m_LessFunc ); + Assert( !!m_LessFunc ); I current = m_Root; while (current != InvalidIndex()) @@ -1551,6 +1607,82 @@ I CUtlRBTree::Find( T const &search ) const } +//----------------------------------------------------------------------------- +// finds a the first node (inorder) with this key in the tree +//----------------------------------------------------------------------------- +template +I CUtlRBTree::FindFirst( T const &search ) const +{ + Assert( !!m_LessFunc ); + + I current = m_Root; + I best = InvalidIndex(); + while ( current != InvalidIndex() ) + { + if ( m_LessFunc( search, Element( current ) ) ) + current = LeftChild( current ); + else if ( m_LessFunc( Element( current ), search ) ) + current = RightChild( current ); + else + { + best = current; + current = LeftChild( current ); + } + } + return best; +} + + +//----------------------------------------------------------------------------- +// finds the closest node to the key supplied +//----------------------------------------------------------------------------- +template +I CUtlRBTree::FindClosest( T const &search, CompareOperands_t eFindCriteria ) const +{ + Assert( !!m_LessFunc ); + Assert( ( eFindCriteria & ( k_EGreaterThan | k_ELessThan ) ) ^ ( k_EGreaterThan | k_ELessThan ) ); + + I current = m_Root; + I best = InvalidIndex(); + + while ( current != InvalidIndex() ) + { + if ( m_LessFunc( search, Element( current ) ) ) + { + // current node is > key + if ( eFindCriteria & k_EGreaterThan ) + best = current; + current = LeftChild( current ); + } + else if ( m_LessFunc( Element( current ), search ) ) + { + // current node is < key + if ( eFindCriteria & k_ELessThan ) + best = current; + current = RightChild( current ); + } + else + { + // exact match + if ( eFindCriteria & k_EEqual ) + { + best = current; + break; + } + else if ( eFindCriteria & k_EGreaterThan ) + { + current = RightChild( current ); + } + else if ( eFindCriteria & k_ELessThan ) + { + current = LeftChild( current ); + } + } + } + return best; +} + + //----------------------------------------------------------------------------- // swap in place //----------------------------------------------------------------------------- diff --git a/tier1/murmurhash3.cpp b/tier1/murmurhash3.cpp new file mode 100644 index 000000000..056f2c331 --- /dev/null +++ b/tier1/murmurhash3.cpp @@ -0,0 +1,74 @@ +//======= Copyright © Valve Corporation, All rights reserved. ================= +// +// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose +// hash with a 32-bit output. References: +// http://code.google.com/p/smhasher/ (home of MurmurHash3) +// https://sites.google.com/site/murmurhash/avalanche +// http://www.strchr.com/hash_functions +// +//============================================================================= + +#include "platform.h" +#include "murmurhash3.h" + +//----------------------------------------------------------------------------- + +uint32 MurmurHash3_32( const void * key, size_t len, uint32 seed, bool bCaselessStringVariant ) +{ + const uint8 * data = (const uint8*)key; + const ptrdiff_t nblocks = len / 4; + uint32 uSourceBitwiseAndMask = 0xDFDFDFDF | ((uint32)bCaselessStringVariant - 1); + + uint32 h1 = seed; + + //---------- + // body + + const uint32 * blocks = (const uint32 *)(data + nblocks*4); + + for(ptrdiff_t i = -nblocks; i; i++) + { + uint32 k1 = LittleDWord(blocks[i]); + k1 &= uSourceBitwiseAndMask; + + k1 *= 0xcc9e2d51; + k1 = (k1 << 15) | (k1 >> 17); + k1 *= 0x1b873593; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >> 19); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8 * tail = (const uint8*)(data + nblocks*4); + + uint32 k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 &= uSourceBitwiseAndMask; + k1 *= 0xcc9e2d51; + k1 = (k1 << 15) | (k1 >> 17); + k1 *= 0x1b873593; + h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + return h1; +} diff --git a/tier1/tier1.vcxproj b/tier1/tier1.vcxproj index 8dcde3b34..02fcd0642 100644 --- a/tier1/tier1.vcxproj +++ b/tier1/tier1.vcxproj @@ -18,9 +18,11 @@ StaticLibrary + v142 StaticLibrary + v142 @@ -130,6 +132,7 @@ + diff --git a/tier1/tier1.vcxproj.filters b/tier1/tier1.vcxproj.filters index 4668aac21..71cfc83fc 100644 --- a/tier1/tier1.vcxproj.filters +++ b/tier1/tier1.vcxproj.filters @@ -89,6 +89,9 @@ Source Files + + Source Files + diff --git a/tier1/tier1.vcxproj.user b/tier1/tier1.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/tier1/tier1.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file