From 20d1fabd0c1b523e685a645848855fce702bcbac Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Thu, 4 Aug 2022 01:28:07 +0200 Subject: [PATCH] Implement CUtlMemory, CUtlBlockMemory and CUtlVector CUtlMemory has been modified to fit the size of the in-engine structure. The new types seem to be either int64 or unsigned (size_t?). --- r5dev/tier0/memalloc.h | 8 + r5dev/tier0/platform.h | 239 ++++ r5dev/tier1/IConVar.cpp | 1 + r5dev/tier1/splitstring.cpp | 103 ++ r5dev/tier1/utlblockmemory.h | 349 ++++++ r5dev/tier1/utlmemory.h | 1127 ++++++++++++++++++- r5dev/tier1/utlvector.h | 1465 ++++++++++++++++++++++++- r5dev/vproj/clientsdk.vcxproj | 3 + r5dev/vproj/clientsdk.vcxproj.filters | 9 + r5dev/vproj/dedicated.vcxproj | 3 + r5dev/vproj/dedicated.vcxproj.filters | 9 + r5dev/vproj/gamesdk.vcxproj | 3 + r5dev/vproj/gamesdk.vcxproj.filters | 9 + 13 files changed, 3317 insertions(+), 11 deletions(-) create mode 100644 r5dev/tier0/memalloc.h create mode 100644 r5dev/tier1/splitstring.cpp create mode 100644 r5dev/tier1/utlblockmemory.h diff --git a/r5dev/tier0/memalloc.h b/r5dev/tier0/memalloc.h new file mode 100644 index 00000000..a23e2e39 --- /dev/null +++ b/r5dev/tier0/memalloc.h @@ -0,0 +1,8 @@ +#ifndef TIER0_MEMALLOC_H +#define TIER0_MEMALLOC_H + +#define MEM_ALLOC_CREDIT_CLASS() +#define MEM_ALLOC_CLASSNAME(type) NULL +#define MEM_ALLOC_CREDIT_FUNCTION() + +#endif /* TIER0_MEMALLOC_H */ \ No newline at end of file diff --git a/r5dev/tier0/platform.h b/r5dev/tier0/platform.h index 0b6d9730..d0f32118 100644 --- a/r5dev/tier0/platform.h +++ b/r5dev/tier0/platform.h @@ -149,6 +149,30 @@ #endif // CROSS_PLATFORM_VERSION < 2 + +//----------------------------------------------------------------------------- +// Set up build configuration defines. +//----------------------------------------------------------------------------- +#ifdef _CERT +#define IsCert() 1 +#else +#define IsCert() 0 +#endif + +#ifdef _DEBUG +#define IsRelease() 0 +#define IsDebug() 1 +#else +#define IsRelease() 1 +#define IsDebug() 0 +#endif + +#ifdef _RETAIL +#define IsRetail() 1 +#else +#define IsRetail() 0 +#endif + #if defined( GNUC ) && !defined( COMPILER_PS3 ) // use pre-align on PS3 // gnuc has the align decoration at the end #define ALIGN4 @@ -156,6 +180,7 @@ #define ALIGN16 #define ALIGN32 #define ALIGN128 +#define ALIGN_N( _align_ ) #undef ALIGN16_POST #define ALIGN4_POST DECL_ALIGN(4) @@ -163,6 +188,7 @@ #define ALIGN16_POST DECL_ALIGN(16) #define ALIGN32_POST DECL_ALIGN(32) #define ALIGN128_POST DECL_ALIGN(128) +#define ALIGN_N_POST( _align_ ) DECL_ALIGN( _align_ ) #else // MSVC has the align at the start of the struct // PS3 SNC supports both @@ -171,14 +197,110 @@ #define ALIGN16 DECL_ALIGN(16) #define ALIGN32 DECL_ALIGN(32) #define ALIGN128 DECL_ALIGN(128) +#define ALIGN_N( _align_ ) DECL_ALIGN( _align_ ) #define ALIGN4_POST #define ALIGN8_POST #define ALIGN16_POST #define ALIGN32_POST #define ALIGN128_POST +#define ALIGN_N_POST( _align_ ) #endif +// !!! NOTE: if you get a compile error here, you are using VALIGNOF on an abstract type :NOTE !!! +#define VALIGNOF_PORTABLE( type ) ( sizeof( AlignOf_t ) - sizeof( type ) ) + +#if defined( COMPILER_GCC ) || defined( COMPILER_MSVC ) +#define VALIGNOF( type ) __alignof( type ) +#define VALIGNOF_TEMPLATE_SAFE( type ) VALIGNOF_PORTABLE( type ) +#else +#error "PORT: Code only tested with MSVC! Must validate with new compiler, and use built-in keyword if available." +#endif + +// Use ValidateAlignment to sanity-check alignment usage when allocating arrays of an aligned type +#define ALIGN_ASSERT( pred ) { COMPILE_TIME_ASSERT( pred ); } +template< class T, int ALIGN > +inline void ValidateAlignmentExplicit(void) +{ + // Alignment must be a power of two + ALIGN_ASSERT((ALIGN & (ALIGN - 1)) == 0); + // Alignment must not imply gaps in the array (which the CUtlMemory pattern does not allow for) + ALIGN_ASSERT(ALIGN <= sizeof(T)); + // Alignment must be a multiple of the size of the object type, or elements will *NOT* be aligned! + ALIGN_ASSERT((sizeof(T) % ALIGN) == 0); + // Alignment should be a multiple of the base alignment of T +// ALIGN_ASSERT((ALIGN % VALIGNOF(T)) == 0); +} +template< class T > inline void ValidateAlignment(void) { ValidateAlignmentExplicit(); } + +// Portable alternative to __alignof +template struct AlignOf_t { AlignOf_t() {} AlignOf_t& operator=(const AlignOf_t&) { return *this; } byte b; T t; }; + +template < size_t NUM, class T, int ALIGN > struct AlignedByteArrayExplicit_t {}; +template < size_t NUM, class T > struct AlignedByteArray_t : public AlignedByteArrayExplicit_t< NUM, T, VALIGNOF_TEMPLATE_SAFE(T) > {}; + + +#if defined(MSVC) && ( defined(_DEBUG) || defined(USE_MEM_DEBUG) ) + +#pragma warning(disable:4290) +#pragma warning(push) +#include + +// MEM_DEBUG_CLASSNAME is opt-in. +// Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads +// simultaneously, it'll need a mutex. +#if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME) + +template const char* MemAllocClassName(T* p) +{ + static const char* pszName = typeid(*p).name(); // @TODO: support having debug heap ignore certain allocations, and ignore memory allocated here [5/7/2009 tom] + return pszName; +} + +#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( MemAllocClassName( this ) ) +#define MEM_ALLOC_CLASSNAME(type) (typeid((type*)(0)).name()) +#else +#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( __FILE__ ) +#define MEM_ALLOC_CLASSNAME(type) (__FILE__) +#endif + +// MEM_ALLOC_CREDIT_FUNCTION is used when no this pointer is available ( inside 'new' overloads, for example ) +#ifdef _MSC_VER +#define MEM_ALLOC_CREDIT_FUNCTION() MEM_ALLOC_CREDIT_( __FUNCTION__ ) +#else +#define MEM_ALLOC_CREDIT_FUNCTION() (__FILE__) +#endif + +#pragma warning(pop) +#else +#define MEM_ALLOC_CREDIT_CLASS() +#define MEM_ALLOC_CLASSNAME(type) NULL +#define MEM_ALLOC_CREDIT_FUNCTION() +#endif + +//----------------------------------------------------------------------------- +// Macro to assist in asserting constant invariants during compilation + +// This implementation of compile time assert has zero cost (so it can safely be +// included in release builds) and can be used at file scope or function scope. +#ifdef __GNUC__ +#define COMPILE_TIME_ASSERT( pred ) typedef int UNIQUE_ID[ (pred) ? 1 : -1 ] +#else +#if _MSC_VER >= 1600 +// If available use static_assert instead of weird language tricks. This +// leads to much more readable messages when compile time assert constraints +// are violated. +#define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) +#else +// Due to gcc bugs this can in rare cases (some template functions) cause redeclaration +// errors when used multiple times in one scope. Fix by adding extra scoping. +#define COMPILE_TIME_ASSERT( pred ) typedef char compile_time_assert_type[(pred) ? 1 : -1]; +#endif +#endif +// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at global +// scope. However the new COMPILE_TIME_ASSERT macro supports that by default. +#define ASSERT_INVARIANT( pred ) COMPILE_TIME_ASSERT( pred ) + // This can be used to declare an abstract (interface only) class. // Classes marked abstract should not be instantiated. If they are, and access violation will occur. // @@ -474,6 +596,123 @@ inline int64 CastPtrToInt64(const void* p) #endif // BUILD_AS_DLL +//----------------------------------------------------------------------------- +// C++11 helpers +//----------------------------------------------------------------------------- +#define VALVE_CPP11 1 + +#if VALVE_CPP11 +template struct C11RemoveReference { typedef T Type; }; +template struct C11RemoveReference { typedef T Type; }; +template struct C11RemoveReference { typedef T Type; }; + +template +inline typename C11RemoveReference::Type&& Move(T&& obj) +{ + return static_cast::Type&&>(obj); +} + +template +inline T&& Forward(typename C11RemoveReference::Type& obj) +{ + return static_cast(obj); +} + +template +inline T&& Forward(typename C11RemoveReference::Type&& obj) +{ + return static_cast(obj); +} +#endif + +//----------------------------------------------------------------------------- +// Methods to invoke the constructor, copy constructor, and destructor +//----------------------------------------------------------------------------- + +template +inline T* Construct(T* pMemory) +{ + return ::new(pMemory) T; +} + +template +inline T* Construct(T* pMemory, ARG1 a1) +{ + return ::new(pMemory) T(a1); +} + +template +inline T* Construct(T* pMemory, ARG1 a1, ARG2 a2) +{ + return ::new(pMemory) T(a1, a2); +} + +template +inline T* Construct(T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3) +{ + return ::new(pMemory) T(a1, a2, a3); +} + +template +inline T* Construct(T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4) +{ + return ::new(pMemory) T(a1, a2, a3, a4); +} + +template +inline T* Construct(T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5) +{ + return ::new(pMemory) T(a1, a2, a3, a4, a5); +} + +template +inline T* CopyConstruct(T* pMemory, T const& src) +{ + return ::new(pMemory) T(src); +} + +template +inline T* MoveConstruct(T* pMemory, T&& src) +{ + return ::new(pMemory) T(Move(src)); +} + +// [will] - Fixing a clang compile: unable to create a pseudo-destructor (aka a destructor that does nothing) for float __attribute__((__vector_size__(16))) +// Fixed by specializing the Destroy function to not call destructor for that type. +#if defined( __clang__ ) || defined (LINUX) + +template +inline void Destruct(T* pMemory); + +template <> +inline void Destruct(float __attribute__((__vector_size__(16)))* pMemory); + +#endif // __clang__ + +template +inline void Destruct(T* pMemory) +{ + pMemory->~T(); + +#ifdef _DEBUG + memset(pMemory, 0xDD, sizeof(T)); +#endif +} + +// [will] - Fixing a clang compile: unable to create a pseudo-destructor (aka a destructor that does nothing) for float __attribute__((__vector_size__(16))) +// Fixed by specializing the Destroy function to not call destructor for that type. +#if defined( __clang__ ) || defined (LINUX) + +template <> +inline void Destruct(float __attribute__((__vector_size__(16)))* pMemory) +{ +#ifdef _DEBUG + memset(pMemory, 0xDD, sizeof(float __attribute__((__vector_size__(16))))); +#endif +} + +#endif // __clang__ + //----------------------------------------------------------------------------- // Processor Information: //----------------------------------------------------------------------------- diff --git a/r5dev/tier1/IConVar.cpp b/r5dev/tier1/IConVar.cpp index e00f3cac..c0a8271d 100644 --- a/r5dev/tier1/IConVar.cpp +++ b/r5dev/tier1/IConVar.cpp @@ -13,6 +13,7 @@ #include "vstdlib/callback.h" #include "public/include/iconvar.h" #include "public/include/iconcommand.h" +#include //----------------------------------------------------------------------------- // Purpose: construct/allocate diff --git a/r5dev/tier1/splitstring.cpp b/r5dev/tier1/splitstring.cpp new file mode 100644 index 00000000..45698205 --- /dev/null +++ b/r5dev/tier1/splitstring.cpp @@ -0,0 +1,103 @@ +//================ Copyright (c) 1996-2009 Valve Corporation. All Rights Reserved. ================= +// +// +// +//================================================================================================== + +#include "core/stdafx.h" +#include "strtools.h" +#include "utlvector.h" + +CSplitString::CSplitString() +{ + m_szBuffer = nullptr; +} + +CSplitString::CSplitString(const char* pString, const char** pSeparators, int nSeparators) +{ + Construct(pString, pSeparators, nSeparators); +}; + +CSplitString::CSplitString(const char* pString, const char* pSeparator) +{ + Construct(pString, &pSeparator, 1); +} + +CSplitString::~CSplitString() +{ + if (m_szBuffer) + delete[] m_szBuffer; +} + +void CSplitString::Set(const char* pString, const char** pSeparators, int nSeparators) +{ + delete[] m_szBuffer; + Construct(pString, pSeparators, nSeparators); +} + +void CSplitString::Construct(const char* pString, const char** pSeparators, int nSeparators) +{ + ////////////////////////////////////////////////////////////////////////// + // make a duplicate of the original string. We'll use pieces of this duplicate to tokenize the string + // and create NULL-terminated tokens of the original string + // + int nOriginalStringLength = strlen(pString); + m_szBuffer = new char[nOriginalStringLength + 1]; + memcpy(m_szBuffer, pString, nOriginalStringLength + 1); + + this->Purge(); + const char* pCurPos = pString; + while (1) + { + int iFirstSeparator = -1; + const char* pFirstSeparator = 0; + for (int i = 0; i < nSeparators; i++) + { + const char* pTest = strstr(pCurPos, pSeparators[i]); + if (pTest && (!pFirstSeparator || pTest < pFirstSeparator)) + { + iFirstSeparator = i; + pFirstSeparator = pTest; + } + } + + if (pFirstSeparator) + { + // Split on this separator and continue on. + int separatorLen = strlen(pSeparators[iFirstSeparator]); + if (pFirstSeparator > pCurPos) + { + ////////////////////////////////////////////////////////////////////////// + /// Cut the token out of the duplicate string + char* pTokenInDuplicate = m_szBuffer + (pCurPos - pString); + int nTokenLength = pFirstSeparator - pCurPos; + //Assert(nTokenLength > 0 && !memcmp(pTokenInDuplicate, pCurPos, nTokenLength)); + pTokenInDuplicate[nTokenLength] = '\0'; + + this->AddToTail(pTokenInDuplicate /*AllocString( pCurPos, pFirstSeparator-pCurPos )*/); + } + + pCurPos = pFirstSeparator + separatorLen; + } + else + { + // Copy the rest of the string + if (int nTokenLength = strlen(pCurPos)) + { + ////////////////////////////////////////////////////////////////////////// + // There's no need to cut this token, because there's no separator after it. + // just add its copy in the buffer to the tail + char* pTokenInDuplicate = m_szBuffer + (pCurPos - pString); + //Assert(!memcmp(pTokenInDuplicate, pCurPos, nTokenLength)); + + this->AddToTail(pTokenInDuplicate/*AllocString( pCurPos, -1 )*/); + } + return; + } + } +} + +void CSplitString::PurgeAndDeleteElements() +{ + Purge(); +} diff --git a/r5dev/tier1/utlblockmemory.h b/r5dev/tier1/utlblockmemory.h new file mode 100644 index 00000000..4e79354c --- /dev/null +++ b/r5dev/tier1/utlblockmemory.h @@ -0,0 +1,349 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A growable memory class. +//===========================================================================// + +#ifndef UTLBLOCKMEMORY_H +#define UTLBLOCKMEMORY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "mathlib/mathlib.h" + +//#include "tier0/memalloc.h" +//#include "tier0/memdbgon.h" + +#pragma warning (disable:4100) +#pragma warning (disable:4514) + +//----------------------------------------------------------------------------- + +#ifdef UTBLOCKLMEMORY_TRACK +#define UTLBLOCKMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "||Sum of all UtlBlockMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#define UTLBLOCKMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "||Sum of all UtlBlockMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#else +#define UTLBLOCKMEMORY_TRACK_ALLOC() ((void)0) +#define UTLBLOCKMEMORY_TRACK_FREE() ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// The CUtlBlockMemory class: +// A growable memory class that allocates non-sequential blocks, but is indexed sequentially +//----------------------------------------------------------------------------- +template< class T, class I > +class CUtlBlockMemory +{ +public: + // constructor, destructor + CUtlBlockMemory(int nGrowSize = 0, int nInitSize = 0); + ~CUtlBlockMemory(); + + // Set the size by which the memory grows - round up to the next power of 2 + void Init(int nGrowSize = 0, int nInitSize = 0); + + // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL + T* Base() { return NULL; } + const T* Base() const { return NULL; } + + class Iterator_t + { + public: + Iterator_t(I i) : index(i) {} + I index; + + bool operator==(const Iterator_t it) const { return index == it.index; } + bool operator!=(const Iterator_t it) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } + Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } + I GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(I i, const Iterator_t& it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } + + // element access + T& operator[](I i); + const T& operator[](I i) const; + T& Element(I i); + const T& Element(I i) const; + + // Can we use this index? + bool IsIdxValid(I i) const; + static I InvalidIndex() { return (I)-1; } + + void Swap(CUtlBlockMemory< T, I >& mem); + + // Size + int NumAllocated() const; + int Count() const { return NumAllocated(); } + + // Grows memory by max(num,growsize) rounded up to the next power of 2, and returns the allocation index/ptr + void Grow(int num = 1); + + // Makes sure we've got at least this much memory + void EnsureCapacity(int num); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements + void Purge(int numElements); + +protected: + int Index(int major, int minor) const { return (major << m_nIndexShift) | minor; } + int MajorIndex(int i) const { return i >> m_nIndexShift; } + int MinorIndex(int i) const { return i & m_nIndexMask; } + void ChangeSize(int nBlocks); + int NumElementsInBlock() const { return m_nIndexMask + 1; } + + T** m_pMemory; + int m_nBlocks; + int m_nIndexMask : 27; + int m_nIndexShift : 5; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class I > +CUtlBlockMemory::CUtlBlockMemory(int nGrowSize, int nInitAllocationCount) + : m_pMemory(0), m_nBlocks(0), m_nIndexMask(0), m_nIndexShift(0) +{ + Init(nGrowSize, nInitAllocationCount); +} + +template< class T, class I > +CUtlBlockMemory::~CUtlBlockMemory() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Swap(CUtlBlockMemory< T, I >& mem) +{ + V_swap(m_pMemory, mem.m_pMemory); + V_swap(m_nBlocks, mem.m_nBlocks); + V_swap(m_nIndexMask, mem.m_nIndexMask); + V_swap(m_nIndexShift, mem.m_nIndexShift); +} + + +//----------------------------------------------------------------------------- +// Set the size by which the memory grows - round up to the next power of 2 +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Init(int nGrowSize /* = 0 */, int nInitSize /* = 0 */) +{ + Purge(); + + if (nGrowSize == 0) + { + // default grow size is smallest size s.t. c++ allocation overhead is ~6% of block size + nGrowSize = (127 + sizeof(T)) / sizeof(T); + } + nGrowSize = SmallestPowerOfTwoGreaterOrEqual(nGrowSize); + m_nIndexMask = nGrowSize - 1; + + m_nIndexShift = 0; + while (nGrowSize > 1) + { + nGrowSize >>= 1; + ++m_nIndexShift; + } + Assert(m_nIndexMask + 1 == (1 << m_nIndexShift)); + + Grow(nInitSize); +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T, class I > +inline T& CUtlBlockMemory::operator[](I i) +{ + Assert(IsIdxValid(i)); + T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::operator[](I i) const +{ + Assert(IsIdxValid(i)); + const T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline T& CUtlBlockMemory::Element(I i) +{ + Assert(IsIdxValid(i)); + T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::Element(I i) const +{ + Assert(IsIdxValid(i)); + const T* pBlock = m_pMemory[MajorIndex(i)]; + return pBlock[MinorIndex(i)]; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T, class I > +inline int CUtlBlockMemory::NumAllocated() const +{ + return m_nBlocks * NumElementsInBlock(); +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T, class I > +inline bool CUtlBlockMemory::IsIdxValid(I i) const +{ + return (i >= 0) && (MajorIndex(i) < m_nBlocks); +} + +template< class T, class I > +void CUtlBlockMemory::Grow(int num) +{ + if (num <= 0) + return; + + int nBlockSize = NumElementsInBlock(); + int nBlocks = (num + nBlockSize - 1) / nBlockSize; + + ChangeSize(m_nBlocks + nBlocks); +} + +template< class T, class I > +void CUtlBlockMemory::ChangeSize(int nBlocks) +{ + UTLBLOCKMEMORY_TRACK_FREE(); // this must stay before the recalculation of m_nBlocks, since it implicitly uses the old value + + int nBlocksOld = m_nBlocks; + m_nBlocks = nBlocks; + + UTLBLOCKMEMORY_TRACK_ALLOC(); // this must stay after the recalculation of m_nBlocks, since it implicitly uses the new value + + // free old blocks if shrinking + for (int i = m_nBlocks; i < nBlocksOld; ++i) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory[i]); + } + + if (m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)realloc(m_pMemory, m_nBlocks * sizeof(T*)); + Assert(m_pMemory); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)malloc(m_nBlocks * sizeof(T*)); + Assert(m_pMemory); + } + + if (!m_pMemory) + { + Error(eDLL_T::COMMON, "CUtlBlockMemory overflow!\n"); + } + + // allocate new blocks if growing + int nBlockSize = NumElementsInBlock(); + for (int i = nBlocksOld; i < m_nBlocks; ++i) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory[i] = (T*)malloc(nBlockSize * sizeof(T)); + Assert(m_pMemory[i]); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, class I > +inline void CUtlBlockMemory::EnsureCapacity(int num) +{ + Grow(num - NumAllocated()); +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Purge() +{ + if (!m_pMemory) + return; + + for (int i = 0; i < m_nBlocks; ++i) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory[i]); + } + m_nBlocks = 0; + + UTLBLOCKMEMORY_TRACK_FREE(); + free((void*)m_pMemory); + m_pMemory = 0; +} + +template< class T, class I > +void CUtlBlockMemory::Purge(int numElements) +{ + Assert(numElements >= 0); + + int nAllocated = NumAllocated(); + if (numElements > nAllocated) + { + // Ensure this isn't a grow request in disguise. + Assert(numElements <= nAllocated); + return; + } + + if (numElements <= 0) + { + Purge(); + return; + } + + int nBlockSize = NumElementsInBlock(); + int nBlocksOld = m_nBlocks; + int nBlocks = (numElements + nBlockSize - 1) / nBlockSize; + + // If the number of blocks is the same as the allocated number of blocks, we are done. + if (nBlocks == m_nBlocks) + return; + + ChangeSize(nBlocks); +} + +//#include "tier0/memdbgoff.h" + +#endif // UTLBLOCKMEMORY_H diff --git a/r5dev/tier1/utlmemory.h b/r5dev/tier1/utlmemory.h index b1cf3531..79a7681a 100644 --- a/r5dev/tier1/utlmemory.h +++ b/r5dev/tier1/utlmemory.h @@ -1,23 +1,1134 @@ -//============ Copyright Valve Corporation, All rights reserved. ============// +//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // // $NoKeywords: $ // // A growable memory class. -//===========================================================================// -#pragma once +//=============================================================================// -template +#ifndef UTLMEMORY_H +#define UTLMEMORY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/memstd.h" +#include "tier0/memalloc.h" +#include "mathlib/mathlib.h" +//#include "tier0/memdbgon.h" + +#pragma warning (disable:4100) +#pragma warning (disable:4514) + + +//----------------------------------------------------------------------------- + + +#ifdef UTLMEMORY_TRACK +#define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "||Sum of all UtlMemory||", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "||Sum of all UtlMemory||", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#else +#define UTLMEMORY_TRACK_ALLOC() ((void)0) +#define UTLMEMORY_TRACK_FREE() ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, class I = int64_t > class CUtlMemory { + template< class A, class B> friend class CUtlVector; + template< class A, size_t B> friend class CUtlVectorFixedGrowableCompat; public: - CUtlMemory() {}; - CUtlMemory(uintptr_t ptr) : m_pMemory(ptr) {}; + // constructor, destructor + CUtlMemory(int64_t nGrowSize = 0, int64_t nInitSize = 0); + CUtlMemory(T* pMemory, int64_t numElements); + CUtlMemory(const T* pMemory, int64_t numElements); + ~CUtlMemory(); -private: - void* m_pMemory; + CUtlMemory(const CUtlMemory&) = delete; + CUtlMemory& operator=(const CUtlMemory&) = delete; + + CUtlMemory(CUtlMemory&& moveFrom); + CUtlMemory& operator=(CUtlMemory&& moveFrom); + + // Set the size by which the memory grows + void Init(int64_t nGrowSize = 0, int64_t nInitSize = 0); + + class Iterator_t + { + public: + Iterator_t(I i) : index(i) {} + I index; + + bool operator==(const Iterator_t it) const { return index == it.index; } + bool operator!=(const Iterator_t it) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } + Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } + I GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(I i, const Iterator_t& it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } + + // element access + T& operator[](I i); + const T& operator[](I i) const; + T& Element(I i); + const T& Element(I i) const; + + // Can we use this index? + bool IsIdxValid(I i) const; + + // Specify the invalid ('null') index that we'll only return on failure + static const I INVALID_INDEX = (I)-1; // For use with COMPILE_TIME_ASSERT + static I InvalidIndex() { return INVALID_INDEX; } + + // Gets the base address (can change when adding elements!) + T* Base(); + const T* Base() const; + + // Attaches the buffer to external memory.... + void SetExternalBuffer(T* pMemory, int64_t numElements); + void SetExternalBuffer(const T* pMemory, int64_t numElements); + void AssumeMemory(T* pMemory, int64_t nSize); + T* Detach(); + void* DetachMemory(); + + // Fast swap + void Swap(CUtlMemory< T, I >& mem); + + // Switches the buffer from an external memory buffer to a reallocatable buffer + // Will copy the current contents of the external buffer to the reallocatable buffer + void ConvertToGrowableMemory(int64_t nGrowSize); + + // Size + int64_t NumAllocated() const; + int64_t Count() const; + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow(int64_t num = 1); + + // Makes sure we've got at least this much memory + void EnsureCapacity(int64_t num); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements + void Purge(int64_t numElements); + + // is the memory externally allocated? + bool IsExternallyAllocated() const; + + // is the memory read only? + bool IsReadOnly() const; + + // Set the size by which the memory grows + void SetGrowSize(int64_t size); + +protected: + void ValidateGrowSize() + { +#ifdef _X360 + if (m_nGrowSize && m_nGrowSize != EXTERNAL_BUFFER_MARKER) + { + // Max grow size at 128 bytes on XBOX + const int64_t MAX_GROW = 128; + if (m_nGrowSize * sizeof(T) > MAX_GROW) + { + m_nGrowSize = max(1, MAX_GROW / sizeof(T)); + } + } +#endif + } + + enum + { + EXTERNAL_BUFFER_MARKER = -1, + EXTERNAL_CONST_BUFFER_MARKER = -2, + }; + + T* m_pMemory; int64_t m_nAllocationCount; int64_t m_nGrowSize; }; + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, size_t SIZE, class I = int64_t > +class CUtlMemoryFixedGrowable : public CUtlMemory< T, I > +{ + typedef CUtlMemory< T, I > BaseClass; + +public: + CUtlMemoryFixedGrowable(int64_t nGrowSize = 0, int64_t nInitSize = SIZE) : BaseClass(m_pFixedMemory, SIZE) + { + Assert(nInitSize == 0 || nInitSize == SIZE); + m_nMallocGrowSize = nGrowSize; + } + + void Grow(int64_t nCount = 1) + { + if (this->IsExternallyAllocated()) + { + this->ConvertToGrowableMemory(m_nMallocGrowSize); + } + BaseClass::Grow(nCount); + } + + void EnsureCapacity(int64_t num) + { + if (CUtlMemory::m_nAllocationCount >= num) + return; + + if (this->IsExternallyAllocated()) + { + // Can't grow a buffer whose memory was externally allocated + this->ConvertToGrowableMemory(m_nMallocGrowSize); + } + + BaseClass::EnsureCapacity(num); + } + +private: + int64_t m_nMallocGrowSize; + T m_pFixedMemory[SIZE]; +}; + +//----------------------------------------------------------------------------- +// The CUtlMemoryFixed class: +// A fixed memory class +//----------------------------------------------------------------------------- +template< typename T, size_t SIZE, int64_t nAlignment = 0 > +class CUtlMemoryFixed +{ +public: + // constructor, destructor + CUtlMemoryFixed(int64_t nGrowSize = 0, int64_t nInitSize = 0) { Assert(nInitSize == 0 || nInitSize == SIZE); } + CUtlMemoryFixed(T* pMemory, int64_t numElements) { Assert(0); } + + // Can we use this index? + bool IsIdxValid(int64_t i) const { return (i >= 0) && (i < SIZE); } + + // Specify the invalid ('null') index that we'll only return on failure + static const int64_t INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT + static int64_t InvalidIndex() { return INVALID_INDEX; } + + // Gets the base address + T* Base() { if (nAlignment == 0) return (T*)(&m_Memory[0]); else return (T*)AlignValue(&m_Memory[0], nAlignment); } + const T* Base() const { if (nAlignment == 0) return (T*)(&m_Memory[0]); else return (T*)AlignValue(&m_Memory[0], nAlignment); } + + // element access + T& operator[](int64_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& operator[](int64_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& Element(int64_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& Element(int64_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + + // Attaches the buffer to external memory.... + void SetExternalBuffer(T* pMemory, int64_t numElements) { Assert(0); } + + // Size + int64_t NumAllocated() const { return SIZE; } + int64_t Count() const { return SIZE; } + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow(int64_t num = 1) { Assert(0); } + + // Makes sure we've got at least this much memory + void EnsureCapacity(int64_t num) { Assert(num <= SIZE); } + + // Memory deallocation + void Purge() {} + + // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryFixed) + void Purge(int64_t numElements) { Assert(0); } + + // is the memory externally allocated? + bool IsExternallyAllocated() const { return false; } + + // Set the size by which the memory grows + void SetGrowSize(int64_t size) {} + + class Iterator_t + { + public: + Iterator_t(int64_t i) : index(i) {} + int64_t index; + bool operator==(const Iterator_t it) const { return index == it.index; } + bool operator!=(const Iterator_t it) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } + Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } + int64_t GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(int64_t i, const Iterator_t& it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } + +private: + char m_Memory[SIZE * sizeof(T) + nAlignment]; +}; + +#ifdef _LINUX +#define REMEMBER_ALLOC_SIZE_FOR_VALGRIND 1 +#endif + +//----------------------------------------------------------------------------- +// The CUtlMemoryConservative class: +// A dynamic memory class that tries to minimize overhead (itself small, no custom grow factor) +//----------------------------------------------------------------------------- +template< typename T > +class CUtlMemoryConservative +{ + +public: + // constructor, destructor + CUtlMemoryConservative(int64_t nGrowSize = 0, int64_t nInitSize = 0) : m_pMemory(NULL) + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + m_nCurAllocSize = 0; +#endif + + } + CUtlMemoryConservative(T* pMemory, int64_t numElements) { Assert(0); } + ~CUtlMemoryConservative() { if (m_pMemory) free(m_pMemory); } + + // Can we use this index? + bool IsIdxValid(int64_t i) const { return (IsDebug()) ? (i >= 0 && i < NumAllocated()) : (i >= 0); } + static int64_t InvalidIndex() { return -1; } + + // Gets the base address + T* Base() { return m_pMemory; } + const T* Base() const { return m_pMemory; } + + // element access + T& operator[](int64_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& operator[](int64_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& Element(int64_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& Element(int64_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + + // Attaches the buffer to external memory.... + void SetExternalBuffer(T* pMemory, int64_t numElements) { Assert(0); } + + // Size + FORCEINLINE void RememberAllocSize(size_t sz) + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + m_nCurAllocSize = sz; +#endif + } + + size_t AllocSize(void) const + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + return m_nCurAllocSize; +#else + //return (m_pMemory) ? MemAllocSingleton()->GetSize(m_pMemory) : 0; // FIXME: reverse vftable and get 'GetSize' + return 0; +#endif + } + + int64_t NumAllocated() const + { + return AllocSize() / sizeof(T); + } + int64_t Count() const + { + return NumAllocated(); + } + + FORCEINLINE void ReAlloc(size_t sz) + { + m_pMemory = (T*)realloc(m_pMemory, sz); + RememberAllocSize(sz); + } + // Grows the memory, so that at least allocated + num elements are allocated + void Grow(int64_t num = 1) + { + int64_t nCurN = NumAllocated(); + ReAlloc((nCurN + num) * sizeof(T)); + } + + // Makes sure we've got at least this much memory + void EnsureCapacity(int64_t num) + { + size_t nSize = sizeof(T) * MAX(num, Count()); + ReAlloc(nSize); + } + + // Memory deallocation + void Purge() + { + free(m_pMemory); + RememberAllocSize(0); + m_pMemory = NULL; + } + + // Purge all but the given number of elements + void Purge(int64_t numElements) { ReAlloc(numElements * sizeof(T)); } + + // is the memory externally allocated? + bool IsExternallyAllocated() const { return false; } + + // Set the size by which the memory grows + void SetGrowSize(int64_t size) {} + + class Iterator_t + { + public: + Iterator_t(int64_t i, int64_t _limit) : index(i), limit(_limit) {} + int64_t index; + int64_t limit; + bool operator==(const Iterator_t it) const { return index == it.index; } + bool operator!=(const Iterator_t it) const { return index != it.index; } + }; + Iterator_t First() const { int64_t limit = NumAllocated(); return Iterator_t(limit ? 0 : InvalidIndex(), limit); } + Iterator_t Next(const Iterator_t& it) const { return Iterator_t((it.index + 1 < it.limit) ? it.index + 1 : InvalidIndex(), it.limit); } + int64_t GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(int64_t i, const Iterator_t& it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index) && (it.index < it.limit); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex(), 0); } + +private: + T* m_pMemory; +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + size_t m_nCurAllocSize; +#endif + +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class I > +CUtlMemory::CUtlMemory(int64_t nGrowSize, int64_t nInitAllocationCount) : m_pMemory(0), +m_nAllocationCount(nInitAllocationCount), m_nGrowSize(nGrowSize) +{ + ValidateGrowSize(); + Assert(nGrowSize >= 0); + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc(m_nAllocationCount * sizeof(T)); + } +} + +template< class T, class I > +CUtlMemory::CUtlMemory(T* pMemory, int64_t numElements) : m_pMemory(pMemory), +m_nAllocationCount(numElements) +{ + // Special marker indicating externally supplied modifyable memory + m_nGrowSize = EXTERNAL_BUFFER_MARKER; +} + +template< class T, class I > +CUtlMemory::CUtlMemory(const T* pMemory, int64_t numElements) : m_pMemory((T*)pMemory), +m_nAllocationCount(numElements) +{ + // Special marker indicating externally supplied modifyable memory + m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; +} + +template< class T, class I > +CUtlMemory::~CUtlMemory() +{ + Purge(); + +#ifdef _DEBUG + m_pMemory = reinterpret_cast(0xFEFEBAAD); + m_nAllocationCount = 0x7BADF00D; +#endif +} + +template< class T, class I > +CUtlMemory::CUtlMemory(CUtlMemory&& moveFrom) + : m_pMemory(moveFrom.m_pMemory) + , m_nAllocationCount(moveFrom.m_nAllocationCount) + , m_nGrowSize(moveFrom.m_nGrowSize) +{ + moveFrom.m_pMemory = nullptr; + moveFrom.m_nAllocationCount = 0; + moveFrom.m_nGrowSize = 0; +} + +template< class T, class I > +CUtlMemory& CUtlMemory::operator=(CUtlMemory&& moveFrom) +{ + // Copy member variables to locals before purge to handle self-assignment + T* pMemory = moveFrom.m_pMemory; + int64_t nAllocationCount = moveFrom.m_nAllocationCount; + int64_t nGrowSize = moveFrom.m_nGrowSize; + + moveFrom.m_pMemory = nullptr; + moveFrom.m_nAllocationCount = 0; + moveFrom.m_nGrowSize = 0; + + // If this is a self-assignment, Purge() is a no-op here + Purge(); + + m_pMemory = pMemory; + m_nAllocationCount = nAllocationCount; + m_nGrowSize = nGrowSize; + + return *this; +} + +template< class T, class I > +void CUtlMemory::Init(int64_t nGrowSize /*= 0*/, int64_t nInitSize /*= 0*/) +{ + Purge(); + + m_nGrowSize = nGrowSize; + m_nAllocationCount = nInitSize; + ValidateGrowSize(); + Assert(nGrowSize >= 0); + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc(m_nAllocationCount * sizeof(T)); + } +} + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::Swap(CUtlMemory& mem) +{ + V_swap(m_nGrowSize, mem.m_nGrowSize); + V_swap(m_pMemory, mem.m_pMemory); + V_swap(m_nAllocationCount, mem.m_nAllocationCount); +} + + +//----------------------------------------------------------------------------- +// Switches the buffer from an external memory buffer to a reallocatable buffer +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::ConvertToGrowableMemory(int64_t nGrowSize) +{ + if (!IsExternallyAllocated()) + return; + + m_nGrowSize = nGrowSize; + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + + int64_t nNumBytes = m_nAllocationCount * sizeof(T); + T* pMemory = (T*)malloc(nNumBytes); + memcpy(pMemory, m_pMemory, nNumBytes); + m_pMemory = pMemory; + } + else + { + m_pMemory = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::SetExternalBuffer(T* pMemory, int64_t numElements) +{ + // Blow away any existing allocated memory + Purge(); + + m_pMemory = pMemory; + m_nAllocationCount = numElements; + + // Indicate that we don't own the memory + m_nGrowSize = EXTERNAL_BUFFER_MARKER; +} + +template< class T, class I > +void CUtlMemory::SetExternalBuffer(const T* pMemory, int64_t numElements) +{ + // Blow away any existing allocated memory + Purge(); + + m_pMemory = const_cast(pMemory); + m_nAllocationCount = numElements; + + // Indicate that we don't own the memory + m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; +} + +template< class T, class I > +void CUtlMemory::AssumeMemory(T* pMemory, int64_t numElements) +{ + // Blow away any existing allocated memory + Purge(); + + // Simply take the pointer but don't mark us as external + m_pMemory = pMemory; + m_nAllocationCount = numElements; +} + +template< class T, class I > +void* CUtlMemory::DetachMemory() +{ + if (IsExternallyAllocated()) + return NULL; + + void* pMemory = m_pMemory; + m_pMemory = 0; + m_nAllocationCount = 0; + return pMemory; +} + +template< class T, class I > +inline T* CUtlMemory::Detach() +{ + return (T*)DetachMemory(); +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T, class I > +inline T& CUtlMemory::operator[](I i) +{ + Assert(!IsReadOnly()); + Assert(IsIdxValid(i)); + return m_pMemory[i]; +} + +template< class T, class I > +inline const T& CUtlMemory::operator[](I i) const +{ + Assert(IsIdxValid(i)); + return m_pMemory[i]; +} + +template< class T, class I > +inline T& CUtlMemory::Element(I i) +{ + Assert(!IsReadOnly()); + Assert(IsIdxValid(i)); + return m_pMemory[i]; +} + +template< class T, class I > +inline const T& CUtlMemory::Element(I i) const +{ + Assert(IsIdxValid(i)); + return m_pMemory[i]; +} + + +//----------------------------------------------------------------------------- +// is the memory externally allocated? +//----------------------------------------------------------------------------- +template< class T, class I > +bool CUtlMemory::IsExternallyAllocated() const +{ + return (m_nGrowSize < 0); +} + + +//----------------------------------------------------------------------------- +// is the memory read only? +//----------------------------------------------------------------------------- +template< class T, class I > +bool CUtlMemory::IsReadOnly() const +{ + return (m_nGrowSize == EXTERNAL_CONST_BUFFER_MARKER); +} + + +template< class T, class I > +void CUtlMemory::SetGrowSize(int64_t nSize) +{ + Assert(!IsExternallyAllocated()); + Assert(nSize >= 0); + m_nGrowSize = nSize; + ValidateGrowSize(); +} + + +//----------------------------------------------------------------------------- +// Gets the base address (can change when adding elements!) +//----------------------------------------------------------------------------- +template< class T, class I > +inline T* CUtlMemory::Base() +{ + Assert(!IsReadOnly()); + return m_pMemory; +} + +template< class T, class I > +inline const T* CUtlMemory::Base() const +{ + return m_pMemory; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T, class I > +inline int64_t CUtlMemory::NumAllocated() const +{ + return m_nAllocationCount; +} + +template< class T, class I > +inline int64_t CUtlMemory::Count() const +{ + return m_nAllocationCount; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T, class I > +inline bool CUtlMemory::IsIdxValid(I i) const +{ + // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). + // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. + long x = i; + return (x >= 0) && (x < m_nAllocationCount); +} + +//----------------------------------------------------------------------------- +// Grows the memory +//----------------------------------------------------------------------------- +inline int64_t UtlMemory_CalcNewAllocationCount(int64_t nAllocationCount, int64_t nGrowSize, int64_t nNewSize, int64_t nBytesItem) +{ + if (nGrowSize) + { + nAllocationCount = ((1 + ((nNewSize - 1) / nGrowSize)) * nGrowSize); + } + else + { + if (!nAllocationCount) + { + // Compute an allocation which is at least as big as a cache line... + nAllocationCount = (31 + nBytesItem) / nBytesItem; + // If the requested amount is larger then compute an allocation which + // is exactly the right size. Otherwise we can end up with wasted memory + // when CUtlVector::EnsureCount(n) is called. + if (nAllocationCount < nNewSize) + nAllocationCount = nNewSize; + } + + while (nAllocationCount < nNewSize) + { +#ifndef _X360 + nAllocationCount *= 2; +#else + int64_t nNewAllocationCount = (nAllocationCount * 9) / 8; // 12.5 % + if (nNewAllocationCount > nAllocationCount) + nAllocationCount = nNewAllocationCount; + else + nAllocationCount *= 2; +#endif + } + } + + return nAllocationCount; +} + +template< class T, class I > +void CUtlMemory::Grow(int64_t num) +{ + Assert(num > 0); + + if (IsExternallyAllocated()) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + // Make sure we have at least numallocated + num allocations. + // Use the grow rules specified for this memory (in m_nGrowSize) + int64_t nAllocationRequested = m_nAllocationCount + num; + + UTLMEMORY_TRACK_FREE(); + + int64_t nNewAllocationCount = UtlMemory_CalcNewAllocationCount(m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T)); + + // if m_nAllocationRequested wraps index type I, recalculate + if ((int64_t)(I)nNewAllocationCount < nAllocationRequested) + { + if ((int64_t)(I)nNewAllocationCount == 0 && (int64_t)(I)(nNewAllocationCount - 1) >= nAllocationRequested) + { + --nNewAllocationCount; // deal w/ the common case of m_nAllocationCount == MAX_USHORT + 1 + } + else + { + if ((int64_t)(I)nAllocationRequested != nAllocationRequested) + { + // we've been asked to grow memory to a size s.t. the index type can't address the requested amount of memory + Assert(0); + return; + } + while ((int64_t)(I)nNewAllocationCount < nAllocationRequested) + { + nNewAllocationCount = (nNewAllocationCount + nAllocationRequested) / 2; + } + } + } + + m_nAllocationCount = nNewAllocationCount; + + UTLMEMORY_TRACK_ALLOC(); + + if (m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc(m_pMemory, m_nAllocationCount * sizeof(T)); + Assert(m_pMemory); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc(m_nAllocationCount * sizeof(T)); + Assert(m_pMemory); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, class I > +inline void CUtlMemory::EnsureCapacity(int64_t num) +{ + if (m_nAllocationCount >= num) + return; + + if (IsExternallyAllocated()) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + m_nAllocationCount = num; + + UTLMEMORY_TRACK_ALLOC(); + + if (m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc(m_pMemory, m_nAllocationCount * sizeof(T)); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc(m_nAllocationCount * sizeof(T)); + } +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::Purge() +{ + if (!IsExternallyAllocated()) + { + if (m_pMemory) + { + UTLMEMORY_TRACK_FREE(); + free((void*)m_pMemory); + m_pMemory = 0; + } + m_nAllocationCount = 0; + } +} + +template< class T, class I > +void CUtlMemory::Purge(int64_t numElements) +{ + Assert(numElements >= 0); + + if (numElements > m_nAllocationCount) + { + // Ensure this isn't a grow request in disguise. + Assert(numElements <= m_nAllocationCount); + return; + } + + // If we have zero elements, simply do a purge: + if (numElements == 0) + { + Purge(); + return; + } + + if (IsExternallyAllocated()) + { + // Can't shrink a buffer whose memory was externally allocated, fail silently like purge + return; + } + + // If the number of elements is the same as the allocation count, we are done. + if (numElements == m_nAllocationCount) + { + return; + } + + + if (!m_pMemory) + { + // Allocation count is non zero, but memory is null. + Assert(m_pMemory); + return; + } + + UTLMEMORY_TRACK_FREE(); + + m_nAllocationCount = numElements; + + UTLMEMORY_TRACK_ALLOC(); + + // Allocation count > 0, shrink it down. + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc(m_pMemory, m_nAllocationCount * sizeof(T)); +} + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +class CUtlMemoryAligned : public CUtlMemory +{ +public: + // constructor, destructor + CUtlMemoryAligned(int64_t nGrowSize = 0, int64_t nInitSize = 0); + CUtlMemoryAligned(T* pMemory, int64_t numElements); + CUtlMemoryAligned(const T* pMemory, int64_t numElements); + ~CUtlMemoryAligned(); + + // Attaches the buffer to external memory.... + void SetExternalBuffer(T* pMemory, int64_t numElements); + void SetExternalBuffer(const T* pMemory, int64_t numElements); + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow(int64_t num = 1); + + // Makes sure we've got at least this much memory + void EnsureCapacity(int64_t num); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryAligned) + void Purge(int64_t numElements) { Assert(0); } + +private: + void* Align(const void* pAddr); +}; + + +//----------------------------------------------------------------------------- +// Aligns a pointer +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +void* CUtlMemoryAligned::Align(const void* pAddr) +{ + size_t nAlignmentMask = nAlignment - 1; + return (void*)(((size_t)pAddr + nAlignmentMask) & (~nAlignmentMask)); +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(int64_t nGrowSize, int64_t nInitAllocationCount) +{ + CUtlMemory::m_pMemory = 0; + CUtlMemory::m_nAllocationCount = nInitAllocationCount; + CUtlMemory::m_nGrowSize = nGrowSize; + this->ValidateGrowSize(); + + // Alignment must be a power of two + COMPILE_TIME_ASSERT((nAlignment & (nAlignment - 1)) == 0); + Assert((nGrowSize >= 0) && (nGrowSize != CUtlMemory::EXTERNAL_BUFFER_MARKER)); + if (CUtlMemory::m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)_aligned_malloc(nInitAllocationCount * sizeof(T), nAlignment); + } +} + +template< class T, int64_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(T* pMemory, int64_t numElements) +{ + // Special marker indicating externally supplied memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; + + CUtlMemory::m_pMemory = (T*)Align(pMemory); + CUtlMemory::m_nAllocationCount = ((int64_t)(pMemory + numElements) - (int64_t)CUtlMemory::m_pMemory) / sizeof(T); +} + +template< class T, int64_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(const T* pMemory, int64_t numElements) +{ + // Special marker indicating externally supplied memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; + + CUtlMemory::m_pMemory = (T*)Align(pMemory); + CUtlMemory::m_nAllocationCount = ((int64_t)(pMemory + numElements) - (int64_t)CUtlMemory::m_pMemory) / sizeof(T); +} + +template< class T, int64_t nAlignment > +CUtlMemoryAligned::~CUtlMemoryAligned() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +void CUtlMemoryAligned::SetExternalBuffer(T* pMemory, int64_t numElements) +{ + // Blow away any existing allocated memory + Purge(); + + CUtlMemory::m_pMemory = (T*)Align(pMemory); + CUtlMemory::m_nAllocationCount = ((int64_t)(pMemory + numElements) - (int64_t)CUtlMemory::m_pMemory) / sizeof(T); + + // Indicate that we don't own the memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; +} + +template< class T, int64_t nAlignment > +void CUtlMemoryAligned::SetExternalBuffer(const T* pMemory, int64_t numElements) +{ + // Blow away any existing allocated memory + Purge(); + + CUtlMemory::m_pMemory = (T*)Align(pMemory); + CUtlMemory::m_nAllocationCount = ((int64_t)(pMemory + numElements) - (int64_t)CUtlMemory::m_pMemory) / sizeof(T); + + // Indicate that we don't own the memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; +} + + +//----------------------------------------------------------------------------- +// Grows the memory +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +void CUtlMemoryAligned::Grow(int64_t num) +{ + Assert(num > 0); + + if (this->IsExternallyAllocated()) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + // Make sure we have at least numallocated + num allocations. + // Use the grow rules specified for this memory (in m_nGrowSize) + int64_t nAllocationRequested = CUtlMemory::m_nAllocationCount + num; + + CUtlMemory::m_nAllocationCount = UtlMemory_CalcNewAllocationCount(CUtlMemory::m_nAllocationCount, CUtlMemory::m_nGrowSize, nAllocationRequested, sizeof(T)); + + UTLMEMORY_TRACK_ALLOC(); + + if (CUtlMemory::m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_ReallocAligned(CUtlMemory::m_pMemory, CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment); + Assert(CUtlMemory::m_pMemory); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_AllocAligned(CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment); + Assert(CUtlMemory::m_pMemory); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +inline void CUtlMemoryAligned::EnsureCapacity(int64_t num) +{ + if (CUtlMemory::m_nAllocationCount >= num) + return; + + if (this->IsExternallyAllocated()) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + CUtlMemory::m_nAllocationCount = num; + + UTLMEMORY_TRACK_ALLOC(); + + if (CUtlMemory::m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_ReallocAligned(CUtlMemory::m_pMemory, CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_AllocAligned(CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment); + } +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, int64_t nAlignment > +void CUtlMemoryAligned::Purge() +{ + if (!this->IsExternallyAllocated()) + { + if (CUtlMemory::m_pMemory) + { + UTLMEMORY_TRACK_FREE(); + MemAlloc_FreeAligned(CUtlMemory::m_pMemory); + CUtlMemory::m_pMemory = 0; + } + CUtlMemory::m_nAllocationCount = 0; + } +} + +//#include "tier0/memdbgoff.h" + +#endif // UTLMEMORY_H diff --git a/r5dev/tier1/utlvector.h b/r5dev/tier1/utlvector.h index 355f1b5f..f0002688 100644 --- a/r5dev/tier1/utlvector.h +++ b/r5dev/tier1/utlvector.h @@ -1,4 +1,4 @@ -//============ Copyright Valve Corporation, All rights reserved. ============// +//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // @@ -6,18 +6,1477 @@ // // A growable array class that maintains a free list and keeps elements // in the same location -//===========================================================================// +//=============================================================================// + +#ifndef UTLVECTOR_H +#define UTLVECTOR_H + +#ifdef _WIN32 #pragma once +#endif #include "tier1/utlmemory.h" +#include "tier1/utlblockmemory.h" +#include "tier1/strtools.h" +#define FOR_EACH_VEC( vecName, iteratorName ) \ + for ( int iteratorName = 0; (vecName).IsUtlVector && iteratorName < (vecName).Count(); iteratorName++ ) +#define FOR_EACH_VEC_BACK( vecName, iteratorName ) \ + for ( int iteratorName = (vecName).Count()-1; (vecName).IsUtlVector && iteratorName >= 0; iteratorName-- ) + +// UtlVector derives from this so we can do the type check above +struct base_vector_t +{ +public: + enum { IsUtlVector = true }; // Used to match this at compiletime +}; + +//----------------------------------------------------------------------------- +// The CUtlVector class: +// A growable array class which doubles in size by default. +// It will always keep all elements consecutive in memory, and may move the +// elements around in memory (via a PvRealloc) when elements are inserted or +// removed. Clients should therefore refer to the elements of the vector +// by index (they should *never* maintain pointers to elements in the vector). +//----------------------------------------------------------------------------- template< class T, class A = CUtlMemory > -class CUtlVector +class CUtlVector : public base_vector_t { typedef A CAllocator; public: typedef T ElemType_t; + typedef T* iterator; + typedef const T* const_iterator; + + // Set the growth policy and initial capacity. Count will always be zero. This is different from std::vector + // where the constructor sets count as well as capacity. + // growSize of zero implies the default growth pattern which is exponential. + explicit CUtlVector(int growSize = 0, int initialCapacity = 0); + + // Initialize with separately allocated buffer, setting the capacity and count. + // The container will not be growable. + CUtlVector(T* pMemory, int initialCapacity, int initialCount = 0); + ~CUtlVector(); + + // Copy the array. + CUtlVector& operator=(const CUtlVector& other); + + // element access + T& operator[](int i); + const T& operator[](int i) const; + T& Element(int i); + const T& Element(int i) const; + T& Head(); + const T& Head() const; + T& Tail(); + const T& Tail() const; + + // STL compatible member functions. These allow easier use of std::sort + // and they are forward compatible with the C++ 11 range-based for loops. + iterator begin() { return Base(); } + const_iterator begin() const { return Base(); } + iterator end() { return Base() + Count(); } + const_iterator end() const { return Base() + Count(); } + + // Gets the base address (can change when adding elements!) + T* Base() { return m_Memory.Base(); } + const T* Base() const { return m_Memory.Base(); } + + // Returns the number of elements in the vector + int Count() const; + /// are there no elements? For compatibility with lists. + inline bool IsEmpty(void) const + { + return (Count() == 0); + } + + // Is element index valid? + bool IsValidIndex(int i) const; + static int InvalidIndex(); + + // Adds an element, uses default constructor + int AddToHead(); + int AddToTail(); + T* AddToTailGetPtr(); + int InsertBefore(int elem); + int InsertAfter(int elem); + + // Adds an element, uses copy constructor + int AddToHead(const T& src); + int AddToTail(const T& src); + int InsertBefore(int elem, const T& src); + int InsertAfter(int elem, const T& src); + + // Adds multiple elements, uses default constructor + int AddMultipleToHead(int num); + int AddMultipleToTail(int num); + int AddMultipleToTail(int num, const T* pToCopy); + int InsertMultipleBefore(int elem, int num); + int InsertMultipleBefore(int elem, int num, const T* pToCopy); + int InsertMultipleAfter(int elem, int num); + + // Calls RemoveAll() then AddMultipleToTail. + // SetSize is a synonym for SetCount + void SetSize(int size); + // SetCount deletes the previous contents of the container and sets the + // container to have this many elements. + // Use GetCount to retrieve the current count. + void SetCount(int count); + void SetCountNonDestructively(int count); //sets count by adding or removing elements to tail TODO: This should probably be the default behavior for SetCount + + // Calls SetSize and copies each element. + void CopyArray(const T* pArray, int size); + + // Fast swap + void Swap(CUtlVector< T, A >& vec); + + // Add the specified array to the tail. + int AddVectorToTail(CUtlVector const& src); + + // Finds an element (element needs operator== defined) + int Find(const T& src) const; + void FillWithValue(const T& src); + + bool HasElement(const T& src) const; + + // Makes sure we have enough memory allocated to store a requested # of elements + // Use NumAllocated() to retrieve the current capacity. + void EnsureCapacity(int num); + + // Makes sure we have at least this many elements + // Use GetCount to retrieve the current count. + void EnsureCount(int num); + + // Element removal + void FastRemove(int elem); // doesn't preserve order + void Remove(int elem); // preserves order, shifts elements + bool FindAndRemove(const T& src); // removes first occurrence of src, preserves order, shifts elements + bool FindAndFastRemove(const T& src); // removes first occurrence of src, doesn't preserve order + void RemoveMultiple(int elem, int num); // preserves order, shifts elements + void RemoveMultipleFromHead(int num); // removes num elements from tail + void RemoveMultipleFromTail(int num); // removes num elements from tail + void RemoveAll(); // doesn't deallocate memory + + // Memory deallocation + void Purge(); + + // Purges the list and calls delete on each element in it. + void PurgeAndDeleteElements(); + + // Compacts the vector to the number of elements actually in use + void Compact(); + + // Set the size by which it grows when it needs to allocate more memory. + void SetGrowSize(int size) { m_Memory.SetGrowSize(size); } + + int NumAllocated() const; // Only use this if you really know what you're doing! + + void Sort(int(__cdecl* pfnCompare)(const T*, const T*)); + + // Call this to quickly sort non-contiguously allocated vectors + void InPlaceQuickSort(int(__cdecl* pfnCompare)(const T*, const T*)); + // reverse the order of elements + void Reverse(); + +#ifdef DBGFLAG_VALIDATE + void Validate(CValidator& validator, char* pchName); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + + + int SortedFindLessOrEqual(const T& search, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) const; + int SortedInsert(const T& src, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext); + + /// sort using std:: and expecting a "<" function to be defined for the type + void Sort(void); + + /// sort using std:: with a predicate. e.g. [] -> bool ( T &a, T &b ) { return a < b; } + template void SortPredicate(F&& predicate); + protected: + // Can't copy this unless we explicitly do it! + CUtlVector(CUtlVector const& vec) { Assert(0); } + + // Grows the vector + void GrowVector(int num = 1); + + + // Shifts elements.... + void ShiftElementsRight(int elem, int num = 1); + void ShiftElementsLeft(int elem, int num = 1); + CAllocator m_Memory; int m_Size; + +#ifndef _X360 + // For easier access to the elements through the debugger + // it's in release builds so this can be used in libraries correctly + + // Unused in r1/r2/r5? + //T* m_pElements; + + //inline void ResetDbgInfo() + //{ + // m_pElements = Base(); + //} +#else + inline void ResetDbgInfo() {} +#endif + +private: + void InPlaceQuickSort_r(int(__cdecl* pfnCompare)(const T*, const T*), int nLeft, int nRight); }; + + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T > +class CUtlBlockVector : public CUtlVector< T, CUtlBlockMemory< T, int > > +{ +public: + explicit CUtlBlockVector(int growSize = 0, int initSize = 0) + : CUtlVector< T, CUtlBlockMemory< T, int > >(growSize, initSize) {} +}; + +//----------------------------------------------------------------------------- +// The CUtlVectorMT class: +// An array class with spurious mutex protection. Nothing is actually protected +// unless you call Lock and Unlock. Also, the Mutex_t is actually not a type +// but a member which probably isn't used. +//----------------------------------------------------------------------------- + +template< class BASE_UTLVECTOR, class MUTEX_TYPE = CThreadFastMutex > +class CUtlVectorMT : public BASE_UTLVECTOR, public MUTEX_TYPE +{ + typedef BASE_UTLVECTOR BaseClass; +public: + // MUTEX_TYPE Mutex_t; + + // constructor, destructor + explicit CUtlVectorMT(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} + CUtlVectorMT(typename BaseClass::ElemType_t* pMemory, int numElements) : BaseClass(pMemory, numElements) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorFixed class: +// A array class with a fixed allocation scheme +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CUtlVectorFixed : public CUtlVector< T, CUtlMemoryFixed > +{ + typedef CUtlVector< T, CUtlMemoryFixed > BaseClass; +public: + + // constructor, destructor + explicit CUtlVectorFixed(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} + CUtlVectorFixed(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorFixedGrowable class: +// A array class with a fixed allocation scheme backed by a dynamic one +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CUtlVectorFixedGrowable : public CUtlVector< T, CUtlMemoryFixedGrowable > +{ + typedef CUtlVector< T, CUtlMemoryFixedGrowable > BaseClass; + +public: + // constructor, destructor + explicit CUtlVectorFixedGrowable(int growSize = 0) : BaseClass(growSize, MAX_SIZE) {} +}; + +// A fixed growable vector that's castable to CUtlVector +template< class T, size_t FIXED_SIZE > +class CUtlVectorFixedGrowableCompat : public CUtlVector< T > +{ + typedef CUtlVector< T > BaseClass; + +public: + // constructor, destructor + CUtlVectorFixedGrowableCompat(int growSize = 0) : BaseClass(nullptr, FIXED_SIZE, growSize) + { + this->m_Memory.m_pMemory = m_FixedMemory.Base(); + } + + AlignedByteArray_t< FIXED_SIZE, T > m_FixedMemory; +}; + +//----------------------------------------------------------------------------- +// The CUtlVectorConservative class: +// A array class with a conservative allocation scheme +//----------------------------------------------------------------------------- +template< class T > +class CUtlVectorConservative : public CUtlVector< T, CUtlMemoryConservative > +{ + typedef CUtlVector< T, CUtlMemoryConservative > BaseClass; +public: + + // constructor, destructor + explicit CUtlVectorConservative(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} + CUtlVectorConservative(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorUltra Conservative class: +// A array class with a very conservative allocation scheme, with customizable allocator +// Especialy useful if you have a lot of vectors that are sparse, or if you're +// carefully packing holders of vectors +//----------------------------------------------------------------------------- +#pragma warning(push) +#pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4815 ) // warning C4815: 'staticData' : zero-sized array in stack object will have no elements + +class CUtlVectorUltraConservativeAllocator +{ +public: + static void* Alloc(size_t nSize) + { + return malloc(nSize); + } + + static void* Realloc(void* pMem, size_t nSize) + { + return realloc(pMem, nSize); + } + + static void Free(void* pMem) + { + free(pMem); + } + + static size_t GetSize(void* pMem) + { + return mallocsize(pMem); + } + +}; + +template +class CUtlVectorUltraConservative : private A +{ +public: + // Don't inherit from base_vector_t because multiple-inheritance increases + // class size! + enum { IsUtlVector = true }; // Used to match this at compiletime + + CUtlVectorUltraConservative() + { + m_pData = StaticData(); + } + + ~CUtlVectorUltraConservative() + { + RemoveAll(); + } + + int Count() const + { + return m_pData->m_Size; + } + + static int InvalidIndex() + { + return -1; + } + + inline bool IsValidIndex(int i) const + { + return (i >= 0) && (i < Count()); + } + + T& operator[](int i) + { + Assert(IsValidIndex(i)); + return m_pData->m_Elements[i]; + } + + const T& operator[](int i) const + { + Assert(IsValidIndex(i)); + return m_pData->m_Elements[i]; + } + + T& Element(int i) + { + Assert(IsValidIndex(i)); + return m_pData->m_Elements[i]; + } + + const T& Element(int i) const + { + Assert(IsValidIndex(i)); + return m_pData->m_Elements[i]; + } + + void EnsureCapacity(int num) + { + int nCurCount = Count(); + if (num <= nCurCount) + { + return; + } + if (m_pData == StaticData()) + { + m_pData = (Data_t*)A::Alloc(sizeof(Data_t) + (num * sizeof(T))); + m_pData->m_Size = 0; + } + else + { + int nNeeded = sizeof(Data_t) + (num * sizeof(T)); + int nHave = A::GetSize(m_pData); + if (nNeeded > nHave) + { + m_pData = (Data_t*)A::Realloc(m_pData, nNeeded); + } + } + } + + int AddToTail(const T& src) + { + int iNew = Count(); + EnsureCapacity(Count() + 1); + m_pData->m_Elements[iNew] = src; + m_pData->m_Size++; + return iNew; + } + + T* AddToTailGetPtr() + { + return &Element(AddToTail()); + } + + void RemoveAll() + { + if (Count()) + { + for (int i = m_pData->m_Size; --i >= 0; ) + { + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&m_pData->m_Elements[i]); + } + } + if (m_pData != StaticData()) + { + A::Free(m_pData); + m_pData = StaticData(); + + } + } + + void PurgeAndDeleteElements() + { + if (m_pData != StaticData()) + { + for (int i = 0; i < m_pData->m_Size; i++) + { + delete Element(i); + } + RemoveAll(); + } + } + + void FastRemove(int elem) + { + Assert(IsValidIndex(elem)); + + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&Element(elem)); + if (Count() > 0) + { + if (elem != m_pData->m_Size - 1) + memcpy(&Element(elem), &Element(m_pData->m_Size - 1), sizeof(T)); + --m_pData->m_Size; + } + if (!m_pData->m_Size) + { + A::Free(m_pData); + m_pData = StaticData(); + } + } + + void Remove(int elem) + { + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&Element(elem)); + ShiftElementsLeft(elem); + --m_pData->m_Size; + if (!m_pData->m_Size) + { + A::Free(m_pData); + m_pData = StaticData(); + } + } + + int Find(const T& src) const + { + int nCount = Count(); + for (int i = 0; i < nCount; ++i) + { + if (Element(i) == src) + return i; + } + return -1; + } + + bool FindAndRemove(const T& src) + { + int elem = Find(src); + if (elem != -1) + { + Remove(elem); + return true; + } + return false; + } + + + bool FindAndFastRemove(const T& src) + { + int elem = Find(src); + if (elem != -1) + { + FastRemove(elem); + return true; + } + return false; + } + + bool DebugCompileError_ANonVectorIsUsedInThe_FOR_EACH_VEC_Macro(void) const { return true; } + + struct Data_t + { + int m_Size; + T m_Elements[]; + }; + + Data_t* m_pData; +private: + void ShiftElementsLeft(int elem, int num = 1) + { + int Size = Count(); + Assert(IsValidIndex(elem) || (Size == 0) || (num == 0)); + int numToMove = Size - elem - num; + if ((numToMove > 0) && (num > 0)) + { + memmove(&Element(elem), &Element(elem + num), numToMove * sizeof(T)); + +#ifdef _DEBUG + memset(&Element(Size - num), 0xDD, num * sizeof(T)); +#endif + } + } + + + + static Data_t* StaticData() + { + static Data_t staticData; + Assert(staticData.m_Size == 0); + return &staticData; + } +}; + +#pragma warning(pop) + +// Make sure nobody adds multiple inheritance and makes this class bigger. +COMPILE_TIME_ASSERT(sizeof(CUtlVectorUltraConservative) == sizeof(void*)); + + +//----------------------------------------------------------------------------- +// The CCopyableUtlVector class: +// A array class that allows copy construction (so you can nest a CUtlVector inside of another one of our containers) +// WARNING - this class lets you copy construct which can be an expensive operation if you don't carefully control when it happens +// Only use this when nesting a CUtlVector() inside of another one of our container classes (i.e a CUtlMap) +//----------------------------------------------------------------------------- +template< class T > +class CCopyableUtlVector : public CUtlVector< T, CUtlMemory > +{ + typedef CUtlVector< T, CUtlMemory > BaseClass; +public: + explicit CCopyableUtlVector(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} + CCopyableUtlVector(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} + virtual ~CCopyableUtlVector() {} + CCopyableUtlVector(CCopyableUtlVector const& vec) { this->CopyArray(vec.Base(), vec.Count()); } + CCopyableUtlVector(CUtlVector const& vec) { this->CopyArray(vec.Base(), vec.Count()); } +}; + +//----------------------------------------------------------------------------- +// The CCopyableUtlVector class: +// A array class that allows copy construction (so you can nest a CUtlVector inside of another one of our containers) +// WARNING - this class lets you copy construct which can be an expensive operation if you don't carefully control when it happens +// Only use this when nesting a CUtlVector() inside of another one of our container classes (i.e a CUtlMap) +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CCopyableUtlVectorFixed : public CUtlVectorFixed< T, MAX_SIZE > +{ + typedef CUtlVectorFixed< T, MAX_SIZE > BaseClass; +public: + explicit CCopyableUtlVectorFixed(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} + CCopyableUtlVectorFixed(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} + virtual ~CCopyableUtlVectorFixed() {} + CCopyableUtlVectorFixed(CCopyableUtlVectorFixed const& vec) { this->CopyArray(vec.Base(), vec.Count()); } +}; + +// TODO (Ilya): It seems like all the functions in CUtlVector are simple enough that they should be inlined. + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline CUtlVector::CUtlVector(int growSize, int initSize) : + m_Memory(growSize, initSize), m_Size(0) +{ + //ResetDbgInfo(); +} + +template< typename T, class A > +inline CUtlVector::CUtlVector(T* pMemory, int allocationCount, int numElements) : + m_Memory(pMemory, allocationCount), m_Size(numElements) +{ + //ResetDbgInfo(); +} + +template< typename T, class A > +inline CUtlVector::~CUtlVector() +{ + Purge(); +} + +template< typename T, class A > +inline CUtlVector& CUtlVector::operator=(const CUtlVector& other) +{ + int nCount = other.Count(); + SetSize(nCount); + for (int i = 0; i < nCount; i++) + { + (*this)[i] = other[i]; + } + return *this; +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< typename T, class A > +inline T& CUtlVector::operator[](int i) +{ + Assert(i < m_Size); + return m_Memory[i]; +} + +template< typename T, class A > +inline const T& CUtlVector::operator[](int i) const +{ + Assert(i < m_Size); + return m_Memory[i]; +} + +template< typename T, class A > +inline T& CUtlVector::Element(int i) +{ + Assert(i < m_Size); + return m_Memory[i]; +} + +template< typename T, class A > +inline const T& CUtlVector::Element(int i) const +{ + Assert(i < m_Size); + return m_Memory[i]; +} + +template< typename T, class A > +inline T& CUtlVector::Head() +{ + Assert(m_Size > 0); + return m_Memory[0]; +} + +template< typename T, class A > +inline const T& CUtlVector::Head() const +{ + Assert(m_Size > 0); + return m_Memory[0]; +} + +template< typename T, class A > +inline T& CUtlVector::Tail() +{ + Assert(m_Size > 0); + return m_Memory[m_Size - 1]; +} + +template< typename T, class A > +inline const T& CUtlVector::Tail() const +{ + Assert(m_Size > 0); + return m_Memory[m_Size - 1]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Reverse - reverse the order of elements, akin to std::vector<>::reverse() +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::Reverse() +{ + for (int i = 0; i < m_Size / 2; i++) + { + V_swap(m_Memory[i], m_Memory[m_Size - 1 - i]); +#if defined( UTLVECTOR_TRACK_STACKS ) + if (bTrackingEnabled) + { + V_swap(m_pElementStackStatsIndices[i], m_pElementStackStatsIndices[m_Size - 1 - i]); + } +#endif + } +} + +// Count +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::Count() const +{ + return m_Size; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< typename T, class A > +inline bool CUtlVector::IsValidIndex(int i) const +{ + return (i >= 0) && (i < m_Size); +} + + +//----------------------------------------------------------------------------- +// Returns in invalid index +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::InvalidIndex() +{ + return -1; +} + + +//----------------------------------------------------------------------------- +// Grows the vector +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::GrowVector(int num) +{ + if (m_Size + num > m_Memory.NumAllocated()) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow(m_Size + num - m_Memory.NumAllocated()); + } + + m_Size += num; + //ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// finds a particular element +// You must sort the list before using or your results will be wrong +//----------------------------------------------------------------------------- +template< typename T, class A > +int CUtlVector::SortedFindLessOrEqual(const T& search, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) const +{ + int start = 0, end = Count() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if (pfnLessFunc(Element(mid), search, pLessContext)) + { + start = mid + 1; + } + else if (pfnLessFunc(search, Element(mid), pLessContext)) + { + end = mid - 1; + } + else + { + return mid; + } + } + return end; +} + + +template< typename T, class A > +int CUtlVector::SortedInsert(const T& src, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) +{ + int pos = SortedFindLessOrEqual(src, pfnLessFunc, pLessContext) + 1; + GrowVector(); + ShiftElementsRight(pos); + CopyConstruct(&Element(pos), src); + //UTLVECTOR_STACK_STATS_ALLOCATED_SINGLE( pos ); + return pos; +} + + + + + +//----------------------------------------------------------------------------- +// Sorts the vector +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::Sort(int(__cdecl* pfnCompare)(const T*, const T*)) +{ + typedef int(__cdecl* QSortCompareFunc_t)(const void*, const void*); + if (Count() <= 1) + return; + + if (Base()) + { + qsort(Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare)); + } + else + { + Assert(0); + // this path is untested + // if you want to sort vectors that use a non-sequential memory allocator, + // you'll probably want to patch in a quicksort algorithm here + // I just threw in this bubble sort to have something just in case... + + for (int i = m_Size - 1; i >= 0; --i) + { + for (int j = 1; j <= i; ++j) + { + if (pfnCompare(&Element(j - 1), &Element(j)) < 0) + { + V_swap(Element(j - 1), Element(j)); + } + } + } + } +} + + +//---------------------------------------------------------------------------------------------- +// Private function that does the in-place quicksort for non-contiguously allocated vectors. +//---------------------------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::InPlaceQuickSort_r(int(__cdecl* pfnCompare)(const T*, const T*), int nLeft, int nRight) +{ + int nPivot; + int nLeftIdx = nLeft; + int nRightIdx = nRight; + + if (nRight - nLeft > 0) + { + nPivot = (nLeft + nRight) / 2; + + while ((nLeftIdx <= nPivot) && (nRightIdx >= nPivot)) + { + while ((pfnCompare(&Element(nLeftIdx), &Element(nPivot)) < 0) && (nLeftIdx <= nPivot)) + { + nLeftIdx++; + } + + while ((pfnCompare(&Element(nRightIdx), &Element(nPivot)) > 0) && (nRightIdx >= nPivot)) + { + nRightIdx--; + } + + V_swap(Element(nLeftIdx), Element(nRightIdx)); + + nLeftIdx++; + nRightIdx--; + + if ((nLeftIdx - 1) == nPivot) + { + nPivot = nRightIdx = nRightIdx + 1; + } + else if (nRightIdx + 1 == nPivot) + { + nPivot = nLeftIdx = nLeftIdx - 1; + } + } + + InPlaceQuickSort_r(pfnCompare, nLeft, nPivot - 1); + InPlaceQuickSort_r(pfnCompare, nPivot + 1, nRight); + } +} + + +//---------------------------------------------------------------------------------------------- +// Call this to quickly sort non-contiguously allocated vectors. Sort uses a slower bubble sort. +//---------------------------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::InPlaceQuickSort(int(__cdecl* pfnCompare)(const T*, const T*)) +{ + InPlaceQuickSort_r(pfnCompare, 0, Count() - 1); +} + +template< typename T, class A > +void CUtlVector::Sort(void) +{ + //STACK STATS TODO: Do we care about allocation tracking precision enough to match element origins across a sort? + std::sort(Base(), Base() + Count()); +} + +template< typename T, class A > +template +void CUtlVector::SortPredicate(F&& predicate) +{ + std::sort(Base(), Base() + Count(), predicate); +} + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::EnsureCapacity(int num) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + //ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Makes sure we have at least this many elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::EnsureCount(int num) +{ + if (Count() < num) + { + AddMultipleToTail(num - Count()); + } +} + + +//----------------------------------------------------------------------------- +// Shifts elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::ShiftElementsRight(int elem, int num) +{ + Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); + int numToMove = m_Size - elem - num; + if ((numToMove > 0) && (num > 0)) + memmove(&Element(elem + num), &Element(elem), numToMove * sizeof(T)); +} + +template< typename T, class A > +void CUtlVector::ShiftElementsLeft(int elem, int num) +{ + Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); + int numToMove = m_Size - elem - num; + if ((numToMove > 0) && (num > 0)) + { + memmove(&Element(elem), &Element(elem + num), numToMove * sizeof(T)); + +#ifdef _DEBUG + memset(&Element(m_Size - num), 0xDD, num * sizeof(T)); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Adds an element, uses default constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddToHead() +{ + return InsertBefore(0); +} + +template< typename T, class A > +inline int CUtlVector::AddToTail() +{ + return InsertBefore(m_Size); +} + +template< typename T, class A > +inline T* CUtlVector::AddToTailGetPtr() +{ + return &Element(AddToTail()); +} + +template< typename T, class A > +inline int CUtlVector::InsertAfter(int elem) +{ + return InsertBefore(elem + 1); +} + +template< typename T, class A > +int CUtlVector::InsertBefore(int elem) +{ + // Can insert at the end + Assert((elem == Count()) || IsValidIndex(elem)); + + GrowVector(); + ShiftElementsRight(elem); + Construct(&Element(elem)); + return elem; +} + + +//----------------------------------------------------------------------------- +// Adds an element, uses copy constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddToHead(const T& src) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); + return InsertBefore(0, src); +} + +template< typename T, class A > +inline int CUtlVector::AddToTail(const T& src) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); + return InsertBefore(m_Size, src); +} + +template< typename T, class A > +inline int CUtlVector::InsertAfter(int elem, const T& src) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); + return InsertBefore(elem + 1, src); +} + +template< typename T, class A > +int CUtlVector::InsertBefore(int elem, const T& src) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); + + // Can insert at the end + Assert((elem == Count()) || IsValidIndex(elem)); + + GrowVector(); + ShiftElementsRight(elem); + CopyConstruct(&Element(elem), src); + return elem; +} + + +//----------------------------------------------------------------------------- +// Adds multiple elements, uses default constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddMultipleToHead(int num) +{ + return InsertMultipleBefore(0, num); +} + +template< typename T, class A > +inline int CUtlVector::AddMultipleToTail(int num) +{ + return InsertMultipleBefore(m_Size, num); +} + +template< typename T, class A > +inline int CUtlVector::AddMultipleToTail(int num, const T* pToCopy) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || !pToCopy || (pToCopy + num <= Base()) || (pToCopy >= (Base() + Count()))); + + return InsertMultipleBefore(m_Size, num, pToCopy); +} + +template< typename T, class A > +int CUtlVector::InsertMultipleAfter(int elem, int num) +{ + return InsertMultipleBefore(elem + 1, num); +} + + +template< typename T, class A > +void CUtlVector::SetCount(int count) +{ + RemoveAll(); + AddMultipleToTail(count); +} + +template< typename T, class A > +inline void CUtlVector::SetSize(int size) +{ + SetCount(size); +} + +template< typename T, class A > +void CUtlVector::SetCountNonDestructively(int count) +{ + int delta = count - m_Size; + if (delta > 0) AddMultipleToTail(delta); + else if (delta < 0) RemoveMultipleFromTail(-delta); +} + +template< typename T, class A > +void CUtlVector::CopyArray(const T* pArray, int size) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert((Base() == NULL) || !pArray || (Base() >= (pArray + size)) || (pArray >= (Base() + Count()))); + + SetSize(size); + for (int i = 0; i < size; i++) + { + (*this)[i] = pArray[i]; + } +} + +template< typename T, class A > +void CUtlVector::Swap(CUtlVector< T, A >& vec) +{ + m_Memory.Swap(vec.m_Memory); + V_swap(m_Size, vec.m_Size); +#ifndef _X360 + //V_swap(m_pElements, vec.m_pElements); +#endif +} + +template< typename T, class A > +int CUtlVector::AddVectorToTail(CUtlVector const& src) +{ + Assert(&src != this); + + int base = Count(); + + // Make space. + int nSrcCount = src.Count(); + EnsureCapacity(base + nSrcCount); + + // Copy the elements. + m_Size += nSrcCount; + for (int i = 0; i < nSrcCount; i++) + { + CopyConstruct(&Element(base + i), src[i]); + } + return base; +} + +template< typename T, class A > +inline int CUtlVector::InsertMultipleBefore(int elem, int num) +{ + if (num == 0) + return elem; + + // Can insert at the end + Assert((elem == Count()) || IsValidIndex(elem)); + + GrowVector(num); + ShiftElementsRight(elem, num); + + // Invoke default constructors + for (int i = 0; i < num; ++i) + { + Construct(&Element(elem + i)); + } + + return elem; +} + +template< typename T, class A > +inline int CUtlVector::InsertMultipleBefore(int elem, int num, const T* pToInsert) +{ + if (num == 0) + return elem; + + // Can insert at the end + Assert((elem == Count()) || IsValidIndex(elem)); + + GrowVector(num); + ShiftElementsRight(elem, num); + + // Invoke default constructors + if (!pToInsert) + { + for (int i = 0; i < num; ++i) + { + Construct(&Element(elem + i)); + } + } + else + { + for (int i = 0; i < num; i++) + { + CopyConstruct(&Element(elem + i), pToInsert[i]); + } + } + + return elem; +} + + +//----------------------------------------------------------------------------- +// Finds an element (element needs operator== defined) +//----------------------------------------------------------------------------- +template< typename T, class A > +int CUtlVector::Find(const T& src) const +{ + for (int i = 0; i < Count(); ++i) + { + if (Element(i) == src) + return i; + } + return -1; +} + +template< typename T, class A > +void CUtlVector::FillWithValue(const T& src) +{ + for (int i = 0; i < Count(); i++) + { + Element(i) = src; + } +} + +template< typename T, class A > +bool CUtlVector::HasElement(const T& src) const +{ + return (Find(src) >= 0); +} + + +//----------------------------------------------------------------------------- +// Element removal +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::FastRemove(int elem) +{ + Assert(IsValidIndex(elem)); + + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&Element(elem)); + if (m_Size > 0) + { + if (elem != m_Size - 1) + memcpy(&Element(elem), &Element(m_Size - 1), sizeof(T)); + --m_Size; + } +} + +template< typename T, class A > +void CUtlVector::Remove(int elem) +{ + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&Element(elem)); + ShiftElementsLeft(elem); + --m_Size; +} + +template< typename T, class A > +bool CUtlVector::FindAndRemove(const T& src) +{ + int elem = Find(src); + if (elem != -1) + { + Remove(elem); + return true; + } + return false; +} + +template< typename T, class A > +bool CUtlVector::FindAndFastRemove(const T& src) +{ + int elem = Find(src); + if (elem != -1) + { + FastRemove(elem); + return true; + } + return false; +} + +template< typename T, class A > +void CUtlVector::RemoveMultiple(int elem, int num) +{ + Assert(elem >= 0); + Assert(elem + num <= Count()); + + // Global scope to resolve conflict with Scaleform 4.0 + for (int i = elem + num; --i >= elem; ) + ::Destruct(&Element(i)); + + ShiftElementsLeft(elem, num); + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveMultipleFromHead(int num) +{ + Assert(num <= Count()); + + // Global scope to resolve conflict with Scaleform 4.0 + for (int i = num; --i >= 0; ) + ::Destruct(&Element(i)); + + ShiftElementsLeft(0, num); + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveMultipleFromTail(int num) +{ + Assert(num <= Count()); + + // Global scope to resolve conflict with Scaleform 4.0 + for (int i = m_Size - num; i < m_Size; i++) + ::Destruct(&Element(i)); + + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveAll() +{ + for (int i = m_Size; --i >= 0; ) + { + // Global scope to resolve conflict with Scaleform 4.0 + ::Destruct(&Element(i)); + } + + m_Size = 0; +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- + +template< typename T, class A > +inline void CUtlVector::Purge() +{ + RemoveAll(); + m_Memory.Purge(); + //ResetDbgInfo(); +} + + +template< typename T, class A > +inline void CUtlVector::PurgeAndDeleteElements() +{ + for (int i = 0; i < m_Size; i++) + { + delete Element(i); + } + Purge(); +} + +template< typename T, class A > +inline void CUtlVector::Compact() +{ + m_Memory.Purge(m_Size); +} + +template< typename T, class A > +inline int CUtlVector::NumAllocated() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Data and memory validation +//----------------------------------------------------------------------------- +#ifdef DBGFLAG_VALIDATE +template< typename T, class A > +void CUtlVector::Validate(CValidator& validator, char* pchName) +{ + validator.Push(typeid(*this).name(), this, pchName); + + m_Memory.Validate(validator, "m_Memory"); + + validator.Pop(); +} +#endif // DBGFLAG_VALIDATE + +// A vector class for storing pointers, so that the elements pointed to by the pointers are deleted +// on exit. +template class CUtlVectorAutoPurge : public CUtlVector< T, CUtlMemory< T, int> > +{ +public: + ~CUtlVectorAutoPurge(void) + { + this->PurgeAndDeleteElements(); + } + +}; + +// easy string list class with dynamically allocated strings. For use with V_SplitString, etc. +// Frees the dynamic strings in destructor. +class CUtlStringList : public CUtlVectorAutoPurge< char*> +{ +public: + void CopyAndAddToTail(char const* pString) // clone the string and add to the end + { + char* pNewStr = new char[1 + strlen(pString)]; + strcpy(pNewStr, pString); + AddToTail(pNewStr); + } + + static int __cdecl SortFunc(char* const* sz1, char* const* sz2) + { + return strcmp(*sz1, *sz2); + } + + CUtlStringList() {} + + // !TODO: + + //CUtlStringList(char const* pString, char const* pSeparator) + //{ + // SplitString(pString, pSeparator); + //} + + //CUtlStringList(char const* pString, const char** pSeparators, int nSeparators) + //{ + // SplitString2(pString, pSeparators, nSeparators); + //} + + //void SplitString(char const* pString, char const* pSeparator) + //{ + // V_SplitString(pString, pSeparator, *this); + //} + + //void SplitString2(char const* pString, const char** pSeparators, int nSeparators) + //{ + // V_SplitString2(pString, pSeparators, nSeparators, *this); + //} +private: + CUtlStringList(const CUtlStringList& other); // copying directly will cause double-release of the same strings; maybe we need to do a deep copy, but unless and until such need arises, this will guard against double-release +}; + + + +// placing it here a few days before Cert to minimize disruption to the rest of codebase +class CSplitString : public CUtlVector > +{ +public: + CSplitString(); + CSplitString(const char* pString, const char* pSeparator); + CSplitString(const char* pString, const char** pSeparators, int nSeparators); + ~CSplitString(); + + void Set(const char* pString, const char** pSeparators, int nSeparators); + + // + // NOTE: If you want to make Construct() public and implement Purge() here, you'll have to free m_szBuffer there + // +private: + void Construct(const char* pString, const char** pSeparators, int nSeparators); + void PurgeAndDeleteElements(); +private: + char* m_szBuffer; // a copy of original string, with '\0' instead of separators +}; + + +#endif // CCVECTOR_H diff --git a/r5dev/vproj/clientsdk.vcxproj b/r5dev/vproj/clientsdk.vcxproj index 079f1d78..7c0ab45b 100644 --- a/r5dev/vproj/clientsdk.vcxproj +++ b/r5dev/vproj/clientsdk.vcxproj @@ -113,6 +113,7 @@ + @@ -462,6 +463,7 @@ + @@ -478,6 +480,7 @@ + diff --git a/r5dev/vproj/clientsdk.vcxproj.filters b/r5dev/vproj/clientsdk.vcxproj.filters index d141937c..df701101 100644 --- a/r5dev/vproj/clientsdk.vcxproj.filters +++ b/r5dev/vproj/clientsdk.vcxproj.filters @@ -564,6 +564,9 @@ sdk\tier1 + + sdk\tier1 + @@ -1664,6 +1667,12 @@ sdk\public\include + + sdk\tier1 + + + sdk\tier0 + diff --git a/r5dev/vproj/dedicated.vcxproj b/r5dev/vproj/dedicated.vcxproj index 7b395608..36bc0a45 100644 --- a/r5dev/vproj/dedicated.vcxproj +++ b/r5dev/vproj/dedicated.vcxproj @@ -423,6 +423,7 @@ + @@ -439,6 +440,7 @@ + @@ -552,6 +554,7 @@ + diff --git a/r5dev/vproj/dedicated.vcxproj.filters b/r5dev/vproj/dedicated.vcxproj.filters index 0f1e94bb..6067a329 100644 --- a/r5dev/vproj/dedicated.vcxproj.filters +++ b/r5dev/vproj/dedicated.vcxproj.filters @@ -1191,6 +1191,12 @@ sdk\public\include + + sdk\tier1 + + + sdk\tier0 + @@ -1493,6 +1499,9 @@ sdk\tier1 + + sdk\tier1 + diff --git a/r5dev/vproj/gamesdk.vcxproj b/r5dev/vproj/gamesdk.vcxproj index 39edabf5..71df00b1 100644 --- a/r5dev/vproj/gamesdk.vcxproj +++ b/r5dev/vproj/gamesdk.vcxproj @@ -123,6 +123,7 @@ + @@ -489,6 +490,7 @@ + @@ -505,6 +507,7 @@ + diff --git a/r5dev/vproj/gamesdk.vcxproj.filters b/r5dev/vproj/gamesdk.vcxproj.filters index 796ae3a2..1d422b74 100644 --- a/r5dev/vproj/gamesdk.vcxproj.filters +++ b/r5dev/vproj/gamesdk.vcxproj.filters @@ -603,6 +603,9 @@ sdk\tier1 + + sdk\tier1 + @@ -1754,6 +1757,12 @@ sdk\public\include + + sdk\tier1 + + + sdk\tier0 +