This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,74 @@
//======= Copyright <20> 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 <stdafx.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;
}

View File

@@ -0,0 +1,100 @@
//======= Copyright <20> 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 <typename T>
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<typename T>
struct MurmurHash3Functor
{
typedef uint32 TargetType;
TargetType operator()(const T &key) const
{
return MurmurHash3Item( key );
}
};
template<>
struct MurmurHash3Functor<char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
template<>
struct MurmurHash3Functor<const char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
#endif // MURMURHASH3_H

View File

@@ -0,0 +1,234 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//=============================================================================
//#include "pch_vstdlib.h"
#include "stdafx.h"
#include "tier0/tslist.h"
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const uint k_cubBytesAllocatedToConsiderFreeingMemory = 5 * k_nMegabyte;
static const int k_cBlocksAllocatedToConsiderFreeingMemory = 10;
typedef TSLNodeBase_t FreeListItem_t;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::CThreadSafeMemoryPool( int blockSize, int numElements, int growMode )
{
m_ptslistFreeBlocks = new CTSListBase;
// round up to the nearest 8-byte boundary
if ( blockSize % TSLIST_NODE_ALIGNMENT != 0 )
{
blockSize += TSLIST_NODE_ALIGNMENT - (blockSize % TSLIST_NODE_ALIGNMENT);
}
Assert( blockSize % TSLIST_NODE_ALIGNMENT == 0 );
Assert( blockSize > sizeof(FreeListItem_t) );
m_nGrowMode = growMode;
m_cubBlockSize = blockSize;
m_nGrowSize = numElements;
m_cubAllocated = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Frees the memory contained in the mempool
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::~CThreadSafeMemoryPool()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
delete m_ptslistFreeBlocks;
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Clear()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
ClearNoLock();
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::ClearNoLock()
{
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
m_ptslistFreeBlocks->Detach();
m_cubAllocated = 0;
m_cBlocksInUse = 0;
m_vecBlockSets.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc()
{
return Alloc( m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc( unsigned int amount )
{
// loop attempting to get memory
// there appears to be a case where memory corruption can get this into an infinite loop
// normally 1 or 2 attempts are necessary to get a block, so if we hit 1000 we know something is wrong
int cAttempts = 1000;
while ( --cAttempts )
{
// pull first from the free list
m_threadRWLock.LockForRead();
FreeListItem_t *pFreeListItem = m_ptslistFreeBlocks->Pop();
if ( pFreeListItem )
{
m_threadRWLock.UnlockRead();
m_cBlocksInUse++;
return (void *)pFreeListItem;
}
m_threadRWLock.UnlockRead();
// no free items, add a new block
// switch from a read lock to a write lock
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// another thread may have allocated memory; try the free list again if so
if ( m_ptslistFreeBlocks->Count() > 0 )
continue;
size_t cubBlob = m_nGrowSize * m_cubBlockSize;
if ( m_nGrowMode == GROW_FAST )
{
cubBlob *= (m_vecBlockSets.Count() + 1);
}
// don't grow if we're told not to
if ( m_nGrowMode == GROW_NONE && m_vecBlockSets.Count() == 1 )
return NULL;
// allocate, but we can fail
byte *pBlobBase = (byte *)MemAlloc_AllocAligned( cubBlob, TSLIST_NODE_ALIGNMENT /*, (m_nGrowMode == GROW_TIL_YOU_CANT)*/ );
if ( !pBlobBase )
return NULL;
byte *pBlobEnd = pBlobBase + cubBlob;
// add all the items to the pool
for ( byte *pBlob = pBlobBase; pBlob < pBlobEnd; pBlob += m_cubBlockSize )
{
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pBlob );
}
m_cubAllocated += cubBlob;
BlockSet_t blockSet = { pBlobBase, cubBlob };
m_vecBlockSets.AddToTail( blockSet );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem )
{
Free( pMem, m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem, int cubAlloc )
{
m_threadRWLock.LockForRead();
// push the item back onto the free list
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pMem );
m_cBlocksInUse--;
m_threadRWLock.UnlockRead();
// if we're completely free, and have too much memory allocated, free some
if ( m_cBlocksInUse == 0
&& m_cubAllocated >= k_cubBytesAllocatedToConsiderFreeingMemory
&& m_vecBlockSets.Count() >= k_cBlocksAllocatedToConsiderFreeingMemory )
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// check again nothing is in use
if ( m_cBlocksInUse == 0 )
{
// free all the blocks
ClearNoLock();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: display
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::PrintStats()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
int cBlocksInUse = m_cBlocksInUse;
Msg( "Block size: %-11s Alloc'd: %8d Num blobs: %5d (%s)\n", Q_pretifymem( m_cubBlockSize, 2, true ),
cBlocksInUse, m_vecBlockSets.Count(), Q_pretifymem( m_cubAllocated, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubTotalSize()
{
return m_cubAllocated;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubSizeInUse()
{
return m_cBlocksInUse * m_cubBlockSize;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
int CThreadSafeMemoryPool::Count()
{
return m_cBlocksInUse;
}

View File

@@ -0,0 +1,166 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMEMPOOL_H
#define TSMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#undef new
//-----------------------------------------------------------------------------
// Purpose: Optimized pool memory allocator
//-----------------------------------------------------------------------------
class CThreadSafeMemoryPool
{
public:
enum
{
GROW_NONE=0, // Don't allow new blobs.
GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates get larger and larger each time it allocates one).
GROW_SLOW=2, // New blob size is numElements.
GROW_TIL_YOU_CANT=3 // GROW_SLOW til alloc fails - then STOP and dont assert!
};
CThreadSafeMemoryPool( int blockSize, int numElements, int growMode = GROW_FAST );
~CThreadSafeMemoryPool();
void *Alloc(); // Allocate the element size you specified in the constructor.
void *Alloc( unsigned int cubAlloc );
void Free( void *pMem );
void Free( void *pMem, int cubAlloc );
// Frees everything
void Clear();
// display
void PrintStats();
size_t CubTotalSize();
size_t CubSizeInUse();
int Count();
static void * operator new( size_t size )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAlignedFileLine( size, 8, __FILE__, __LINE__
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAlignedFileLine( size, 8, pFileName, nLine
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void operator delete( void *p)
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
private:
// These ain't gonna work
static void * operator new[] ( size_t size );
static void operator delete [] ( void *p);
// CThreadSpinRWLock needs 8 byte alignment to work but we new() CThreadSafeMemoryPool
// so simply place it at the start of the class to make sure it fits on the 8-byte boundary
CThreadSpinRWLock m_threadRWLock;
int m_nGrowMode;
int m_cubBlockSize;
int m_nGrowSize;
void ClearNoLock();
CInterlockedInt m_cBlocksInUse;
size_t m_cubAllocated;
struct BlockSet_t
{
byte *m_pubBlockSet;
size_t m_cubAllocated;
};
CUtlVector<BlockSet_t> m_vecBlockSets;
class CTSListBase *m_ptslistFreeBlocks;
};
//-----------------------------------------------------------------------------
// Wrapper macro to make an allocator that returns particular typed allocations
// and construction and destruction of objects.
//-----------------------------------------------------------------------------
template< class T >
class CThreadSafeClassMemoryPool : public CThreadSafeMemoryPool
{
public:
CThreadSafeClassMemoryPool(int numElements, int growMode = GROW_FAST) :
CThreadSafeMemoryPool( sizeof(T), numElements, growMode ) {}
T* Alloc();
void Free( T *pMem );
};
template< class T >
T* CThreadSafeClassMemoryPool<T>::Alloc()
{
T *pRet = (T*)CThreadSafeMemoryPool::Alloc();
if ( pRet )
{
Construct( pRet );
}
return pRet;
}
template< class T >
void CThreadSafeClassMemoryPool<T>::Free(T *pMem)
{
if ( pMem )
{
Destruct( pMem );
}
CThreadSafeMemoryPool::Free( pMem );
}
#endif // TSMEMPOOL_H

View File

@@ -0,0 +1,351 @@
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <stdafx.h>
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const int k_cubMemBlockPrefixSize = sizeof(uint32);
#define ALLOCSIZE_TO_LOOKUP( cubAlloc ) ( (cubAlloc - 1) >> 5 )
#define LOOKUP_TO_ALLOCSIZE( iLookup ) ( (iLookup << 5) + 1 )
//-----------------------------------------------------------------------------
// Purpose: constructor, the sizes in pMemPoolConfig must be in ascending order
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pMemPoolConfig, int cnMemPoolConfig, int nGrowMode /*= GROW_FAST*/ )
{
m_cubReallocedTotal = 0;
m_MapRawAllocation.SetLessFunc( DefLessFunc( void * ) );
for ( int iMemPoolConfig = 0; iMemPoolConfig < cnMemPoolConfig; iMemPoolConfig++ )
{
MemPoolRecord_t memPoolRecord;
// verify that the mem pool sizes are in ascending order
Assert( iMemPoolConfig == 0 || ( iMemPoolConfig > 0 && pMemPoolConfig[ iMemPoolConfig - 1 ].m_cubBlockSize < pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize ) );
AssertMsg( pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize % 32 == 0, "Mempools sizes must be multiples of 32" );
// add an int to the block size so we can note the alloc size
memPoolRecord.m_pMemPool = new CThreadSafeMemoryPool( pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize + k_cubMemBlockPrefixSize,
pMemPoolConfig[ iMemPoolConfig ].m_cubDefaultPoolSize, nGrowMode );
Assert( memPoolRecord.m_pMemPool );
memPoolRecord.m_nBlockSize = pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize;
m_VecMemPool.AddToTail( memPoolRecord );
// update the largest blocksize
m_nBlockSizeMax = MAX( m_nBlockSizeMax, memPoolRecord.m_nBlockSize );
}
// build the lookup table
int nLookupMax = m_nBlockSizeMax >> 5;
m_VecMemPoolLookup.AddMultipleToTail( nLookupMax );
for ( int i = 0; i < nLookupMax; i++ )
{
uint32 cubAllocSize = LOOKUP_TO_ALLOCSIZE( i );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( m_VecMemPool[iMemPool].m_nBlockSize >= cubAllocSize )
{
m_VecMemPoolLookup[i] = &m_VecMemPool[iMemPool];
break;
}
}
}
#if defined(_DEBUG)
// validate the lookup table
for ( int i = 1; i < (int)m_nBlockSizeMax; i++ )
{
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( (int)m_VecMemPool[iMemPool].m_nBlockSize >= i )
{
AssertMsg( m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP(i)] == &m_VecMemPool[iMemPool], "Invalid mempool block size, can't generate lookup table" );
break;
}
}
}
#endif // _DEBUG
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::~CThreadSafeMultiMemoryPool()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool ++ )
{
delete m_VecMemPool[iMemPool].m_pMemPool;
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a block of memory at of least nAllocSize bytes
// Input : nAllocSize - number of bytes to alloc
// Output : pointer to memory alloc'd, NULL on error
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::Alloc( uint32 cubAllocSize )
{
if ( cubAllocSize == 0 )
return NULL;
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
void *pvMem = pMemPoolRecord->m_pMemPool->Alloc( cubAllocSize + k_cubMemBlockPrefixSize );
*(uint32 *)pvMem = cubAllocSize;
return ( (char *)pvMem + k_cubMemBlockPrefixSize );
}
// can't fit in our mem pools, alloc it in our one off buffer
RawAllocation_t rawAllocation;
rawAllocation.m_nBlockSize = cubAllocSize;
rawAllocation.m_pvMem = PvAlloc( cubAllocSize + k_cubMemBlockPrefixSize );
if ( !rawAllocation.m_pvMem )
{
return NULL;
}
*(uint32 *)rawAllocation.m_pvMem = rawAllocation.m_nBlockSize;
AUTO_LOCK( m_mutexRawAllocations );
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
return ( (char *)rawAllocation.m_pvMem + k_cubMemBlockPrefixSize );
}
//-----------------------------------------------------------------------------
// Purpose: Free a previously alloc'd block
// Input : pMem - memory to free
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Free( void *pvMem )
{
if ( !pvMem )
return;
uint32 cubAllocSize = *( (uint32 *)pvMem - 1 );
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
pMemPoolRecord->m_pMemPool->Free( (char *)pvMem - k_cubMemBlockPrefixSize, cubAllocSize + k_cubMemBlockPrefixSize );
return;
}
AUTO_LOCK( m_mutexRawAllocations );
// must have been alloc'd from the raw heap, find it in map
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::Free: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubAllocSize );
return;
}
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
m_MapRawAllocation.RemoveAt( iRawAllocation);
}
//-----------------------------------------------------------------------------
// Purpose: Return the size alloc'd for this block
// Input : pMem - memory to report
// Output : size in bytes of this memory
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CubAllocSize(void *pvMem)
{
if ( !pvMem )
{
return -1;
}
return *(((uint32 *)pvMem) -1);
}
//-----------------------------------------------------------------------------
// Purpose: Frees all previously alloc'd memory
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Clear()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->Clear();
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
m_MapRawAllocation.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: print to the console info about our storage
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::PrintStats()
{
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->PrintStats();
}
int cubRawBytesAllocd = 0;
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
Msg( "Raw bytes alloc'd: %s\n", Q_pretifymem( cubRawBytesAllocd, 2, true ) );
Msg( "Cumulative bytes re-alloced: %s\n", Q_pretifymem( m_cubReallocedTotal, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSize() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubTotalSize() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSizeInUse() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubSizeInUse() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return number of mempool blocks alloc'd
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::Count()
{
int cCount = 0;
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cCount += m_VecMemPool[iMemPool].m_pMemPool->Count();
}
return cCount;
}
//-----------------------------------------------------------------------------
// Purpose: reallocate an existing block of memory to a new size (and copy the data
// Input: pvMem - a pointer to the existing memory
// cubAlloc - number of bytes to alloc
// Output: returns a pointer to the memory allocated (NULL on error)
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::ReAlloc( void *pvMem, uint32 cubAlloc )
{
uint32 cubOldAlloc = CubAllocSize(pvMem);
if ( pvMem && cubAlloc <= cubOldAlloc )
return pvMem;
if ( cubOldAlloc > m_nBlockSizeMax )
{
AUTO_LOCK( m_mutexRawAllocations );
// okay, must have been alloc'd from the raw heap, search for it
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::ReAlloc: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubOldAlloc );
return NULL;
}
// realloc the memory
void *pvNewMem = PvRealloc( pvAllocedMem, cubAlloc + k_cubMemBlockPrefixSize );
if ( !pvNewMem )
{
m_MapRawAllocation.RemoveAt( iRawAllocation );
return NULL;
}
// update our tracking
*(uint32 *)pvNewMem = cubAlloc;
if ( pvAllocedMem == pvNewMem )
{
// if pointer is the same, use the same map entry with the same key (the pointer given to caller)
m_MapRawAllocation[iRawAllocation].m_pvMem = pvNewMem;
m_MapRawAllocation[iRawAllocation].m_nBlockSize = cubAlloc;
}
else
{
// if pointer changed, need to remove the old entry and re-insert with new key
m_MapRawAllocation.RemoveAt( iRawAllocation );
RawAllocation_t rawAllocation;
rawAllocation.m_pvMem = pvNewMem;
rawAllocation.m_nBlockSize = cubAlloc;
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
}
return ( (char *)pvNewMem + k_cubMemBlockPrefixSize );
}
else
{
// see if we can stay in the same block
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubOldAlloc )];
if ( cubAlloc <= pMemPoolRecord->m_nBlockSize )
{
// re-assign the size
*((uint32 *)pvMem - 1) = cubAlloc;
return pvMem;
}
void *pvNewMem = Alloc( cubAlloc );
if ( !pvNewMem )
{
return NULL;
}
m_cubReallocedTotal += cubOldAlloc;
Q_memcpy( pvNewMem, pvMem, cubOldAlloc );
Free( pvMem ); // now free the old memory buffer we had
return pvNewMem;
}
}

View File

@@ -0,0 +1,93 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMULTIMEMPOOL_H
#define TSMULTIMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmap.h"
#include "tier1/mempool.h"
#include "tier1/tsmempool.h"
//-----------------------------------------------------------------------------
// Purpose: A container of a range of mem pool sizes (for network buffers for example)
// and a raw alloc capability (for sizes greater than any contained mem pool).
//-----------------------------------------------------------------------------
class CThreadSafeMultiMemoryPool
{
public:
struct MemPoolConfig_t
{
uint32 m_cubBlockSize;
uint32 m_cubDefaultPoolSize;
};
CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pnBlock, int cnMemPoolConfig, int nGrowMode = CUtlMemoryPool::GROW_FAST );
~CThreadSafeMultiMemoryPool();
// Allocate a block of at least nAllocSize bytes
void* Alloc( uint32 cubAlloc );
// Free a previously alloc'd block
void Free(void *pvMem);
// ReAllocate a previously allocated block to a new size
void* ReAlloc( void *pvMem, uint32 cubAlloc );
// Frees everything
void Clear();
// alloc size for this bit of alloc'd memory
int CubAllocSize( void *pvMem );
// prints details about our contained memory
void PrintStats();
// total number of alloc'd elements
int Count();
// Return the total size in MB allocated for this pool
int CMBPoolSize() const;
// Return the amount of memory in use
int CMBPoolSizeInUse() const;
private:
struct MemPoolRecord_t
{
CThreadSafeMemoryPool *m_pMemPool;
uint32 m_nBlockSize;
};
CUtlVector<MemPoolRecord_t> m_VecMemPool; // stores our list of mem pools
uint32 m_nBlockSizeMax;
CUtlVector<MemPoolRecord_t *> m_VecMemPoolLookup; // quick lookup table of mempools
struct RawAllocation_t
{
void *m_pvMem;
uint32 m_nBlockSize;
};
CUtlMap<void *,RawAllocation_t,int> m_MapRawAllocation; // stores our list of raw alloc'd mem
CThreadFastMutex m_mutexRawAllocations;
uint32 m_cubReallocedTotal;
};
#endif // TSMULTIMEMPOOL_H

