diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gcsdk/steamextra/tier1 | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'gcsdk/steamextra/tier1')
| -rw-r--r-- | gcsdk/steamextra/tier1/hashglobals.cpp | 35 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/murmurhash3.cpp | 74 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/murmurhash3.h | 100 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/pearsonshash.h | 268 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/tsmempool.cpp | 255 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/tsmempool.h | 170 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/tsmultimempool.cpp | 383 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/tsmultimempool.h | 97 | ||||
| -rw-r--r-- | gcsdk/steamextra/tier1/utlhashmaplarge.h | 693 |
9 files changed, 2075 insertions, 0 deletions
diff --git a/gcsdk/steamextra/tier1/hashglobals.cpp b/gcsdk/steamextra/tier1/hashglobals.cpp new file mode 100644 index 0000000..775e1da --- /dev/null +++ b/gcsdk/steamextra/tier1/hashglobals.cpp @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + + +#include "stdafx.h" +#include "pearsonshash.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// This is a table of the values 0-255 in pseudo random order for use in our pearsons hash +// variant implemented below +const unsigned char g_CTHashRandomValues[256] = +{ + 131, 184, 146, 42, 124, 142, 226, 76, 8, 135, 215, 116, 228, 49, 144, 231, + 238, 25, 156, 125, 128, 87, 223, 38, 98, 122, 105, 4, 35, 180, 188, 160, + 179, 59, 218, 0, 192, 207, 209, 150, 227, 143, 140, 161, 73, 84, 111, 162, + 239, 74, 210, 195, 15, 225, 104, 221, 245, 12, 72, 47, 109, 187, 40, 178, + 208, 56, 190, 193, 126, 95, 33, 45, 177, 170, 186, 123, 202, 149, 60, 194, + 168, 102, 71, 148, 46, 121, 52, 119, 196, 247, 127, 145, 75, 79, 61, 254, + 9, 44, 23, 211, 18, 175, 251, 130, 203, 108, 85, 167, 29, 250, 138, 182, + 101, 213, 159, 92, 36, 10, 86, 32, 176, 80, 17, 134, 181, 114, 64, 165, + 89, 68, 6, 14, 205, 137, 117, 7, 39, 132, 26, 19, 214, 99, 166, 163, + 69, 174, 157, 100, 201, 118, 2, 28, 235, 236, 139, 244, 70, 20, 155, 82, + 51, 154, 115, 94, 93, 83, 136, 27, 198, 43, 50, 243, 183, 153, 53, 206, + 77, 55, 57, 3, 220, 147, 253, 110, 37, 246, 97, 13, 120, 103, 91, 169, + 58, 11, 133, 22, 152, 189, 222, 151, 141, 88, 224, 1, 48, 191, 249, 173, + 106, 113, 252, 172, 232, 66, 219, 96, 237, 21, 233, 62, 242, 54, 230, 65, + 78, 248, 16, 41, 31, 200, 90, 112, 255, 171, 164, 24, 199, 81, 212, 197, + 185, 67, 5, 234, 30, 129, 216, 63, 204, 158, 217, 229, 107, 240, 241, 34, +}; diff --git a/gcsdk/steamextra/tier1/murmurhash3.cpp b/gcsdk/steamextra/tier1/murmurhash3.cpp new file mode 100644 index 0000000..72fd8aa --- /dev/null +++ b/gcsdk/steamextra/tier1/murmurhash3.cpp @@ -0,0 +1,74 @@ +//======= Copyright � Valve Corporation, All rights reserved. ================= +// +// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose +// hash with a 32-bit output. References: +// http://code.google.com/p/smhasher/ (home of MurmurHash3) +// https://sites.google.com/site/murmurhash/avalanche +// http://www.strchr.com/hash_functions +// +//============================================================================= + +#include <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; +} diff --git a/gcsdk/steamextra/tier1/murmurhash3.h b/gcsdk/steamextra/tier1/murmurhash3.h new file mode 100644 index 0000000..8f477cd --- /dev/null +++ b/gcsdk/steamextra/tier1/murmurhash3.h @@ -0,0 +1,100 @@ +//======= Copyright � Valve Corporation, All rights reserved. ================= +// +// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose +// hash with a 32-bit output. References: +// http://code.google.com/p/smhasher/ (home of MurmurHash3) +// https://sites.google.com/site/murmurhash/avalanche +// http://www.strchr.com/hash_functions +// +//============================================================================= + +#ifndef MURMURHASH3_H +#define MURMURHASH3_H + +#if defined(_WIN32) +#pragma once +#endif + +uint32 MurmurHash3_32( const void *key, size_t len, uint32 seed, bool bCaselessStringVariant = false ); + +inline uint32 MurmurHash3String( const char *pszKey, size_t len ) +{ + return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, false ); +} + +inline uint32 MurmurHash3StringCaseless( const char *pszKey, size_t len ) +{ + return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, true ); +} + +inline uint32 MurmurHash3String( const char *pszKey ) +{ + return MurmurHash3String( pszKey, strlen( pszKey ) ); +} + +inline uint32 MurmurHash3StringCaseless( const char *pszKey ) +{ + return MurmurHash3StringCaseless( pszKey, strlen( pszKey ) ); +} + +template <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 diff --git a/gcsdk/steamextra/tier1/pearsonshash.h b/gcsdk/steamextra/tier1/pearsonshash.h new file mode 100644 index 0000000..29da4a5 --- /dev/null +++ b/gcsdk/steamextra/tier1/pearsonshash.h @@ -0,0 +1,268 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: PearsonsHash.h +// +// This file contains implementation definitions of 'Pearson's' Hash' +// The generic implementation is a template, plus a couple of specializations +// for commonly used types. +// +// Take a look at http://en.wikipedia.org/wiki/Pearson_hashing for more info. +// +//============================================================================= + +#ifndef _PEARSONSHASH_H_ +#define _PEARSONSHASH_H_ + +#if defined(_WIN32) || defined(_WIN64) +#pragma once +#endif + +extern const unsigned char g_CTHashRandomValues[256] ; + +template<typename T> +struct PearsonsHashFunctor +{ + typedef uint32 TargetType ; + TargetType operator()(const T& unKey) const + { + // This is a pearsons hash variant that returns a maximum of 32 bits + size_t size = sizeof(T); + const uint8 * k = (const uint8 *) &unKey; + uint32 byte_one = 0, byte_two = 0, byte_three = 0, byte_four = 0, n; + while (size) + { + --size; + n = *k++; + byte_one = g_CTHashRandomValues[byte_one ^ n]; + + if (size) + { + --size; + n = *k++; + byte_two = g_CTHashRandomValues[byte_two ^ n]; + } + else + break; + + if (size) + { + --size; + n = *k++; + byte_three = g_CTHashRandomValues[byte_three ^ n]; + } + else + break; + + if (size) + { + --size; + n = *k++; + byte_four = g_CTHashRandomValues[byte_four ^ n]; + } + else + break; + } + + TargetType idx = ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one; + return ( idx ); + } +}; + +// +// We use this specialization for pointer types - it allows somebody +// to define a specialization for some complicated type, and then use a +// pointer to that type as a key, and have that automatically go to the +// specialization for the complicated type ! +// +template<typename T> +struct PearsonsHashFunctor<T*> +{ + typedef uint32 TargetType ; + TargetType operator()(const T* key) const + { + PearsonsHashFunctor<T> functor ; + return functor(*key) ; + } +}; + +// +// This functor specializes for unsigned 32 bit integers, a commonly used type in Steam. +// It should return the exact same result as the unspecialized version on Intel Architecture machines. +// +template<> +struct PearsonsHashFunctor<uint32> +{ + typedef uint32 TargetType ; + TargetType operator()(const uint32 unKey) const + { + uint32 byte_one = g_CTHashRandomValues[(unKey>>0) & 0xff]; + uint32 byte_two = g_CTHashRandomValues[(unKey>>8) & 0xff]; + uint32 byte_three = g_CTHashRandomValues[(unKey>>16) & 0xff]; + uint32 byte_four = g_CTHashRandomValues[(unKey>>24)&0xff]; + return ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one; + } +}; + +// +// This functor specializes for unsigned 64 bit integers, another commonly used type in Steam. +// It should return the exact same result as the unspecialized version on Intel Architecture machines. +// +template<> +struct PearsonsHashFunctor<uint64> +{ + typedef uint32 TargetType ; + TargetType operator()(const uint64 unKey) const + { + // + // Note that we pull apart the 64 bits in Intel's endian order. + // + uint32 n; + // + // On Intel Machines, to make this return the exact same result as the generic version + // we have to go from least significant byte to most significant byte ! + // + n = static_cast<uint32>((unKey >> (0)) & 0xff) ; + uint32 byte_one = g_CTHashRandomValues[n]; + n = static_cast<uint32>((unKey >> (8)) & 0xff) ; + uint32 byte_two = g_CTHashRandomValues[n]; + n = static_cast<uint32>((unKey >> (16)) & 0xff) ; + uint32 byte_three = g_CTHashRandomValues[n]; + n = static_cast<uint32>((unKey >> (24)) & 0xff) ; + uint32 byte_four = g_CTHashRandomValues[n]; + n = static_cast<uint32>((unKey >> (32)) & 0xff) ; + byte_one = g_CTHashRandomValues[n ^ byte_one]; + n = static_cast<uint32>((unKey >> (8+32)) & 0xff) ; + byte_two = g_CTHashRandomValues[n ^ byte_two]; + n = static_cast<uint32>((unKey >> (16+32)) & 0xff) ; + byte_three = g_CTHashRandomValues[n ^ byte_three]; + n = static_cast<uint32>((unKey >> (24+32)) & 0xff) ; + byte_four = g_CTHashRandomValues[n ^ byte_four]; + return ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one; + } +}; + +// +// This functor specializes for C standard NULL terminated strings ! +// It is setup so that if you had a char array containing a NULL terminated string +// and correctly sized, ie char rgch[16] = { "123456789012345" } and a +// null terminated string i.e. char *sz = "123456789012345" these will return identical +// results, and both include the NULL terminator in the hash calculation. +// +template<> +struct PearsonsHashFunctor<char*> +{ + typedef uint32 TargetType ; + TargetType operator()(const char* szKey) const + { + const uint8 * k = (const uint8 *) szKey ; + uint32 byte_one = 0, byte_two = 0, byte_three = 0, byte_four = 0, n; + do + { + n = *k++; + byte_one = g_CTHashRandomValues[byte_one ^ n]; + if (n=='\0') + break; + + n = *k++; + byte_two = g_CTHashRandomValues[byte_two ^ n]; + if (n=='\0') + break; + + n = *k++; + byte_three = g_CTHashRandomValues[byte_three ^ n]; + if (n=='\0') + break; + + n = *k++; + byte_four = g_CTHashRandomValues[byte_four ^ n]; + } while(n!='\0') ; + + TargetType idx = ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one; + return ( idx ); + } +}; + +// +// This functor compares two objects of a particular type and returns a result +// that follows the strcmp/memcmp. +// If the type doesn't provide an operator== or operator< then you can provide +// a type specific specialization to override this defualt functor ! +// +template<typename T> +struct ComparisonFunctor +{ + int operator()(const T &lhs, const T &rhs) const + { + if( lhs == rhs ) + return 0 ; + else if( lhs < rhs ) + return -1 ; + else + return 1 ; + } + +}; + +// +// I expect people to build Comparison specializations for full types, +// not pointer types - this Specialization should allow the C++ compiler +// to bind to a specialization for a particular type. +// We do not want to compare pointers for equality, and a memcmp() may +// not be good for a complicated type ! +// +template<typename T> +struct ComparisonFunctor<T*> +{ + int operator()(const T *lhs, const T *rhs) const + { + ComparisonFunctor<T> functor ; + return functor(*lhs, *rhs) ; + } +}; + +template<> +struct ComparisonFunctor<int> +{ + int operator()(const int lhs, const int rhs) const + { + return lhs-rhs ; + } +}; + +template<> +struct ComparisonFunctor<unsigned int> +{ + int operator()(const unsigned int lhs, const unsigned int rhs) const + { + return static_cast<int>(lhs) - static_cast<int>(rhs) ; + } +}; + + +template<> +struct ComparisonFunctor<uint64> +{ + int operator()(const uint64 lhs, const uint64 rhs) const + { + if( lhs < rhs ) + return -1 ; + else if( lhs == rhs ) + return 0 ; + else + return 1 ; + } +}; + +template<> +struct ComparisonFunctor<char*> +{ + int operator()(const char * lhs, const char* rhs) const + { + return Q_strcmp(lhs, rhs) ; + } +}; + + +#endif // _PEARSONSHASH_H_ + + diff --git a/gcsdk/steamextra/tier1/tsmempool.cpp b/gcsdk/steamextra/tier1/tsmempool.cpp new file mode 100644 index 0000000..71b0f5b --- /dev/null +++ b/gcsdk/steamextra/tier1/tsmempool.cpp @@ -0,0 +1,255 @@ +//========= Copyright Valve Corporation, 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; +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: Run a global validation pass on all of our data structures and memory +// allocations. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::Validate( CValidator &validator, const char *pchName ) +{ + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + VALIDATE_SCOPE(); + FOR_EACH_VEC( m_vecBlockSets, i ) + { + validator.ClaimMemory( MemAlloc_Unalign( m_vecBlockSets[i].m_pubBlockSet ) ); + } + ValidateObj( m_vecBlockSets ); + validator.ClaimMemory( MemAlloc_Unalign( m_ptslistFreeBlocks ) ); +} +#endif // DBGFLAG_VALIDATE diff --git a/gcsdk/steamextra/tier1/tsmempool.h b/gcsdk/steamextra/tier1/tsmempool.h new file mode 100644 index 0000000..402681c --- /dev/null +++ b/gcsdk/steamextra/tier1/tsmempool.h @@ -0,0 +1,170 @@ +//========= Copyright Valve Corporation, 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_AllocAligned( 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_AllocAligned( 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 + ); + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +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 diff --git a/gcsdk/steamextra/tier1/tsmultimempool.cpp b/gcsdk/steamextra/tier1/tsmultimempool.cpp new file mode 100644 index 0000000..86a8b50 --- /dev/null +++ b/gcsdk/steamextra/tier1/tsmultimempool.cpp @@ -0,0 +1,383 @@ +//========= Copyright 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; + } +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: Ensure that all of our internal structures are consistent, and +// account for all memory that we've allocated. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +void CThreadSafeMultiMemoryPool::Validate( CValidator &validator, const char *pchName ) +{ + validator.Push( "CThreadSafeMultiMemoryPool", this, pchName ); + + ValidateObj( m_VecMemPool ); + for( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ ) + { + validator.ClaimMemory_Aligned( m_VecMemPool[iMemPool].m_pMemPool ); + m_VecMemPool[iMemPool].m_pMemPool->Validate( validator, "m_VecMemPool[iMemPool].m_pMemPool" ); + } + + AUTO_LOCK( m_mutexRawAllocations ); + ValidateObj( m_MapRawAllocation ); + FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation ) + { + validator.ClaimMemory( m_MapRawAllocation[iRawAllocation].m_pvMem ); + } + + ValidateObj( m_VecMemPoolLookup ); + + validator.Pop(); +} +#endif // DBGFLAG_VALIDATE + diff --git a/gcsdk/steamextra/tier1/tsmultimempool.h b/gcsdk/steamextra/tier1/tsmultimempool.h new file mode 100644 index 0000000..860e9df --- /dev/null +++ b/gcsdk/steamextra/tier1/tsmultimempool.h @@ -0,0 +1,97 @@ +//========= Copyright Valve Corporation, 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 = UTLMEMORYPOOL_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; + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +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 diff --git a/gcsdk/steamextra/tier1/utlhashmaplarge.h b/gcsdk/steamextra/tier1/utlhashmaplarge.h new file mode 100644 index 0000000..5525930 --- /dev/null +++ b/gcsdk/steamextra/tier1/utlhashmaplarge.h @@ -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 "tier1/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: + // 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 |