r5sdk/r5dev/tier1/utlfixedmemory.h
Kawe Mazidjatari 277e05b2b8 Improve logging throughout SDK
Used proper enum for context. The low level tier1 stuff should print in COMMON.
Also added newlines where missing, the logging system will undergo a change where a newline will only be appended if we are logging in the same context without a newline.
2022-11-25 23:03:56 +01:00

357 lines
9.5 KiB
C++
Raw Blame History

//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//
// A growable memory class.
//===========================================================================//
#ifndef UTLFIXEDMEMORY_H
#define UTLFIXEDMEMORY_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#include "tier0/platform.h"
#include "tier0/memalloc.h"
//#include "tier0/memdbgon.h"
#include "mathlib/mathlib.h"
#pragma warning (disable:4100)
#pragma warning (disable:4514)
//-----------------------------------------------------------------------------
#ifdef UTLFIXEDMEMORY_TRACK
#define UTLFIXEDMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "||Sum of all UtlFixedMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 )
#define UTLFIXEDMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "||Sum of all UtlFixedMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 )
#else
#define UTLFIXEDMEMORY_TRACK_ALLOC() ((void)0)
#define UTLFIXEDMEMORY_TRACK_FREE() ((void)0)
#endif
//-----------------------------------------------------------------------------
// The CUtlFixedMemory class:
// A growable memory class that allocates non-sequential blocks, but is indexed sequentially
//-----------------------------------------------------------------------------
template< class T >
class CUtlFixedMemory
{
public:
// constructor, destructor
CUtlFixedMemory(ssize_t nGrowSize = 0, ssize_t nInitSize = 0);
~CUtlFixedMemory();
// Set the size by which the memory grows
void Init(ssize_t nGrowSize = 0, ssize_t 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; }
protected:
struct BlockHeader_t;
public:
class Iterator_t
{
public:
Iterator_t(BlockHeader_t* p, ssize_t i) : m_pBlockHeader(p), m_nIndex(i) {}
BlockHeader_t* m_pBlockHeader;
ssize_t m_nIndex;
bool operator==(const Iterator_t it) const { return m_pBlockHeader == it.m_pBlockHeader && m_nIndex == it.m_nIndex; }
bool operator!=(const Iterator_t it) const { return m_pBlockHeader != it.m_pBlockHeader || m_nIndex != it.m_nIndex; }
};
Iterator_t First() const { return m_pBlocks ? Iterator_t(m_pBlocks, 0) : InvalidIterator(); }
Iterator_t Next(const Iterator_t& it) const
{
Assert(IsValidIterator(it));
if (!IsValidIterator(it))
return InvalidIterator();
BlockHeader_t* RESTRICT pHeader = it.m_pBlockHeader;
if (it.m_nIndex + 1 < pHeader->m_nBlockSize)
return Iterator_t(pHeader, it.m_nIndex + 1);
return pHeader->m_pNext ? Iterator_t(pHeader->m_pNext, 0) : InvalidIterator();
}
ssize_t GetIndex(const Iterator_t& it) const
{
Assert(IsValidIterator(it));
if (!IsValidIterator(it))
return InvalidIndex();
return (ssize_t)(HeaderToBlock(it.m_pBlockHeader) + it.m_nIndex);
}
bool IsIdxAfter(ssize_t i, const Iterator_t& it) const
{
Assert(IsValidIterator(it));
if (!IsValidIterator(it))
return false;
if (IsInBlock(i, it.m_pBlockHeader))
return i > GetIndex(it);
for (BlockHeader_t* RESTRICT pbh = it.m_pBlockHeader->m_pNext; pbh; pbh = pbh->m_pNext)
{
if (IsInBlock(i, pbh))
return true;
}
return false;
}
bool IsValidIterator(const Iterator_t& it) const { return it.m_pBlockHeader && it.m_nIndex >= 0 && it.m_nIndex < it.m_pBlockHeader->m_nBlockSize; }
Iterator_t InvalidIterator() const { return Iterator_t(NULL, -1); }
// element access
T& operator[](ssize_t i);
const T& operator[](ssize_t i) const;
T& Element(ssize_t i);
const T& Element(ssize_t i) const;
// Can we use this index?
bool IsIdxValid(ssize_t i) const;
// Specify the invalid ('null') index that we'll only return on failure
static const ssize_t INVALID_INDEX = 0; // For use with COMPILE_TIME_ASSERT
static ssize_t InvalidIndex() { return INVALID_INDEX; }
// Size
ssize_t NumAllocated() const;
ssize_t Count() const { return NumAllocated(); }
// Grows memory by max(num,growsize), and returns the allocation index/ptr
void Grow(ssize_t num = 1);
// Makes sure we've got at least this much memory
void EnsureCapacity(ssize_t num);
// Memory deallocation
void Purge();
protected:
// Fast swap - WARNING: Swap invalidates all ptr-based indices!!!
void Swap(CUtlFixedMemory< T >& mem);
bool IsInBlock(ssize_t i, BlockHeader_t* pBlockHeader) const
{
T* p = (T*)i;
const T* p0 = HeaderToBlock(pBlockHeader);
return p >= p0 && p < p0 + pBlockHeader->m_nBlockSize;
}
struct BlockHeader_t
{
BlockHeader_t* m_pNext;
ssize_t m_nBlockSize;
};
const T* HeaderToBlock(const BlockHeader_t* pHeader) const { return (T*)(pHeader + 1); }
const BlockHeader_t* BlockToHeader(const T* pBlock) const { return (BlockHeader_t*)(pBlock)-1; }
BlockHeader_t* m_pBlocks;
ssize_t m_nAllocationCount;
ssize_t m_nGrowSize;
};
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
template< class T >
CUtlFixedMemory<T>::CUtlFixedMemory(ssize_t nGrowSize, ssize_t nInitAllocationCount)
: m_pBlocks(0), m_nAllocationCount(0), m_nGrowSize(0)
{
Init(nGrowSize, nInitAllocationCount);
}
template< class T >
CUtlFixedMemory<T>::~CUtlFixedMemory()
{
Purge();
}
//-----------------------------------------------------------------------------
// Fast swap - WARNING: Swap invalidates all ptr-based indices!!!
//-----------------------------------------------------------------------------
template< class T >
void CUtlFixedMemory<T>::Swap(CUtlFixedMemory< T >& mem)
{
V_swap(m_pBlocks, mem.m_pBlocks);
V_swap(m_nAllocationCount, mem.m_nAllocationCount);
V_swap(m_nGrowSize, mem.m_nGrowSize);
}
//-----------------------------------------------------------------------------
// Set the size by which the memory grows - round up to the next power of 2
//-----------------------------------------------------------------------------
template< class T >
void CUtlFixedMemory<T>::Init(ssize_t nGrowSize /* = 0 */, ssize_t nInitSize /* = 0 */)
{
Purge();
m_nGrowSize = nGrowSize;
Grow(nInitSize);
}
//-----------------------------------------------------------------------------
// element access
//-----------------------------------------------------------------------------
template< class T >
inline T& CUtlFixedMemory<T>::operator[](ssize_t i)
{
Assert(IsIdxValid(i));
return *(T*)i;
}
template< class T >
inline const T& CUtlFixedMemory<T>::operator[](ssize_t i) const
{
Assert(IsIdxValid(i));
return *(T*)i;
}
template< class T >
inline T& CUtlFixedMemory<T>::Element(ssize_t i)
{
Assert(IsIdxValid(i));
return *(T*)i;
}
template< class T >
inline const T& CUtlFixedMemory<T>::Element(ssize_t i) const
{
Assert(IsIdxValid(i));
return *(T*)i;
}
//-----------------------------------------------------------------------------
// Size
//-----------------------------------------------------------------------------
template< class T >
inline ssize_t CUtlFixedMemory<T>::NumAllocated() const
{
return m_nAllocationCount;
}
//-----------------------------------------------------------------------------
// Is element index valid?
//-----------------------------------------------------------------------------
template< class T >
inline bool CUtlFixedMemory<T>::IsIdxValid(ssize_t i) const
{
#ifdef _DEBUG
for (BlockHeader_t* pbh = m_pBlocks; pbh; pbh = pbh->m_pNext)
{
if (IsInBlock(i, pbh))
return true;
}
return false;
#else
return i != InvalidIndex();
#endif
}
template< class T >
void CUtlFixedMemory<T>::Grow(ssize_t num)
{
if (num <= 0)
return;
ssize_t nBlockSize = m_nGrowSize;
if (nBlockSize == 0)
{
if (m_nAllocationCount)
{
nBlockSize = m_nAllocationCount;
}
else
{
// Compute an allocation which is at least as big as a cache line...
nBlockSize = (31 + sizeof(T)) / sizeof(T);
Assert(nBlockSize);
}
}
if (nBlockSize < num)
{
ssize_t n = (num + nBlockSize - 1) / nBlockSize;
Assert(n * nBlockSize >= num);
Assert((n - 1) * nBlockSize < num);
nBlockSize *= n;
}
m_nAllocationCount += nBlockSize;
MEM_ALLOC_CREDIT_CLASS();
BlockHeader_t* RESTRICT pBlockHeader = (BlockHeader_t*)malloc(sizeof(BlockHeader_t) + nBlockSize * sizeof(T));
if (!pBlockHeader)
{
Error(eDLL_T::COMMON, EXIT_FAILURE, "CUtlFixedMemory overflow!\n");
}
pBlockHeader->m_pNext = NULL;
pBlockHeader->m_nBlockSize = nBlockSize;
if (!m_pBlocks)
{
m_pBlocks = pBlockHeader;
}
else
{
#if 1 // IsIdxAfter assumes that newly allocated blocks are at the end
BlockHeader_t* RESTRICT pbh = m_pBlocks;
while (pbh->m_pNext)
{
pbh = pbh->m_pNext;
}
pbh->m_pNext = pBlockHeader;
#else
pBlockHeader = m_pBlocks;
pBlockHeader->m_pNext = m_pBlocks;
#endif
}
}
//-----------------------------------------------------------------------------
// Makes sure we've got at least this much memory
//-----------------------------------------------------------------------------
template< class T >
inline void CUtlFixedMemory<T>::EnsureCapacity(ssize_t num)
{
Grow(num - NumAllocated());
}
//-----------------------------------------------------------------------------
// Memory deallocation
//-----------------------------------------------------------------------------
template< class T >
void CUtlFixedMemory<T>::Purge()
{
if (!m_pBlocks)
return;
for (BlockHeader_t* pbh = m_pBlocks; pbh; )
{
BlockHeader_t* pFree = pbh;
pbh = pbh->m_pNext;
MemAllocSingleton()->Free(pFree);
}
m_pBlocks = NULL;
m_nAllocationCount = 0;
}
//#include "tier0/memdbgoff.h"
#endif // UTLFIXEDMEMORY_H