View File

@@ -0,0 +1,693 @@
//========= 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 "murmurhash3.h"
// fast mod for power of 2 numbers
namespace basetypes
{
template <class T>
inline bool IsPowerOf2(T n)
{
return n > 0 && (n & (n-1)) == 0;
}
template <class T1, class T2>
inline T2 ModPowerOf2(T1 a, T2 b)
{
return T2(a) & (b-1);
}
}
// default comparison operator
template <typename T>
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 <typename T>
class CDefEquals<T*>
{
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<CUtlString>
{
typedef uint32 TargetType ;
TargetType operator()(const CUtlString &strKey) const
{
return MurmurHash3Functor<const char*>()( 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<const char*>()( 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 K, typename T, typename L = CDefEquals<K>, typename H = MurmurHash3Functor<K> >
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<K,T,L,H> &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<HashBucket_t> m_vecHashBuckets;
CLargeVarBitVec m_bitsMigratedBuckets;
struct Node_t
{
KeyType_t m_key;
ElemType_t m_elem;
int m_iNextNode;
};
CUtlMemory<Node_t> 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 <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline bool CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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 <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::PurgeAndDeleteElements()
{
FOR_EACH_MAP_FAST( *this, i )
{
delete this->Element( i );
}
Purge();
}
//-----------------------------------------------------------------------------
// Purpose: rehashes buckets
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::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

View File

@@ -0,0 +1,14 @@
//========= Copyright Valve Corporation, All rights reserved. =================//
//
// Purpose: Larger string builder functions go here.
//
//=============================================================================//
#include <stdafx.h>
#include "tier0/t0constants.h"
#include "utlstringbuilder.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

View File

@@ -0,0 +1,44 @@
//====== Copyright <20> Valve Corporation, All rights reserved. ==================
//
// Purpose: String class thats more suited to frequent modification/appends
// than CUtlString. Copied from Steam's tier1 utlstring.h instead of
// a full utlstring.h merge because the files differed nearly 100%.
//
//=============================================================================
#ifndef UTLSTRINGBUILDER_H
#define UTLSTRINGBUILDER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmemory.h"
#include "tier1/strtools.h"
#include "tier1/utlstring.h"
#if 0
#include "limits.h"
#include "tier1/utlbinaryblock.h"
#endif
//-----------------------------------------------------------------------------
// Data and memory validation
//-----------------------------------------------------------------------------
#ifdef DBGFLAG_VALIDATE
inline void CUtlStringBuilder::Validate( CValidator &validator, const char *pchName )
{
#ifdef _WIN32
validator.Push( typeid(*this).raw_name(), this, pchName );
#else
validator.Push( typeid(*this).name(), this, pchName );
#endif
if ( m_data.IsHeap() )
validator.ClaimMemory( Access() );
validator.Pop();
}
#endif // DBGFLAG_VALIDATE
#endif // UTLSTRINGBUILDER_H