initial
This commit is contained in:
74
gcsdk/steamextra/tier1/murmurhash3.cpp
Normal file
74
gcsdk/steamextra/tier1/murmurhash3.cpp
Normal 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;
|
||||
}
|
||||
100
gcsdk/steamextra/tier1/murmurhash3.h
Normal file
100
gcsdk/steamextra/tier1/murmurhash3.h
Normal 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
|
||||
234
gcsdk/steamextra/tier1/tsmempool.cpp
Normal file
234
gcsdk/steamextra/tier1/tsmempool.cpp
Normal 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;
|
||||
}
|
||||
166
gcsdk/steamextra/tier1/tsmempool.h
Normal file
166
gcsdk/steamextra/tier1/tsmempool.h
Normal 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
|
||||
351
gcsdk/steamextra/tier1/tsmultimempool.cpp
Normal file
351
gcsdk/steamextra/tier1/tsmultimempool.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
93
gcsdk/steamextra/tier1/tsmultimempool.h
Normal file
93
gcsdk/steamextra/tier1/tsmultimempool.h
Normal 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
|
||||
693
gcsdk/steamextra/tier1/utlhashmaplarge.h
Normal file
693
gcsdk/steamextra/tier1/utlhashmaplarge.h
Normal 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
|
||||
14
gcsdk/steamextra/tier1/utlstringbuilder.cpp
Normal file
14
gcsdk/steamextra/tier1/utlstringbuilder.cpp
Normal 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"
|
||||
|
||||
|
||||
44
gcsdk/steamextra/tier1/utlstringbuilder.h
Normal file
44
gcsdk/steamextra/tier1/utlstringbuilder.h
Normal 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
|
||||
Reference in New Issue
Block a user