Major refactor of utl* classes (#164)

Changed/Added classes: CThreadMutex, CUtlMemoryBlockAllocator, CRawAllocator, CUtlHashtable, CUtlVector, CUtlMemory, CUtlMemory_RawAllocator, CUtlSymbolLarge, CUtlSymbolTableLargeBase
This commit is contained in:
vanz666 2023-10-13 20:16:09 +03:00 committed by GitHub
parent dfa2103b28
commit c0def21c93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 933 additions and 569 deletions

View File

@ -69,10 +69,7 @@ struct GameEventKeySymbol_t
if (!keyName || !keyName[0])
return;
CUtlString buf( keyName );
buf.ToLowerFast();
m_nHashCode = MurmurHash2(buf.Get(), strlen(keyName), 0x31415926);
m_nHashCode = MurmurHash2LowerCase(keyName, strlen(keyName), 0x31415926);
m_pszKeyName = keyName;
#if 0

View File

@ -65,6 +65,8 @@ struct _CrtMemState;
typedef size_t (*MemAllocFailHandler_t)( size_t );
PLATFORM_INTERFACE void CMemAllocSystemInitialize();
//-----------------------------------------------------------------------------
// NOTE! This should never be called directly from leaf code
// Just use new,delete,malloc,free etc. They will call into this eventually

View File

@ -332,7 +332,7 @@ typedef unsigned int uint;
#ifndef UINT32_MAX
#define UINT32_MAX ((uint32)~0)
#endif
#ifndef UINT16_MAX
#ifndef UINT64_MAX
#define UINT64_MAX ((uint64)~0)
#endif
@ -547,6 +547,13 @@ typedef unsigned int uint;
#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)(uintp)( p ) )
// 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.
//
@ -1335,6 +1342,24 @@ inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5 )
return ::new( pMemory ) T( a1, a2, a3, a4, a5 );
}
template <class T, class P>
inline void ConstructOneArg( T* pMemory, P const& arg)
{
::new( pMemory ) T(arg);
}
template <class T, class P1, class P2 >
inline void ConstructTwoArg( T* pMemory, P1 const& arg1, P2 const& arg2)
{
::new( pMemory ) T(arg1, arg2);
}
template <class T, class P1, class P2, class P3 >
inline void ConstructThreeArg( T* pMemory, P1 const& arg1, P2 const& arg2, P3 const& arg3)
{
::new( pMemory ) T(arg1, arg2, arg3);
}
template <class T>
inline T* CopyConstruct( T* pMemory, T const& src )
{

View File

@ -67,11 +67,7 @@ typedef void *HANDLE;
const unsigned TT_INFINITE = 0xffffffff;
#ifdef PLATFORM_64BITS
typedef uint64 ThreadId_t;
#else
typedef uint32 ThreadId_t;
#endif
typedef unsigned long ThreadId_t;
//-----------------------------------------------------------------------------
//
@ -102,7 +98,7 @@ typedef int (*ThreadedLoadLibraryFunc_t)();
PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func );
PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc();
#if defined( PLATFORM_WINDOWS_PC32 )
#if defined( _WIN32 )
DLL_IMPORT unsigned long STDCALL GetCurrentThreadId();
#define ThreadGetCurrentId GetCurrentThreadId
#endif
@ -595,22 +591,22 @@ private:
class PLATFORM_CLASS CThreadMutex
{
public:
CThreadMutex();
CThreadMutex( const char* pDebugName );
~CThreadMutex();
//------------------------------------------------------
// Mutex acquisition/release. Const intentionally defeated.
//------------------------------------------------------
void Lock();
void Lock() const { (const_cast<CThreadMutex *>(this))->Lock(); }
void Unlock();
void Unlock() const { (const_cast<CThreadMutex *>(this))->Unlock(); }
void Lock( const char *pFileName, int nLine );
void Lock( const char *pFileName, int nLine ) const { (const_cast<CThreadMutex *>(this))->Lock( pFileName, nLine ); }
void Unlock( const char *pFileName, int nLine );
void Unlock( const char *pFileName, int nLine ) const { (const_cast<CThreadMutex *>(this))->Unlock( pFileName, nLine ); }
bool TryLock();
bool TryLock() const { return (const_cast<CThreadMutex *>(this))->TryLock(); }
bool TryLock( const char *pFileName, int nLine );
bool TryLock( const char *pFileName, int nLine ) const { return (const_cast<CThreadMutex *>(this))->TryLock( pFileName, nLine ); }
void LockSilent(); // A Lock() operation which never spews. Required by the logging system to prevent badness.
void UnlockSilent(); // An Unlock() operation which never spews. Required by the logging system to prevent badness.
void LockSilent( const char *pFileName, int nLine ); // A Lock() operation which never spews. Required by the logging system to prevent badness.
void UnlockSilent( const char *pFileName, int nLine ); // An Unlock() operation which never spews. Required by the logging system to prevent badness.
//------------------------------------------------------
// Use this to make deadlocks easier to track by asserting
@ -649,12 +645,25 @@ private:
#ifdef THREAD_MUTEX_TRACING_SUPPORTED
// Debugging (always herge to allow mixed debug/release builds w/o changing size)
uint m_currentOwnerID;
uint16 m_lockCount;
bool m_bTrace;
ThreadId_t m_currentOwnerID;
uint16 m_lockCount;
bool m_bTrace;
const char* m_pDebugName;
#endif
};
class CThreadEmptyMutex
{
public:
CThreadEmptyMutex( const char* pDebugName ) { }
~CThreadEmptyMutex() { }
inline void Lock( const char *pFileName, int nLine ) { }
inline void Lock( const char *pFileName, int nLine ) const { }
inline void Unlock( const char *pFileName, int nLine ) { }
inline void Unlock( const char *pFileName, int nLine ) const { }
};
//-----------------------------------------------------------------------------
//
// An alternative mutex that is useful for cases when thread contention is
@ -1490,55 +1499,57 @@ extern "C"
//---------------------------------------------------------
inline void CThreadMutex::Lock()
inline void CThreadMutex::Lock( const char *pFileName, int nLine )
{
ThreadId_t thisThreadID = ThreadGetCurrentId();
#ifdef THREAD_MUTEX_TRACING_ENABLED
uint thisThreadID = ThreadGetCurrentId();
if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) )
Msg( _T( "Thread %u about to wait for lock %x owned by %u\n" ), ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID );
#endif
LockSilent();
LockSilent( pFileName, nLine );
#ifdef THREAD_MUTEX_TRACING_ENABLED
if (m_lockCount == 0)
{
// we now own it for the first time. Set owner information
m_currentOwnerID = thisThreadID;
#ifdef THREAD_MUTEX_TRACING_ENABLED
if ( m_bTrace )
Msg( _T( "Thread %u now owns lock 0x%x\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
#endif
}
m_lockCount++;
#endif
}
//---------------------------------------------------------
inline void CThreadMutex::Unlock()
inline void CThreadMutex::Unlock( const char *pFileName, int nLine )
{
#ifdef THREAD_MUTEX_TRACING_ENABLED
AssertMsg( m_lockCount >= 1, "Invalid unlock of thread lock" );
#endif
m_lockCount--;
if (m_lockCount == 0)
{
#ifdef THREAD_MUTEX_TRACING_ENABLED
if ( m_bTrace )
Msg( _T( "Thread %u releasing lock 0x%x\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
#endif
m_currentOwnerID = 0;
}
#endif
UnlockSilent();
UnlockSilent( pFileName, nLine );
}
//---------------------------------------------------------
inline void CThreadMutex::LockSilent()
inline void CThreadMutex::LockSilent( const char *pFileName, int nLine )
{
EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection);
}
//---------------------------------------------------------
inline void CThreadMutex::UnlockSilent()
inline void CThreadMutex::UnlockSilent( const char *pFileName, int nLine )
{
LeaveCriticalSection((CRITICAL_SECTION *)&m_CriticalSection);
}
@ -1547,14 +1558,12 @@ inline void CThreadMutex::UnlockSilent()
inline bool CThreadMutex::AssertOwnedByCurrentThread()
{
#ifdef THREAD_MUTEX_TRACING_ENABLED
if (ThreadGetCurrentId() == m_currentOwnerID)
return true;
#ifdef THREAD_MUTEX_TRACING_ENABLED
AssertMsg3( 0, "Expected thread %u as owner of lock 0x%x, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID );
return false;
#else
return true;
#endif
return false;
}
//---------------------------------------------------------
@ -1570,7 +1579,8 @@ inline void CThreadMutex::SetTrace( bool bTrace )
#elif defined(POSIX)
inline CThreadMutex::CThreadMutex()
inline CThreadMutex::CThreadMutex( const char* pDebugName ) :
m_currentOwnerID(0), m_lockCount(0), m_pDebugName(0)
{
// enable recursive locks as we need them
pthread_mutexattr_init( &m_Attr );
@ -1587,28 +1597,34 @@ inline CThreadMutex::~CThreadMutex()
//---------------------------------------------------------
inline void CThreadMutex::Lock()
inline void CThreadMutex::Lock( const char *pFileName, int nLine )
{
pthread_mutex_lock( &m_Mutex );
if (m_lockCount == 0)
m_currentOwnerID = ThreadGetCurrentId();
m_lockCount++;
}
//---------------------------------------------------------
inline void CThreadMutex::Unlock()
inline void CThreadMutex::Unlock( const char *pFileName, int nLine )
{
m_lockCount--;
if (m_lockCount == 0)
m_currentOwnerID = 0;
pthread_mutex_unlock( &m_Mutex );
}
//---------------------------------------------------------
inline void CThreadMutex::LockSilent()
inline void CThreadMutex::LockSilent( const char *pFileName, int nLine )
{
pthread_mutex_lock( &m_Mutex );
}
//---------------------------------------------------------
inline void CThreadMutex::UnlockSilent()
inline void CThreadMutex::UnlockSilent( const char *pFileName, int nLine )
{
pthread_mutex_unlock( &m_Mutex );
}
@ -1617,7 +1633,9 @@ inline void CThreadMutex::UnlockSilent()
inline bool CThreadMutex::AssertOwnedByCurrentThread()
{
return true;
if (ThreadGetCurrentId() == m_currentOwnerID)
return true;
return false;
}
//---------------------------------------------------------

View File

@ -108,7 +108,7 @@ template<> inline unsigned HashItem<char *>(char * const &pszKey )
uint32 MurmurHash2( const void * key, int len, uint32 seed );
// return murmurhash2 of a downcased string
uint32 MurmurHash2LowerCase( char const *pString, uint32 nSeed );
uint32 MurmurHash2LowerCase( char const *pString, int nLength, uint32 nSeed );
uint64 MurmurHash64( const void * key, int len, uint32 seed );

View File

@ -0,0 +1,74 @@
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//===========================================================================//
#ifndef MEMBLOCKALLOCATOR_H
#define MEMBLOCKALLOCATOR_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
typedef unsigned int MemBlockHandle_t;
#define MEMBLOCKHANDLE_INVALID ((MemBlockHandle_t)~0)
class CUtlMemoryBlockAllocator
{
public:
DLL_CLASS_IMPORT CUtlMemoryBlockAllocator( int nInitPages, unsigned int nPageSize, RawAllocatorType_t eAllocatorType );
DLL_CLASS_IMPORT ~CUtlMemoryBlockAllocator( void );
DLL_CLASS_IMPORT void RemoveAll( size_t nSize = 0 );
DLL_CLASS_IMPORT void Purge( void );
DLL_CLASS_IMPORT MemBlockHandle_t Alloc( unsigned int nSize );
DLL_CLASS_IMPORT MemBlockHandle_t AllocAndCopy( const char* pBuf, unsigned int nSize );
DLL_CLASS_IMPORT uint64 MemUsage( void );
DLL_CLASS_IMPORT void SetPageSize( unsigned int nPageSize );
DLL_CLASS_IMPORT MemBlockHandle_t FindPageWithSpace( unsigned int nSpace );
void* GetBlock( MemBlockHandle_t handle ) const;
private:
struct MemPage_t
{
unsigned int m_nTotalSize;
unsigned int m_nUsedSize;
byte* m_pMemory;
};
typedef CUtlVector<MemPage_t, CUtlMemory_RawAllocator<MemPage_t>> MemPagesVec_t;
unsigned int m_nMaxPagesExp;
unsigned int m_nPageIndexMask;
unsigned int m_nPageIndexShift;
unsigned int m_nBlockOffsetMask;
MemPagesVec_t m_MemPages;
unsigned int m_nPageSize;
RawAllocatorType_t m_eRawAllocatorType;
};
inline void* CUtlMemoryBlockAllocator::GetBlock( MemBlockHandle_t handle ) const
{
int nPageIndex = handle >> m_nPageIndexShift;
int nBlockOffset = handle & m_nBlockOffsetMask;
if ( nPageIndex >= 0 && nPageIndex < m_MemPages.Count() )
return (void*)&m_MemPages[ nPageIndex ].m_pMemory[ nBlockOffset ];
return NULL;
}
#endif // MEMBLOCKALLOCATOR_H

View File

@ -0,0 +1,34 @@
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//===========================================================================//
#ifndef RAWALLOCATOR_H
#define RAWALLOCATOR_H
#ifdef _WIN32
#pragma once
#endif
enum RawAllocatorType_t : uint8
{
RawAllocator_Standard = 0,
RawAllocator_Platform = 1,
};
class CRawAllocator
{
public:
DLL_CLASS_IMPORT static void* Alloc( RawAllocatorType_t eAllocatorType, size_t nSize, size_t* nAdjustedSize );
DLL_CLASS_IMPORT static void Free( RawAllocatorType_t eAllocatorType, void* pMem, size_t nSize );
};
#endif // RAWALLOCATOR_H

View File

@ -139,6 +139,8 @@ inline char *strlwr( char *start )
// AM TODO: handle this for the rest (above and more) now exported by tier0
PLATFORM_INTERFACE int V_stricmp_fast(const char* s1, const char* s2);
PLATFORM_INTERFACE int _V_strnicmp_fast(const char* s1, const char* s2, int n);
// Compares two strings with the support of wildcarding only for the first arg (includes '*' for multiple and '?' for single char usages)
PLATFORM_INTERFACE int V_CompareNameWithWildcards(const char *wildcarded_string, const char *compare_to, bool case_sensitive = false);

View File

@ -104,7 +104,7 @@ public:
}
};
template <typename KeyT, typename ValueT = empty_t, typename KeyHashT = DefaultHashFunctor<KeyT>, typename KeyIsEqualT = DefaultEqualFunctor<KeyT>, typename AlternateKeyT = typename ArgumentTypeInfo<KeyT>::Alt_t >
template <typename KeyT, typename ValueT = empty_t, typename KeyHashT = DefaultHashFunctor<KeyT>, typename KeyIsEqualT = DefaultEqualFunctor<KeyT>, typename AlternateKeyT = typename ArgumentTypeInfo<KeyT>::Alt_t, typename TableT = CUtlMemory< CUtlHashtableEntry< KeyT, ValueT > > >
class CUtlHashtable
{
public:
@ -121,13 +121,16 @@ protected:
enum { FLAG_LAST = entry_t::FLAG_LAST };
enum { MASK_HASH = entry_t::MASK_HASH };
CUtlMemory< entry_t > m_table;
TableT m_table;
int m_nUsed;
int m_nTableSize;
int m_nMinSize;
bool m_bSizeLocked;
KeyIsEqualT m_eq;
KeyHashT m_hash;
void InitTable();
// Allocate an empty table and then re-insert all existing entries.
void DoRealloc( int size );
@ -155,13 +158,16 @@ protected:
public:
explicit CUtlHashtable( int minimumSize = 32 )
: m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(), m_hash() { }
: m_nUsed(0), m_nTableSize(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(), m_hash() { InitTable(); }
CUtlHashtable( int nInitSize, RawAllocatorType_t eAllocatorType, int minimumSize = 32 )
: m_table(0, nInitSize, eAllocatorType), m_nUsed(0), m_nTableSize(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(), m_hash() { InitTable(); }
CUtlHashtable( int minimumSize, const KeyHashT &hash, KeyIsEqualT const &eq = KeyIsEqualT() )
: m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { }
: m_nUsed(0), m_nTableSize(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { InitTable(); }
CUtlHashtable( entry_t* pMemory, unsigned int nCount, const KeyHashT &hash = KeyHashT(), KeyIsEqualT const &eq = KeyIsEqualT() )
: m_nUsed(0), m_nMinSize(8), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { SetExternalBuffer( pMemory, nCount ); }
: m_nUsed(0), m_nTableSize(0), m_nMinSize(8), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { SetExternalBuffer( pMemory, nCount ); }
~CUtlHashtable() { RemoveAll(); }
@ -178,7 +184,7 @@ public:
KeyIsEqualT const &GetEqualRef() const { return m_eq; }
// Handle validation
bool IsValidHandle( handle_t idx ) const { return (unsigned)idx < (unsigned)m_table.Count() && m_table[idx].IsValid(); }
bool IsValidHandle( handle_t idx ) const { return (unsigned)idx < (unsigned)m_nTableSize && m_table[idx].IsValid(); }
static handle_t InvalidHandle() { return (handle_t) -1; }
// Iteration functions
@ -191,7 +197,7 @@ public:
// Key lookup, returns InvalidHandle() if not found
handle_t Find( KeyArg_t k ) const { return DoLookup<KeyArg_t>( k, m_hash(k), NULL ); }
handle_t Find( KeyArg_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup<KeyArg_t>( k, hash, NULL ); }
handle_t Find( KeyArg_t k, unsigned int hash ) const { Assert( hash == m_hash(k) ); return DoLookup<KeyArg_t>( k, hash, NULL ); }
// Alternate-type key lookup, returns InvalidHandle() if not found
handle_t Find( KeyAlt_t k ) const { return DoLookup<KeyAlt_t>( k, m_hash(k), NULL ); }
handle_t Find( KeyAlt_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup<KeyAlt_t>( k, hash, NULL ); }
@ -224,7 +230,7 @@ public:
void RemoveAll();
// Nuke and release memory.
void Purge() { RemoveAll(); m_table.Purge(); }
void Purge() { RemoveAll(); m_table.Purge(); m_nTableSize = 0; }
// Reserve table capacity up front to avoid reallocation during insertions
void Reserve( int expected ) { if ( expected > m_nUsed ) DoRealloc( expected * 4 / 3 ); }
@ -254,7 +260,7 @@ public:
// Swap memory and contents with another identical hashtable
// (NOTE: if using function pointers or functors with state,
// it is up to the caller to ensure that they are compatible!)
void Swap( CUtlHashtable &other ) { m_table.Swap(other.m_table); ::V_swap(m_nUsed, other.m_nUsed); }
void Swap( CUtlHashtable &other ) { m_table.Swap(other.m_table); ::V_swap(m_nUsed, other.m_nUsed); ::V_swap(m_nTableSize, other.m_nTableSize); }
#if _DEBUG
// Validate the integrity of the hashtable
@ -265,21 +271,31 @@ private:
CUtlHashtable(const CUtlHashtable& copyConstructorIsNotImplemented);
};
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::InitTable()
{
if ( m_table.Count() > 0 )
{
m_nTableSize = LargestPowerOfTwoLessThanOrEqual( m_table.Count() );
for ( int i = 0; i < m_nTableSize; ++i )
m_table[i].MarkInvalid();
}
}
// Set external memory (raw byte buffer, best-fit)
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership, bool bGrowable )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership, bool bGrowable )
{
Assert( ((uintptr_t)pRawBuffer % __alignof(int)) == 0 );
uint32 bestSize = LargestPowerOfTwoLessThanOrEqual( nBytes / sizeof(entry_t) );
Assert( bestSize != 0 && bestSize*sizeof(entry_t) <= nBytes );
return SetExternalBuffer( (entry_t*) pRawBuffer, bestSize, bAssumeOwnership, bGrowable );
}
// Set external memory (typechecked, must be power of two)
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership, bool bGrowable )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership, bool bGrowable )
{
Assert( IsPowerOfTwo(nSize) );
Assert( m_nUsed == 0 );
@ -289,31 +305,76 @@ void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::SetExternalBuf
m_table.AssumeMemory( pBuffer, nSize );
else
m_table.SetExternalBuffer( pBuffer, nSize );
m_nTableSize = nSize;
m_bSizeLocked = !bGrowable;
}
// Allocate an empty table and then re-insert all existing entries.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRealloc( int size )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoRealloc( int size )
{
Assert( !m_bSizeLocked );
CUtlMemory_RawAllocator<entry_t> oldTable;
entry_t * RESTRICT pOldBase = NULL;
int nOldSize = m_nTableSize;
if ( !m_table.IsExternallyAllocated() )
{
int tableSize = m_table.Count();
pOldBase = m_table.Detach();
if ( pOldBase )
oldTable.AssumeMemory( pOldBase, tableSize, m_table.GetRawAllocatorType() );
}
else
{
if ( nOldSize > 0 )
{
size_t nBytes = nOldSize * sizeof(entry_t);
if ( nBytes >= 0x4000 )
{
pOldBase = (entry_t *)malloc( nBytes );
oldTable.AssumeMemory( pOldBase, nOldSize, RawAllocator_Standard );
}
else
{
pOldBase = (entry_t *)stackalloc( nBytes );
}
memcpy( pOldBase, m_table.Base(), nBytes );
}
m_table.Purge();
}
size = SmallestPowerOfTwoGreaterOrEqual( MAX( m_nMinSize, size ) );
Assert( size > 0 && (uint)size <= entry_t::IdealIndex( ~0, 0x1FFFFFFF ) ); // reasonable power of 2
Assert( size > m_nUsed );
CUtlMemory<entry_t> oldTable;
oldTable.Swap( m_table );
entry_t * RESTRICT const pOldBase = oldTable.Base();
m_table.EnsureCapacity( size );
// correct the size if we have allocated more than required
while ( size <= INT_MAX/2 )
{
int newSize = size*2;
if ( newSize > m_table.Count() )
break;
size = newSize;
}
m_nTableSize = size;
entry_t * const pNewBase = m_table.Base();
for ( int i = 0; i < size; ++i )
for ( int i = 0; i < m_table.Count(); ++i )
pNewBase[i].MarkInvalid();
int nLeftToMove = m_nUsed;
m_nUsed = 0;
for ( int i = oldTable.Count() - 1; i >= 0; --i )
for ( int i = nOldSize - 1; i >= 0; --i )
{
if ( pOldBase[i].IsValid() )
{
@ -328,14 +389,14 @@ void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRealloc( int
// Move an existing entry to a free slot, leaving a hole behind
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::BumpEntry( unsigned int idx )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::BumpEntry( unsigned int idx )
{
Assert( m_table[idx].IsValid() );
Assert( m_nUsed < m_table.Count() );
Assert( m_nUsed < m_nTableSize );
entry_t* table = m_table.Base();
unsigned int slotmask = m_table.Count()-1;
unsigned int slotmask = m_nTableSize-1;
unsigned int new_flags_and_hash = table[idx].flags_and_hash & (FLAG_LAST | MASK_HASH);
unsigned int chainid = entry_t::IdealIndex( new_flags_and_hash, slotmask );
@ -394,25 +455,25 @@ void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::BumpEntry( uns
table[idx].MarkInvalid();
}
// Insert a value at the root position for that value's hash chain.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsertUnconstructed( unsigned int h, bool allowGrow )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoInsertUnconstructed( unsigned int h, bool allowGrow )
{
if ( allowGrow && !m_bSizeLocked )
{
// Keep the load factor between .25 and .75
int newSize = m_nUsed + 1;
if ( ( newSize*4 < m_table.Count() && m_table.Count() > m_nMinSize*2 ) || newSize*4 > m_table.Count()*3 )
if ( newSize*4 > m_nTableSize*3 )
{
DoRealloc( newSize * 4 / 3 );
}
}
Assert( m_nUsed < m_table.Count() );
Assert( m_nUsed < m_nTableSize );
++m_nUsed;
entry_t* table = m_table.Base();
unsigned int slotmask = m_table.Count()-1;
unsigned int slotmask = m_nTableSize-1;
unsigned int new_flags_and_hash = FLAG_LAST | (h & MASK_HASH);
unsigned int idx = entry_t::IdealIndex( h, slotmask );
if ( table[idx].IdealIndex( slotmask ) == idx )
@ -433,9 +494,9 @@ int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsertUnconst
// Key lookup. Can also return previous-in-chain if result is a chained slot.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
template <typename KeyParamT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const
{
if ( m_nUsed == 0 )
{
@ -444,8 +505,8 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoL
}
const entry_t* table = m_table.Base();
unsigned int slotmask = m_table.Count()-1;
Assert( m_table.Count() > 0 && (slotmask & m_table.Count()) == 0 );
unsigned int slotmask = m_nTableSize-1;
Assert( m_nTableSize > 0 && (slotmask & m_nTableSize) == 0 );
unsigned int chainid = entry_t::IdealIndex( h, slotmask );
unsigned int idx = chainid;
@ -486,9 +547,9 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoL
// Key insertion, or return index of existing key if found
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
template <typename KeyParamT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsert( KeyParamT k, unsigned int h )
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoInsert( KeyParamT k, unsigned int h )
{
handle_t idx = DoLookup<KeyParamT>( k, h, NULL );
if ( idx == (handle_t) -1 )
@ -500,9 +561,9 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoI
}
// Key insertion, or return index of existing key if found
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
template <typename KeyParamT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsert( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h, bool *pDidInsert )
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoInsert( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h, bool *pDidInsert )
{
handle_t idx = DoLookup<KeyParamT>( k, h, NULL );
if ( idx == (handle_t) -1 )
@ -519,9 +580,9 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoI
}
// Key insertion
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
template <typename KeyParamT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h )
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h )
{
Assert( DoLookup<KeyParamT>( k, h, NULL ) == (handle_t) -1 );
handle_t idx = (handle_t) DoInsertUnconstructed( h, true );
@ -531,11 +592,11 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoI
// Remove single element by key + hash. Returns the location of the new empty hole.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
template <typename KeyParamT>
int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRemove( KeyParamT x, unsigned int h )
int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::DoRemove( KeyParamT x, unsigned int h )
{
unsigned int slotmask = m_table.Count()-1;
unsigned int slotmask = m_nTableSize-1;
handle_t previous = (handle_t) -1;
int idx = (int) DoLookup<KeyParamT>( x, h, &previous );
if (idx == -1)
@ -589,12 +650,12 @@ int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRemove( KeyPa
// Assignment operator. It's up to the user to make sure that the hash and equality functors match.
template <typename K, typename V, typename H, typename E, typename A>
CUtlHashtable<K,V,H,E,A> &CUtlHashtable<K,V,H,E,A>::operator=( CUtlHashtable<K,V,H,E,A> const &src )
template <typename K, typename V, typename H, typename E, typename A, typename T>
CUtlHashtable<K,V,H,E,A,T> &CUtlHashtable<K,V,H,E,A,T>::operator=( CUtlHashtable<K,V,H,E,A,T> const &src )
{
if ( &src != this )
{
Assert( !m_bSizeLocked || m_table.Count() >= src.m_nUsed );
Assert( !m_bSizeLocked || m_nTableSize >= src.m_nUsed );
if ( !m_bSizeLocked )
{
Purge();
@ -606,7 +667,7 @@ CUtlHashtable<K,V,H,E,A> &CUtlHashtable<K,V,H,E,A>::operator=( CUtlHashtable<K,V
}
const entry_t * srcTable = src.m_table.Base();
for ( int i = src.m_table.Count() - 1; i >= 0; --i )
for ( int i = src.m_nTableSize - 1; i >= 0; --i )
{
if ( srcTable[i].IsValid() )
{
@ -622,8 +683,8 @@ CUtlHashtable<K,V,H,E,A> &CUtlHashtable<K,V,H,E,A>::operator=( CUtlHashtable<K,V
}
// Remove and return the next valid iterator for a forward iteration.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveAndAdvance( UtlHashHandle_t idx )
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::RemoveAndAdvance( UtlHashHandle_t idx )
{
Assert( IsValidHandle( idx ) );
@ -644,14 +705,14 @@ UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::Rem
}
// Burn it with fire.
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveAll()
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::RemoveAll()
{
int used = m_nUsed;
if ( used != 0 )
{
entry_t* table = m_table.Base();
for ( int i = m_table.Count() - 1; i >= 0; --i )
for ( int i = m_nTableSize - 1; i >= 0; --i )
{
if ( table[i].IsValid() )
{
@ -665,11 +726,11 @@ void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveAll()
}
}
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::NextHandle( handle_t start ) const
template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT, typename TableT>
UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT, TableT>::NextHandle( handle_t start ) const
{
const entry_t *table = m_table.Base();
for ( int i = (int)start + 1; i < m_table.Count(); ++i )
for ( int i = (int)start + 1; i < m_nTableSize; ++i )
{
if ( table[i].IsValid() )
return (handle_t) i;

View File

@ -19,6 +19,7 @@
#include "tier0/platform.h"
#include "tier0/memalloc.h"
#include "tier1/rawallocator.h"
#include "mathlib/mathlib.h"
#include "tier0/memdbgon.h"
@ -48,7 +49,7 @@ class CUtlMemory
{
public:
// constructor, destructor
CUtlMemory( int nGrowSize = 0, int nInitSize = 0 );
CUtlMemory( int nGrowSize = 0, int nInitSize = 0, RawAllocatorType_t eAllocatorType = RawAllocator_Standard );
CUtlMemory( T* pMemory, int numElements );
CUtlMemory( const T* pMemory, int numElements );
~CUtlMemory();
@ -128,6 +129,8 @@ public:
// Set the size by which the memory grows
void SetGrowSize( int size );
RawAllocatorType_t GetRawAllocatorType() const { return RawAllocator_Standard; };
protected:
void ValidateGrowSize()
{
@ -171,14 +174,6 @@ public:
Assert( nInitSize == 0 || nInitSize == SIZE );
}
void EnsureCapacity( int num )
{
if ( CUtlMemory<T>::m_nAllocationCount >= num )
return;
BaseClass::EnsureCapacity( num );
}
private:
T m_pFixedMemory[ SIZE ];
};
@ -389,16 +384,15 @@ private:
//-----------------------------------------------------------------------------
template< class T, class I >
CUtlMemory<T,I>::CUtlMemory( int nGrowSize, int nInitAllocationCount ) : m_pMemory(0),
m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize )
CUtlMemory<T,I>::CUtlMemory( int nGrowSize, int nInitAllocationCount, RawAllocatorType_t eAllocatorType ) : m_pMemory(0),
m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize & ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER) )
{
ValidateGrowSize();
Assert( nGrowSize >= 0 );
if (m_nAllocationCount)
{
UTLMEMORY_TRACK_ALLOC();
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
if (m_nAllocationCount)
{
UTLMEMORY_TRACK_ALLOC();
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
}
}
@ -429,15 +423,14 @@ void CUtlMemory<T,I>::Init( int nGrowSize /*= 0*/, int nInitSize /*= 0*/ )
{
Purge();
m_nGrowSize = nGrowSize;
m_nGrowSize = nGrowSize & ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
m_nAllocationCount = nInitSize;
ValidateGrowSize();
Assert( nGrowSize >= 0 );
if (m_nAllocationCount)
{
UTLMEMORY_TRACK_ALLOC();
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
if (m_nAllocationCount)
{
UTLMEMORY_TRACK_ALLOC();
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
}
}
@ -462,7 +455,7 @@ void CUtlMemory<T,I>::ConvertToGrowableMemory( int nGrowSize )
if ( !IsExternallyAllocated() )
return;
m_nGrowSize = nGrowSize;
m_nGrowSize = nGrowSize & ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
if (m_nAllocationCount)
{
UTLMEMORY_TRACK_ALLOC();
@ -518,6 +511,8 @@ void CUtlMemory<T,I>::AssumeMemory( T* pMemory, int numElements )
// Simply take the pointer but don't mark us as external
m_pMemory = pMemory;
m_nAllocationCount = numElements;
m_nGrowSize &= ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
}
template< class T, class I >
@ -596,9 +591,7 @@ bool CUtlMemory<T,I>::IsReadOnly() const
template< class T, class I >
void CUtlMemory<T,I>::SetGrowSize( int nSize )
{
Assert( !IsExternallyAllocated() );
Assert( nSize >= 0 );
m_nGrowSize = nSize;
m_nGrowSize |= nSize & ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
ValidateGrowSize();
}
@ -648,59 +641,35 @@ inline bool CUtlMemory<T,I>::IsIdxValid( I i ) const
return ( x >= 0 ) && ( x < m_nAllocationCount );
}
PLATFORM_INTERFACE int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem );
PLATFORM_INTERFACE void* UtlMemory_Alloc( void* pMem, bool bRealloc, int nNewSize, int nOldSize );
PLATFORM_INTERFACE void UtlMemory_FailedAllocation( int nTotalElements, int nNewElements );
//-----------------------------------------------------------------------------
// Grows the memory
//-----------------------------------------------------------------------------
inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem )
{
if ( nGrowSize )
{
nAllocationCount = ((1 + ((nNewSize - 1) / nGrowSize)) * nGrowSize);
}
else
{
if ( !nAllocationCount )
{
// Compute an allocation which is at least as big as a cache line...
nAllocationCount = (31 + nBytesItem) / nBytesItem;
}
while (nAllocationCount < nNewSize)
{
#ifndef _X360
nAllocationCount *= 2;
#else
int nNewAllocationCount = ( nAllocationCount * 9) / 8; // 12.5 %
if ( nNewAllocationCount > nAllocationCount )
nAllocationCount = nNewAllocationCount;
else
nAllocationCount *= 2;
#endif
}
}
return nAllocationCount;
}
template< class T, class I >
void CUtlMemory<T,I>::Grow( int num )
{
Assert( num > 0 );
if ( IsExternallyAllocated() )
if ( IsReadOnly() )
{
// Can't grow a buffer whose memory was externally allocated
Assert(0);
return;
}
if ( ( ( int64 )m_nAllocationCount + num ) > INT_MAX )
UtlMemory_FailedAllocation( m_nAllocationCount, num );
// Make sure we have at least numallocated + num allocations.
// Use the grow rules specified for this memory (in m_nGrowSize)
int nAllocationRequested = m_nAllocationCount + num;
UTLMEMORY_TRACK_FREE();
int nNewAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T) );
int nNewAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize & ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER), nAllocationRequested, sizeof(T) );
// if m_nAllocationRequested wraps index type I, recalculate
if ( ( int )( I )nNewAllocationCount < nAllocationRequested )
@ -724,22 +693,16 @@ void CUtlMemory<T,I>::Grow( int num )
}
}
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)UtlMemory_Alloc( m_pMemory, !IsExternallyAllocated(), nNewAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T) );
Assert( m_pMemory );
if ( IsExternallyAllocated() )
m_nGrowSize &= ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
m_nAllocationCount = nNewAllocationCount;
UTLMEMORY_TRACK_ALLOC();
if (m_pMemory)
{
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
Assert( m_pMemory );
}
else
{
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
Assert( m_pMemory );
}
}
@ -752,7 +715,7 @@ inline void CUtlMemory<T,I>::EnsureCapacity( int num )
if (m_nAllocationCount >= num)
return;
if ( IsExternallyAllocated() )
if ( IsReadOnly() )
{
// Can't grow a buffer whose memory was externally allocated
Assert(0);
@ -761,20 +724,15 @@ inline void CUtlMemory<T,I>::EnsureCapacity( int num )
UTLMEMORY_TRACK_FREE();
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)UtlMemory_Alloc( m_pMemory, !IsExternallyAllocated(), num * sizeof(T), m_nAllocationCount * sizeof(T) );
if ( IsExternallyAllocated() )
m_nGrowSize &= ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
m_nAllocationCount = num;
UTLMEMORY_TRACK_ALLOC();
if (m_pMemory)
{
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
}
else
{
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
}
}
@ -815,7 +773,7 @@ void CUtlMemory<T,I>::Purge( int numElements )
return;
}
if ( IsExternallyAllocated() )
if ( IsReadOnly() )
{
// Can't shrink a buffer whose memory was externally allocated, fail silently like purge
return;
@ -837,13 +795,15 @@ void CUtlMemory<T,I>::Purge( int numElements )
UTLMEMORY_TRACK_FREE();
m_nAllocationCount = numElements;
UTLMEMORY_TRACK_ALLOC();
// Allocation count > 0, shrink it down.
MEM_ALLOC_CREDIT_CLASS();
m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
m_pMemory = (T*)UtlMemory_Alloc( m_pMemory, !IsExternallyAllocated(), numElements * sizeof(T), m_nAllocationCount * sizeof(T) );
if ( IsExternallyAllocated() )
m_nGrowSize &= ~(EXTERNAL_CONST_BUFFER_MARKER | EXTERNAL_BUFFER_MARKER);
m_nAllocationCount = numElements;
UTLMEMORY_TRACK_ALLOC();
}
//-----------------------------------------------------------------------------
@ -1064,6 +1024,294 @@ void CUtlMemoryAligned<T, nAlignment>::Purge()
}
}
template< class T >
class CUtlMemory_RawAllocator
{
public:
// constructor, destructor
CUtlMemory_RawAllocator( int nGrowSize = 0, int nInitSize = 0, RawAllocatorType_t eAllocatorType = RawAllocator_Standard );
CUtlMemory_RawAllocator( T* pMemory, int numElements ) { Assert( 0 ); }
~CUtlMemory_RawAllocator();
// Can we use this index?
bool IsIdxValid( int i ) const { return (i >= 0) && (i < NumAllocated()); }
static int InvalidIndex() { return -1; }
// Gets the base address (can change when adding elements!)
T* Base() { return m_pMemory; }
const T* Base() const { return m_pMemory; }
// element access
T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; }
T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; }
// Attaches the buffer to external memory....
void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); }
void AssumeMemory( T *pMemory, int nSize, RawAllocatorType_t eAllocatorType = RawAllocator_Standard );
T* Detach();
void *DetachMemory();
// Fast swap
void Swap( CUtlMemory_RawAllocator< T > &mem );
// Size
int NumAllocated() const { return m_nAllocationCount; }
int Count() const { return m_nAllocationCount; }
// Grows the memory, so that at least allocated + num elements are allocated
void Grow( int num = 1 );
// Makes sure we've got at least this much memory
void EnsureCapacity( int num );
// Memory deallocation
void Purge();
// Purge all but the given number of elements
void Purge( int numElements );
// is the memory externally allocated?
bool IsExternallyAllocated() const { return false; }
// Set the size by which the memory grows
void SetGrowSize( int size );
RawAllocatorType_t GetRawAllocatorType() const;
class Iterator_t
{
public:
Iterator_t( int i ) : index( i ) {}
int index;
bool operator==( const Iterator_t it ) const { return index == it.index; }
bool operator!=( const Iterator_t it ) const { return index != it.index; }
};
Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); }
Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); }
int GetIndex( const Iterator_t &it ) const { return it.index; }
bool IsIdxAfter( int i, const Iterator_t &it ) const { return i > it.index; }
bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); }
Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); }
private:
void SetRawAllocatorType( RawAllocatorType_t eAllocatorType );
enum
{
PLATFORM_ALLOC_MARKER = (1 << 30),
UNUSED_MARKER = (1 << 31),
};
T* m_pMemory;
int m_nAllocationCount;
int m_nGrowSize;
};
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
template< class T >
CUtlMemory_RawAllocator<T>::CUtlMemory_RawAllocator( int nGrowSize, int nInitAllocationCount, RawAllocatorType_t eAllocatorType ) : m_pMemory(0),
m_nAllocationCount(0), m_nGrowSize(nGrowSize & ~(PLATFORM_ALLOC_MARKER | UNUSED_MARKER))
{
SetRawAllocatorType( eAllocatorType );
EnsureCapacity( nInitAllocationCount );
}
template< class T >
CUtlMemory_RawAllocator<T>::~CUtlMemory_RawAllocator()
{
Purge();
}
//-----------------------------------------------------------------------------
// Fast swap
//-----------------------------------------------------------------------------
template< class T >
void CUtlMemory_RawAllocator<T>::Swap( CUtlMemory_RawAllocator<T> &mem )
{
V_swap( m_nGrowSize, mem.m_nGrowSize );
V_swap( m_pMemory, mem.m_pMemory );
V_swap( m_nAllocationCount, mem.m_nAllocationCount );
}
template< class T >
void CUtlMemory_RawAllocator<T>::AssumeMemory( T* pMemory, int numElements, RawAllocatorType_t eAllocatorType )
{
// Blow away any existing allocated memory
Purge();
// Simply take the pointer but don't mark us as external
m_pMemory = pMemory;
m_nAllocationCount = numElements;
SetRawAllocatorType( eAllocatorType );
}
template< class T >
void *CUtlMemory_RawAllocator<T>::DetachMemory()
{
void *pMemory = m_pMemory;
m_pMemory = 0;
m_nAllocationCount = 0;
return pMemory;
}
template< class T >
inline T* CUtlMemory_RawAllocator<T>::Detach()
{
return (T*)DetachMemory();
}
template< class T >
void CUtlMemory_RawAllocator<T>::SetGrowSize( int nSize )
{
m_nGrowSize |= nSize & ~(PLATFORM_ALLOC_MARKER | UNUSED_MARKER);
}
//-----------------------------------------------------------------------------
// Grows the memory
//-----------------------------------------------------------------------------
template< class T >
void CUtlMemory_RawAllocator<T>::Grow( int num )
{
Assert( num > 0 );
if ( ( INT_MAX - m_nAllocationCount ) < num )
{
Plat_FatalErrorFunc( "%s: Invalid grow amount %d\n", __FUNCTION__, num );
DebuggerBreak();
}
EnsureCapacity( m_nAllocationCount + num );
}
//-----------------------------------------------------------------------------
// Makes sure we've got at least this much memory
//-----------------------------------------------------------------------------
template< class T >
inline void CUtlMemory_RawAllocator<T>::EnsureCapacity( int num )
{
if (m_nAllocationCount >= num)
return;
if ( num > ( SIZE_MAX / sizeof(T) ) )
{
Plat_FatalErrorFunc( "%s: Invalid capacity %u\n", __FUNCTION__, num );
DebuggerBreak();
}
void *pMemory = m_pMemory;
size_t nSize = m_nAllocationCount * sizeof(T);
RawAllocatorType_t eAllocatorType = GetRawAllocatorType();
size_t adjustedSize;
m_pMemory = (T*)CRawAllocator::Alloc( eAllocatorType, num * sizeof(T), &adjustedSize );
m_nAllocationCount = ( int )( adjustedSize / sizeof(T) );
if ( pMemory )
{
memcpy( m_pMemory, pMemory, nSize );
CRawAllocator::Free( eAllocatorType, pMemory, nSize );
}
}
//-----------------------------------------------------------------------------
// Memory deallocation
//-----------------------------------------------------------------------------
template< class T >
void CUtlMemory_RawAllocator<T>::Purge()
{
if (m_pMemory)
{
CRawAllocator::Free( GetRawAllocatorType(), m_pMemory, m_nAllocationCount * sizeof(T) );
m_pMemory = 0;
m_nAllocationCount = 0;
}
}
template< class T >
void CUtlMemory_RawAllocator<T>::Purge( int numElements )
{
Assert( numElements >= 0 );
if( numElements > m_nAllocationCount )
{
// Ensure this isn't a grow request in disguise.
Assert( numElements <= m_nAllocationCount );
return;
}
// If we have zero elements, simply do a purge:
if( numElements == 0 )
{
Purge();
return;
}
// If the number of elements is the same as the allocation count, we are done.
if( numElements == m_nAllocationCount )
{
return;
}
if( !m_pMemory )
{
// Allocation count is non zero, but memory is null.
Assert( m_pMemory );
return;
}
void *pMemory = m_pMemory;
size_t nSize = m_nAllocationCount * sizeof(T);
RawAllocatorType_t eAllocatorType = GetRawAllocatorType();
size_t adjustedSize;
m_pMemory = (T*)CRawAllocator::Alloc( eAllocatorType, numElements * sizeof(T), &adjustedSize );
if ( adjustedSize < nSize )
{
m_nAllocationCount = ( int )( adjustedSize / sizeof(T) );
memcpy( m_pMemory, pMemory, adjustedSize );
CRawAllocator::Free( eAllocatorType, pMemory, nSize );
}
else
{
CRawAllocator::Free( eAllocatorType, m_pMemory, adjustedSize );
m_pMemory = pMemory;
}
}
template< class T >
RawAllocatorType_t CUtlMemory_RawAllocator<T>::GetRawAllocatorType() const
{
return ( RawAllocatorType_t )( ( m_nGrowSize & PLATFORM_ALLOC_MARKER ) != 0 );
}
template< class T >
void CUtlMemory_RawAllocator<T>::SetRawAllocatorType( RawAllocatorType_t eAllocatorType )
{
if ( eAllocatorType == RawAllocator_Platform )
{
m_nGrowSize |= PLATFORM_ALLOC_MARKER;
}
else
{
if ( eAllocatorType != RawAllocator_Standard )
{
Plat_FatalErrorFunc( "%s: Unsupported raw allocator type %u\n", __FUNCTION__, eAllocatorType );
DebuggerBreak();
}
m_nGrowSize &= ~PLATFORM_ALLOC_MARKER;
}
}
#include "tier0/memdbgoff.h"
#endif // UTLMEMORY_H

