summaryrefslogtreecommitdiff
path: root/gcsdk/steamextra/tier1
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gcsdk/steamextra/tier1
downloadarchived-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.cpp35
-rw-r--r--gcsdk/steamextra/tier1/murmurhash3.cpp74
-rw-r--r--gcsdk/steamextra/tier1/murmurhash3.h100
-rw-r--r--gcsdk/steamextra/tier1/pearsonshash.h268
-rw-r--r--gcsdk/steamextra/tier1/tsmempool.cpp255
-rw-r--r--gcsdk/steamextra/tier1/tsmempool.h170
-rw-r--r--gcsdk/steamextra/tier1/tsmultimempool.cpp383
-rw-r--r--gcsdk/steamextra/tier1/tsmultimempool.h97
-rw-r--r--gcsdk/steamextra/tier1/utlhashmaplarge.h693
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