2template <
typename T, plUInt32 SizeInBytes>
9template <
typename T, plUInt32 SizeInBytes>
12 PL_ASSERT_DEV(m_uiCount < CAPACITY,
"Block is full.");
13 return m_pData + m_uiCount++;
16template <
typename T, plUInt32 SizeInBytes>
19 PL_ASSERT_DEV(m_uiCount > 0,
"Block is empty");
21 return m_pData + m_uiCount;
24template <
typename T, plUInt32 SizeInBytes>
27 return m_uiCount == 0;
30template <
typename T, plUInt32 SizeInBytes>
33 return m_uiCount == CAPACITY;
36template <
typename T, plUInt32 SizeInBytes>
39 PL_ASSERT_DEV(uiIndex < m_uiCount,
"Out of bounds access. Data block has {0} elements, trying to access element at index {1}.", m_uiCount, uiIndex);
40 return m_pData[uiIndex];
45template <plUInt32 BlockSize>
47 : m_TrackingMode(mode)
48 , m_SuperBlocks(pParent)
49 , m_FreeBlocks(pParent)
51 static_assert(BlockSize >= 4096,
"Block size must be 4096 or bigger");
53 m_Id = plMemoryTracker::RegisterAllocator(sName, mode, plPageAllocator::GetId());
57 PL_IGNORE_UNUSED(uiPageSize);
58 PL_ASSERT_DEV(uiPageSize <= BlockSize,
"Memory Page size is bigger than block size.");
59 PL_ASSERT_DEV(BlockSize % uiPageSize == 0,
"Blocksize ({0}) must be a multiple of page size ({1})", BlockSize, uiPageSize);
62template <plUInt32 BlockSize>
66 plMemoryTracker::DeregisterAllocator(m_Id);
68 for (plUInt32 i = 0; i < m_SuperBlocks.GetCount(); ++i)
70 plPageAllocator::DeallocatePage(m_SuperBlocks[i].m_pBasePtr);
74template <plUInt32 BlockSize>
87 Helper::BLOCK_CAPACITY >= 1,
"Type is too big for block allocation. Consider using regular heap allocation instead or increase the block size.");
93template <plUInt32 BlockSize>
97 Deallocate(inout_block.m_pData);
98 inout_block.m_pData =
nullptr;
99 inout_block.m_uiCount = 0;
102template <plUInt32 BlockSize>
105 return plMemoryTracker::GetAllocatorName(m_Id);
108template <plUInt32 BlockSize>
114template <plUInt32 BlockSize>
117 return plMemoryTracker::GetAllocatorStats(m_Id);
120template <plUInt32 BlockSize>
123 PL_ASSERT_RELEASE(
plMath::IsPowerOf2((plUInt32)uiAlign),
"Alignment must be power of two");
131 if (!m_FreeBlocks.IsEmpty())
134 plUInt32 uiFreeBlockIndex = m_FreeBlocks.PeekBack();
135 m_FreeBlocks.PopBack();
137 const plUInt32 uiSuperBlockIndex = uiFreeBlockIndex / SuperBlock::NUM_BLOCKS;
138 const plUInt32 uiInnerBlockIndex = uiFreeBlockIndex & (SuperBlock::NUM_BLOCKS - 1);
139 SuperBlock& superBlock = m_SuperBlocks[uiSuperBlockIndex];
140 ++superBlock.m_uiUsedBlocks;
147 void* pMemory = plPageAllocator::AllocatePage(SuperBlock::SIZE_IN_BYTES);
148 PL_CHECK_ALIGNMENT(pMemory, uiAlign);
150 SuperBlock superBlock;
151 superBlock.m_pBasePtr = pMemory;
152 superBlock.m_uiUsedBlocks = 1;
154 m_SuperBlocks.PushBack(superBlock);
156 const plUInt32 uiBlockBaseIndex = (m_SuperBlocks.GetCount() - 1) * SuperBlock::NUM_BLOCKS;
157 for (plUInt32 i = SuperBlock::NUM_BLOCKS - 1; i > 0; --i)
159 m_FreeBlocks.PushBack(uiBlockBaseIndex + i);
165 if (m_TrackingMode >= plAllocatorTrackingMode::AllocationStats)
167 plMemoryTracker::AddAllocation(m_Id, m_TrackingMode, ptr, BlockSize, uiAlign,
plTime::Now() - fAllocationTime);
173template <plUInt32 BlockSize>
178 if (m_TrackingMode >= plAllocatorTrackingMode::AllocationStats)
180 plMemoryTracker::RemoveAllocation(m_Id, ptr);
185 plUInt32 uiSuperBlockIndex = m_SuperBlocks.GetCount();
186 std::ptrdiff_t diff = 0;
188 for (; uiSuperBlockIndex-- > 0;)
190 diff = (
char*)ptr - (
char*)m_SuperBlocks[uiSuperBlockIndex].m_pBasePtr;
191 if (diff >= 0 && diff < SuperBlock::SIZE_IN_BYTES)
198 PL_IGNORE_UNUSED(bFound);
199 PL_ASSERT_DEV(bFound,
"'{0}' was not allocated with this allocator",
plArgP(ptr));
201 SuperBlock& superBlock = m_SuperBlocks[uiSuperBlockIndex];
202 --superBlock.m_uiUsedBlocks;
204 if (superBlock.m_uiUsedBlocks == 0 && m_FreeBlocks.GetCount() > SuperBlock::NUM_BLOCKS * 4)
207 plPageAllocator::DeallocatePage(superBlock.m_pBasePtr);
209 m_SuperBlocks.RemoveAtAndSwap(uiSuperBlockIndex);
210 const plUInt32 uiLastSuperBlockIndex = m_SuperBlocks.GetCount();
213 for (plUInt32 i = 0; i < m_FreeBlocks.GetCount(); ++i)
215 const plUInt32 uiIndex = m_FreeBlocks[i];
216 const plUInt32 uiSBIndex = uiIndex / SuperBlock::NUM_BLOCKS;
218 if (uiSBIndex == uiSuperBlockIndex)
221 m_FreeBlocks.RemoveAtAndSwap(i);
224 else if (uiSBIndex == uiLastSuperBlockIndex)
227 m_FreeBlocks[i] = uiSuperBlockIndex * SuperBlock::NUM_BLOCKS + (uiIndex & (SuperBlock::NUM_BLOCKS - 1));
234 const plUInt32 uiInnerBlockIndex = (plUInt32)(diff / BlockSize);
235 m_FreeBlocks.PushBack(uiSuperBlockIndex * SuperBlock::NUM_BLOCKS + uiInnerBlockIndex);
Base class for all memory allocators.
Definition Allocator.h:23
A block allocator which can only allocates blocks of memory at once.
Definition LargeBlockAllocator.h:40
static T * AddByteOffset(T *pPtr, std::ptrdiff_t offset)
Returns the address stored in ptr plus the given byte offset iOffset, cast to type T.
plStringView represent a read-only sub-string of a larger string, as it can store a dedicated string ...
Definition StringView.h:34
static plThreadID GetCurrentThreadID()
Returns an identifier for the currently running thread.
Definition ThreadUtils_Posix.h:42
constexpr PL_FORCE_INLINE bool IsPowerOf2(plInt32 value)
Returns true, if there exists some x with 2^x == value.
Definition Math_inl.h:260
Definition Allocator.h:26
Definition FormatStringArgs.h:76
This struct represents a block of type T, typically 4kb.
Definition LargeBlockAllocator.h:14
The time class encapsulates a double value storing the time in seconds.
Definition Time.h:12
static plTime Now()
Gets the current time.
Definition Time_Posix.h:12