View File

@ -14,10 +14,10 @@
#endif
#include "tier0/threadtools.h"
#include "tier1/utltshash.h"
#include "tier1/stringpool.h"
#include "tier0/vprof.h"
#include "tier1/utltshash.h"
#include "tier1/generichash.h"
#include "tier1/utlvector.h"
#include "tier1/utlhashtable.h"
#include "tier1/memblockallocator.h"
//-----------------------------------------------------------------------------
// CUtlSymbolTableLarge:
@ -29,7 +29,7 @@
// to the string data, the hash precedes it in memory and is used to speed up searching, etc.
//-----------------------------------------------------------------------------
typedef intp UtlSymLargeId_t;
typedef unsigned int UtlSymLargeId_t;
#define UTL_INVAL_SYMBOL_LARGE ((UtlSymLargeId_t)~0)
@ -39,456 +39,349 @@ public:
// constructor, destructor
CUtlSymbolLarge()
{
u.m_Id = UTL_INVAL_SYMBOL_LARGE;
m_pString = NULL;
}
CUtlSymbolLarge( UtlSymLargeId_t id )
CUtlSymbolLarge( const char* pStr )
{
u.m_Id = id;
m_pString = pStr;
}
CUtlSymbolLarge( CUtlSymbolLarge const& sym )
{
u.m_Id = sym.u.m_Id;
m_pString = sym.m_pString;
}
// operator=
CUtlSymbolLarge& operator=( CUtlSymbolLarge const& src )
{
u.m_Id = src.u.m_Id;
m_pString = src.m_pString;
return *this;
}
// operator==
bool operator==( CUtlSymbolLarge const& src ) const
{
return u.m_Id == src.u.m_Id;
return m_pString == src.m_pString;
}
// operator==
bool operator==( UtlSymLargeId_t const& src ) const
{
return u.m_Id == src;
}
// operator==
// operator!=
bool operator!=( CUtlSymbolLarge const& src ) const
{
return u.m_Id != src.u.m_Id;
return m_pString != src.m_pString;
}
// operator==
bool operator!=( UtlSymLargeId_t const& src ) const
inline const char* String() const
{
return u.m_Id != src;
}
// Gets at the symbol
operator UtlSymLargeId_t const() const
{
return u.m_Id;
}
// Gets the string associated with the symbol
inline const char* String( ) const
{
if ( u.m_Id == UTL_INVAL_SYMBOL_LARGE )
return "";
return u.m_pAsString;
return m_pString;
}
inline bool IsValid() const
{
return u.m_Id != UTL_INVAL_SYMBOL_LARGE ? true : false;
return m_pString != NULL;
}
private:
// Disallowed
CUtlSymbolLarge( const char* pStr ); // they need to go through the table to assign the ptr
bool operator==( const char* pStr ) const; // disallow since we don't know if the table this is from was case sensitive or not... maybe we don't care
union
{
UtlSymLargeId_t m_Id;
char const *m_pAsString;
} u;
const char* m_pString;
};
#define MIN_STRING_POOL_SIZE 2048
inline uint32 CUtlSymbolLarge_Hash( bool CASEINSENSITIVE, const char *pString, int len )
{
return ( CASEINSENSITIVE ? HashStringCaseless( pString ) : HashString( pString ) );
inline uint32 CUtlSymbolLarge_Hash( bool CASEINSENSITIVE, const char *pString, int len )
{
return ( CASEINSENSITIVE ? MurmurHash2LowerCase( pString, len, 0x31415926 ) : MurmurHash2( pString, len, 0x31415926 ) );
}
typedef uint64 LargeSymbolTableHashDecoration_t;
// The structure consists of the hash immediately followed by the string data
struct CUtlSymbolTableLargeBaseTreeEntry_t
{
LargeSymbolTableHashDecoration_t m_Hash;
// Variable length string data
char m_String[1];
bool IsEmpty() const
{
return ( ( m_Hash == 0 ) && ( 0 == m_String[0] ) );
}
char const *String() const
{
return (const char *)&m_String[ 0 ];
}
CUtlSymbolLarge ToSymbol() const
{
return reinterpret_cast< UtlSymLargeId_t >( String() );
}
LargeSymbolTableHashDecoration_t HashValue() const
{
return m_Hash;
}
};
template< class TreeType, bool CASEINSENSITIVE >
class CTreeEntryLess
{
public:
CTreeEntryLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree
bool operator!() const { return false; }
bool operator()( CUtlSymbolTableLargeBaseTreeEntry_t * const &left, CUtlSymbolTableLargeBaseTreeEntry_t * const &right ) const
{
// compare the hashes
if ( left->m_Hash == right->m_Hash )
{
// if the hashes match compare the strings
if ( !CASEINSENSITIVE )
return strcmp( left->String(), right->String() ) < 0;
else
return V_stricmp( left->String(), right->String() ) < 0;
}
else
{
return left->m_Hash < right->m_Hash;
}
}
};
// For non-threaded versions, simply index into CUtlRBTree
template< bool CASEINSENSITIVE >
class CNonThreadsafeTree : public CUtlRBTree<CUtlSymbolTableLargeBaseTreeEntry_t *, intp, CTreeEntryLess< CNonThreadsafeTree< CASEINSENSITIVE >, CASEINSENSITIVE > >
{
public:
typedef CUtlRBTree<CUtlSymbolTableLargeBaseTreeEntry_t *, intp, CTreeEntryLess< CNonThreadsafeTree, CASEINSENSITIVE > > CNonThreadsafeTreeType;
CNonThreadsafeTree() :
CNonThreadsafeTreeType( 0, 16 )
{
}
inline void Commit()
{
// Nothing, only matters for thread-safe tables
}
inline int Insert( CUtlSymbolTableLargeBaseTreeEntry_t *entry )
{
return CNonThreadsafeTreeType::Insert( entry );
}
inline int Find( CUtlSymbolTableLargeBaseTreeEntry_t *entry ) const
{
return CNonThreadsafeTreeType::Find( entry );
}
inline int InvalidIndex() const
{
return CNonThreadsafeTreeType::InvalidIndex();
}
inline int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const
{
CUtlVector< CUtlSymbolTableLargeBaseTreeEntry_t * > list;
list.EnsureCount( nCount );
for ( int i = 0; i < nCount; ++i )
{
pElements[ i ] = CNonThreadsafeTreeType::Element( i )->ToSymbol();
}
return nCount;
}
};
// Since CUtlSymbolTableLargeBaseTreeEntry_t already has the hash
// contained inside of it, don't need to recompute a hash here
template < int BUCKET_COUNT, class KEYTYPE, bool CASEINSENSITIVE >
class CCThreadsafeTreeHashMethod
{
public:
static int Hash( const KEYTYPE &key, int nBucketMask )
{
uint32 nHash = key->HashValue();
return ( nHash & nBucketMask );
}
static bool Compare( CUtlSymbolTableLargeBaseTreeEntry_t * const &lhs, CUtlSymbolTableLargeBaseTreeEntry_t * const &rhs )
{
if ( lhs->m_Hash != rhs->m_Hash )
return false;
if ( !CASEINSENSITIVE )
{
return ( !Q_strcmp( lhs->String(), rhs->String() ) ? true : false );
}
return ( !Q_stricmp( lhs->String(), rhs->String() ) ? true : false );
}
};
/*
NOTE: So the only crappy thing about using a CUtlTSHash here is that the KEYTYPE is a CUtlSymbolTableLargeBaseTreeEntry_t ptr which has both the
hash and the string since with strings there is a good chance of hash collision after you have a fair number of strings so we have to implement
a Compare method (above) which falls back to strcmp/stricmp if the hashes are equal. This means that all of the data is in the KEYTYPE of the hash and the
payload doesn't matter. So I made the payload also be a pointer to a CUtlSymbolTableLargeBaseTreeEntry_t since that makes using the API more convenient
TODO: If we have a CUtlTSHash that was all about the existence of the KEYTYPE and didn't require a payload (or template on 'void') then we could eliminate
50% of the pointer overhead used for this data structure.
*/
// Thread safe version is based on the
template < bool CASEINSENSITIVE >
class CThreadsafeTree : public CUtlTSHash< CUtlSymbolTableLargeBaseTreeEntry_t *, 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CCThreadsafeTreeHashMethod< 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CASEINSENSITIVE > >
{
public:
typedef CUtlTSHash< CUtlSymbolTableLargeBaseTreeEntry_t *, 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CCThreadsafeTreeHashMethod< 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CASEINSENSITIVE > > CThreadsafeTreeType;
CThreadsafeTree() :
CThreadsafeTreeType( 32 )
{
}
inline void Commit()
{
CThreadsafeTreeType::Commit();
}
inline int Insert( CUtlSymbolTableLargeBaseTreeEntry_t *entry )
{
return CThreadsafeTreeType::Insert( entry, entry );
}
inline int Find( CUtlSymbolTableLargeBaseTreeEntry_t *entry )
{
return CThreadsafeTreeType::Find( entry );
}
inline int InvalidIndex() const
{
return CThreadsafeTreeType::InvalidHandle();
}
inline int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const
{
CUtlVector< UtlTSHashHandle_t > list;
list.EnsureCount( nCount );
int c = CThreadsafeTreeType::GetElements( nFirstElement, nCount, list.Base() );
for ( int i = 0; i < c; ++i )
{
pElements[ i ] = CThreadsafeTreeType::Element( list[ i ] )->ToSymbol();
}
return c;
}
typedef uint32 LargeSymbolTableHashDecoration_t;
// The structure consists of the hash immediately followed by the string data
struct CUtlSymbolTableLargeBaseTreeEntry_t
{
LargeSymbolTableHashDecoration_t m_Hash;
// Variable length string data
char m_String[1];
bool IsEmpty() const
{
return ( ( m_Hash == 0 ) && ( 0 == m_String[0] ) );
}
char const *String() const
{
return (const char *)&m_String[ 0 ];
}
CUtlSymbolLarge ToSymbol() const
{
return CUtlSymbolLarge( String() );
}
LargeSymbolTableHashDecoration_t HashValue() const
{
return m_Hash;
}
};
// Base Class for threaded and non-threaded types
template < class TreeType, bool CASEINSENSITIVE >
template < class MutexType, bool CASEINSENSITIVE >
class CUtlSymbolTableLargeBase
{
public:
// constructor, destructor
CUtlSymbolTableLargeBase();
~CUtlSymbolTableLargeBase();
CUtlSymbolTableLargeBase( int nGrowSize = 0, int nInitSize = 16, RawAllocatorType_t eAllocatorType = RawAllocator_Standard )
: m_HashTable( 0, eAllocatorType ),
m_MemBlocks( nGrowSize, nInitSize, eAllocatorType ),
m_Mutex( "CUtlSymbolTableLargeBase" ),
m_MemBlockAllocator( ( nInitSize > 0 ) ? 8 : 0, 2048, eAllocatorType ),
m_nElementLimit( INT_MAX - 1 ),
m_bThrowError( true ) { }
~CUtlSymbolTableLargeBase() { }
// Finds and/or creates a symbol based on the string
CUtlSymbolLarge AddString( const char* pString );
CUtlSymbolLarge AddString( const char* pString, int nLength );
// Finds the symbol for pString
CUtlSymbolLarge Find( const char* pString ) const;
CUtlSymbolLarge Find( const char* pString, int nLength ) const;
// Remove all symbols in the table.
void RemoveAll();
void RemoveAll();
void Purge();
private:
CUtlSymbolLarge AddString( unsigned int hash, const char* pString, int nLength );
CUtlSymbolLarge Find( unsigned int hash, const char* pString, int nLength ) const;
int GetNumStrings( void ) const
{
return m_Lookup.Count();
}
const char* String( UtlSymLargeId_t id ) const;
unsigned int HashValue( UtlSymLargeId_t id ) const;
void Commit()
{
m_Lookup.Commit();
}
// Returns elements in the table
int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const
{
return m_Lookup.GetElements( nFirstElement, nCount, pElements );
}
uint64 GetMemoryUsage() const
{
uint64 unBytesUsed = 0u;
for ( int i=0; i < m_StringPools.Count(); i++ )
{
StringPool_t *pPool = m_StringPools[i];
unBytesUsed += (uint64)pPool->m_TotalLen;
}
return unBytesUsed;
}
protected:
struct StringPool_t
{
int m_TotalLen; // How large is
int m_SpaceUsed;
char m_Data[1];
struct UtlSymTableLargeAltKey
{
CUtlSymbolTableLargeBase* m_pTable;
const char* m_pString;
int m_nLength;
};
TreeType m_Lookup;
struct UtlSymTableLargeHashFunctor
{
ptrdiff_t m_tableOffset;
// stores the string data
CUtlVector< StringPool_t * > m_StringPools;
UtlSymTableLargeHashFunctor()
{
m_tableOffset = 1024 - (uintptr_t)(&((Hashtable_t*)1024)->GetHashRef());
}
private:
int FindPoolWithSpace( int len ) const;
unsigned int operator()( UtlSymTableLargeAltKey k ) const
{
return CUtlSymbolLarge_Hash( CASEINSENSITIVE, k.m_pString, k.m_nLength );
}
unsigned int operator()( UtlSymLargeId_t k ) const
{
CUtlSymbolTableLargeBase* pTable = (CUtlSymbolTableLargeBase*)((uintptr_t)this + m_tableOffset);
pTable->HashValue( k );
}
};
struct UtlSymTableLargeEqualFunctor
{
ptrdiff_t m_tableOffset;
UtlSymTableLargeEqualFunctor()
{
m_tableOffset = 1024 - (uintptr_t)(&((Hashtable_t*)1024)->GetEqualRef());
}
bool operator()( UtlSymLargeId_t a, UtlSymLargeId_t b ) const
{
CUtlSymbolTableLargeBase* pTable = (CUtlSymbolTableLargeBase*)((uintptr_t)this + m_tableOffset);
if ( !CASEINSENSITIVE )
return strcmp( pTable->String( a ), pTable->String( b ) ) == 0;
else
return V_stricmp_fast( pTable->String( a ), pTable->String( b ) ) == 0;
}
bool operator()( UtlSymTableLargeAltKey a, UtlSymLargeId_t b ) const
{
const char* pString = a.m_pTable->String( b );
if ( a.m_nLength != strlen( pString ) )
return false;
if ( !CASEINSENSITIVE )
return strncmp( a.m_pString, pString, a.m_nLength ) == 0;
else
return _V_strnicmp_fast( a.m_pString, pString, a.m_nLength ) == 0;
}
bool operator()( UtlSymLargeId_t a, UtlSymTableLargeAltKey b ) const
{
return operator()( b, a );
}
};
typedef CUtlHashtable<UtlSymLargeId_t, empty_t, UtlSymTableLargeHashFunctor, UtlSymTableLargeEqualFunctor, UtlSymTableLargeAltKey, CUtlMemory_RawAllocator<CUtlHashtableEntry<UtlSymLargeId_t, empty_t>>> Hashtable_t;
typedef CUtlVector< MemBlockHandle_t, CUtlMemory_RawAllocator<MemBlockHandle_t> > MemBlocksVec_t;
Hashtable_t m_HashTable;
MemBlocksVec_t m_MemBlocks;
MutexType m_Mutex;
CUtlMemoryBlockAllocator m_MemBlockAllocator;
int m_nElementLimit;
bool m_bThrowError;
};
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
template< class TreeType, bool CASEINSENSITIVE >
inline CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE >::CUtlSymbolTableLargeBase() :
m_StringPools( 8 )
{
}
template< class TreeType, bool CASEINSENSITIVE >
inline CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE>::~CUtlSymbolTableLargeBase()
{
// Release the stringpool string data
RemoveAll();
}
template< class TreeType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE>::Find( const char* pString ) const
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::Find( unsigned int hash, const char* pString, int nLength ) const
{
VPROF( "CUtlSymbolLarge::Find" );
if (!pString)
UtlSymTableLargeAltKey key;
key.m_pTable = ( CUtlSymbolTableLargeBase* )this;
key.m_pString = pString;
key.m_nLength = nLength;
UtlHashHandle_t h = m_HashTable.Find( key, hash );
if ( h == m_HashTable.InvalidHandle() )
return CUtlSymbolLarge();
// Passing this special invalid symbol makes the comparison function
// use the string passed in the context
int len = Q_strlen( pString ) + 1;
return CUtlSymbolLarge( String( m_HashTable[ h ] ) );
}
CUtlSymbolTableLargeBaseTreeEntry_t *search = (CUtlSymbolTableLargeBaseTreeEntry_t *)alloca( len + sizeof( LargeSymbolTableHashDecoration_t ) );
search->m_Hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, len );
Q_memcpy( (char *)&search->m_String[ 0 ], pString, len );
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::AddString( unsigned int hash, const char* pString, int nLength )
{
if ( m_MemBlocks.Count() >= m_nElementLimit )
{
if ( m_bThrowError )
{
Plat_FatalErrorFunc( "FATAL ERROR: CUtlSymbolTableLarge element limit of %u exceeded\n", m_nElementLimit );
DebuggerBreak();
}
int idx = const_cast< TreeType & >(m_Lookup).Find( search );
Warning( "ERROR: CUtlSymbolTableLarge element limit of %u exceeded\n", m_nElementLimit );
}
if ( idx == m_Lookup.InvalidIndex() )
return UTL_INVAL_SYMBOL_LARGE;
MemBlockHandle_t block = m_MemBlockAllocator.Alloc( nLength + sizeof( LargeSymbolTableHashDecoration_t ) + 1 );
CUtlSymbolTableLargeBaseTreeEntry_t *entry = (CUtlSymbolTableLargeBaseTreeEntry_t *)m_MemBlockAllocator.GetBlock( block );
entry->m_Hash = hash;
char *pText = (char *)&entry->m_String[ 0 ];
memcpy( pText, pString, nLength );
pText[ nLength ] = '\0';
UtlSymLargeId_t id = m_MemBlocks.AddToTail( block + sizeof( LargeSymbolTableHashDecoration_t ) );
empty_t empty;
m_HashTable.Insert( id, empty, hash );
const CUtlSymbolTableLargeBaseTreeEntry_t *entry = m_Lookup[ idx ];
return entry->ToSymbol();
}
template< class TreeType, bool CASEINSENSITIVE >
inline int CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE>::FindPoolWithSpace( int len ) const
template < class MutexType, bool CASEINSENSITIVE >
inline const char* CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::String( UtlSymLargeId_t id ) const
{
for ( int i=0; i < m_StringPools.Count(); i++ )
{
StringPool_t *pPool = m_StringPools[i];
return ( const char* )m_MemBlockAllocator.GetBlock( m_MemBlocks[ id ] );
}
if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len )
{
return i;
}
template < class MutexType, bool CASEINSENSITIVE >
inline unsigned int CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::HashValue( UtlSymLargeId_t id ) const
{
CUtlSymbolTableLargeBaseTreeEntry_t *entry = (CUtlSymbolTableLargeBaseTreeEntry_t *)m_MemBlockAllocator.GetBlock( m_MemBlocks[ id ] - sizeof( LargeSymbolTableHashDecoration_t ) );
return entry->HashValue();
}
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::Find( const char* pString, int nLength ) const
{
CUtlSymbolLarge sym;
if ( pString && nLength > 0 && *pString )
{
unsigned int hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, nLength );
m_Mutex.Lock( __FILE__, __LINE__ );
sym = Find( hash, pString, nLength );
m_Mutex.Unlock( __FILE__, __LINE__ );
}
return -1;
return sym;
}
//-----------------------------------------------------------------------------
// Finds and/or creates a symbol based on the string
//-----------------------------------------------------------------------------
template< class TreeType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE>::AddString( const char* pString )
{
VPROF("CUtlSymbolLarge::AddString");
if (!pString)
return UTL_INVAL_SYMBOL_LARGE;
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::Find( const char* pString ) const
{
return Find( pString, pString ? strlen( pString ) : 0 );
}
CUtlSymbolLarge id = Find( pString );
if ( id != UTL_INVAL_SYMBOL_LARGE )
return id;
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::AddString( const char* pString, int nLength )
{
CUtlSymbolLarge sym;
int lenString = Q_strlen(pString) + 1; // length of just the string
int lenDecorated = lenString + sizeof(LargeSymbolTableHashDecoration_t); // and with its hash decoration
// make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly
COMPILE_TIME_ASSERT(sizeof(LargeSymbolTableHashDecoration_t) == sizeof(intp));
lenDecorated = ALIGN_VALUE(lenDecorated, sizeof( intp ) );
// Find a pool with space for this string, or allocate a new one.
int iPool = FindPoolWithSpace( lenDecorated );
if ( iPool == -1 )
if ( pString && nLength > 0 && *pString )
{
// Add a new pool.
int newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE );
StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize );
unsigned int hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, nLength );
pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t );
pPool->m_SpaceUsed = 0;
iPool = m_StringPools.AddToTail( pPool );
m_Mutex.Lock( __FILE__, __LINE__ );
sym = Find( hash, pString, nLength );
if ( !sym.IsValid() )
sym = AddString( hash, pString, nLength );
m_Mutex.Unlock( __FILE__, __LINE__ );
}
// Compute a hash
LargeSymbolTableHashDecoration_t hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, lenString );
// Copy the string in.
StringPool_t *pPool = m_StringPools[iPool];
Assert( pPool->m_SpaceUsed < 0xFFFF );
// This should never happen, because if we had a string > 64k, it
// would have been given its entire own pool.
CUtlSymbolTableLargeBaseTreeEntry_t *entry = ( CUtlSymbolTableLargeBaseTreeEntry_t * )&pPool->m_Data[ pPool->m_SpaceUsed ];
pPool->m_SpaceUsed += lenDecorated;
entry->m_Hash = hash;
char *pText = (char *)&entry->m_String [ 0 ];
Q_memcpy( pText, pString, lenString );
// insert the string into the database
MEM_ALLOC_CREDIT();
int idx = m_Lookup.Insert( entry );
return m_Lookup.Element( idx )->ToSymbol();
return sym;
}
//-----------------------------------------------------------------------------
// Remove all symbols in the table.
//-----------------------------------------------------------------------------
template< class TreeType, bool CASEINSENSITIVE >
inline void CUtlSymbolTableLargeBase<TreeType, CASEINSENSITIVE>::RemoveAll()
{
m_Lookup.Purge();
for ( int i=0; i < m_StringPools.Count(); i++ )
free( m_StringPools[i] );
m_StringPools.RemoveAll();
template < class MutexType, bool CASEINSENSITIVE >
inline CUtlSymbolLarge CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::AddString( const char* pString )
{
return AddString( pString, pString ? strlen( pString ) : 0 );
}
// Case-sensitive
typedef CUtlSymbolTableLargeBase< CNonThreadsafeTree< false >, false > CUtlSymbolTableLarge;
// Case-insensitive
typedef CUtlSymbolTableLargeBase< CNonThreadsafeTree< true >, true > CUtlSymbolTableLarge_CI;
// Multi-threaded case-sensitive
typedef CUtlSymbolTableLargeBase< CThreadsafeTree< false >, false > CUtlSymbolTableLargeMT;
// Multi-threaded case-insensitive
typedef CUtlSymbolTableLargeBase< CThreadsafeTree< true >, true > CUtlSymbolTableLargeMT_CI;
template < class MutexType, bool CASEINSENSITIVE >
inline void CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::RemoveAll()
{
m_Mutex.Lock( __FILE__, __LINE__ );
m_MemBlocks.RemoveAll();
m_HashTable.RemoveAll();
m_MemBlockAllocator.RemoveAll();
m_Mutex.Unlock( __FILE__, __LINE__ );
}
template < class MutexType, bool CASEINSENSITIVE >
inline void CUtlSymbolTableLargeBase< MutexType, CASEINSENSITIVE >::Purge()
{
m_Mutex.Lock( __FILE__, __LINE__ );
m_MemBlocks.Purge();
m_HashTable.Purge();
m_MemBlockAllocator.Purge();
m_Mutex.Unlock( __FILE__, __LINE__ );
}
// Case-sensitive
typedef CUtlSymbolTableLargeBase< CThreadEmptyMutex, false > CUtlSymbolTableLarge;
// Case-insensitive
typedef CUtlSymbolTableLargeBase< CThreadEmptyMutex, true > CUtlSymbolTableLarge_CI;
// Multi-threaded case-sensitive
typedef CUtlSymbolTableLargeBase< CThreadMutex, false > CUtlSymbolTableLargeMT;
// Multi-threaded case-insensitive
typedef CUtlSymbolTableLargeBase< CThreadMutex, true > CUtlSymbolTableLargeMT_CI;
#endif // UTLSYMBOLLARGE_H

View File

@ -45,7 +45,7 @@ public:
typedef T ElemType_t;
// constructor, destructor
CUtlVector( int growSize = 0, int initSize = 0 );
CUtlVector( int growSize = 0, int initSize = 0, RawAllocatorType_t allocatorType = RawAllocator_Standard );
CUtlVector( T* pMemory, int allocationCount, int numElements = 0 );
~CUtlVector();
@ -525,7 +525,7 @@ public:
// constructor, destructor
//-----------------------------------------------------------------------------
template< typename T, class A >
inline CUtlVector<T, A>::CUtlVector( int growSize, int initSize ) :
inline CUtlVector<T, A>::CUtlVector( int growSize, int initSize, RawAllocatorType_t allocatorType ) :
m_Size(0), m_Memory(growSize, initSize)
{
}
@ -729,7 +729,7 @@ void CUtlVector<T, A>::ShiftElementsRight( int elem, int num )
Assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 ));
int numToMove = m_Size - elem - num;
if ((numToMove > 0) && (num > 0))
Q_memmove( &Element(elem+num), &Element(elem), numToMove * sizeof(T) );
memmove( &Element(elem+num), &Element(elem), numToMove * sizeof(T) );
}
template< typename T, class A >
@ -739,7 +739,7 @@ void CUtlVector<T, A>::ShiftElementsLeft( int elem, int num )
int numToMove = m_Size - elem - num;
if ((numToMove > 0) && (num > 0))
{
Q_memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) );
memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) );
#ifdef _DEBUG
Q_memset( &Element(m_Size-num), 0xDD, num * sizeof(T) );

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include "tier0/basetypes.h"
#include "tier0/platform.h"
#include "tier1/utlstring.h"
#include "generichash.h"
#include <ctype.h>
@ -351,3 +352,12 @@ uint32 MurmurHash2( const void *key, int len, uint32 seed )
return h;
}
// return murmurhash2 of a downcased string
uint32 MurmurHash2LowerCase( char const *pString, int nLength, uint32 nSeed )
{
CUtlString buf( pString );
buf.ToLowerFast();
return MurmurHash2( buf.Get(), nLength, nSeed );
}