Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
MemStack.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/ContainerAllocationPolicies.h"
6#include "Containers/LockFreeFixedSizeAllocator.h"
7#include "CoreGlobals.h"
8#include "CoreTypes.h"
9#include "HAL/MemoryBase.h"
10#include "HAL/PlatformCrt.h"
11#include "HAL/ThreadSafeCounter.h"
12#include "HAL/ThreadSingleton.h"
13#include "HAL/UnrealMemory.h"
14#include "Math/UnrealMathUtility.h"
15#include "Misc/AssertionMacros.h"
16#include "Misc/Build.h"
17#include "Misc/NoopCounter.h"
18#include "Templates/AlignmentTemplates.h"
19#include "Templates/UnrealTemplate.h"
20
21
22// Enums for specifying memory allocation type.
24{
25 MEM_Zeroed = 1
26};
27
28
30{
31 MEM_Oned = 1
32};
33
34
36{
37public:
38 enum
39 {
40 PageSize = 64 * 1024,
41 SmallPageSize = 1024-16 // allow a little extra space for allocator headers, etc
42 };
44 typedef TLockFreeFixedSizeAllocator<PageSize, PLATFORM_CACHE_LINE_SIZE, FNoopCounter> TPageAllocator;
45#else
47#endif
48
50
52 void* Alloc();
53 void Free(void* Mem);
54 void* AllocSmall();
55 void FreeSmall(void* Mem);
56 uint64 BytesUsed();
57 uint64 BytesFree();
59private:
60
64
65#if STATS
66 void UpdateStats();
67#endif
68 TPageAllocator TheAllocator;
69};
70
71
72/**
73 * Simple linear-allocation memory stack.
74 * Items are allocated via PushBytes() or the specialized operator new()s.
75 * Items are freed en masse by using FMemMark to Pop() them.
76 **/
77class FMemStackBase //-V1062
78{
79public:
80
82
83 FMemStackBase(const FMemStackBase&) = delete;
85 {
86 *this = MoveTemp(Other);
87 }
88
90 {
91 Top = Other.Top;
92 End = Other.End;
93 TopChunk = Other.TopChunk;
94 TopMark = Other.TopMark;
95 NumMarks = Other.NumMarks;
97 Other.Top = nullptr;
98 Other.End = nullptr;
99 Other.TopChunk = nullptr;
100 Other.NumMarks = 0;
101 Other.bShouldEnforceAllocMarks = false;
102 return *this;
103 }
104
106 {
107 check((GIsCriticalError || !NumMarks));
108 FreeChunks(nullptr);
109 }
110
111 FORCEINLINE uint8* PushBytes(size_t AllocSize, size_t Alignment)
112 {
113 return (uint8*)Alloc(AllocSize, FMath::Max(AllocSize >= 16 ? (size_t)16 : (size_t)8, Alignment));
114 }
115
116 FORCEINLINE void* Alloc(size_t AllocSize, size_t Alignment)
117 {
118 // Debug checks.
119 checkSlow(AllocSize>=0);
120 checkSlow((Alignment&(Alignment-1))==0);
121 checkSlow(Top<=End);
122 check(!bShouldEnforceAllocMarks || NumMarks > 0);
123
124 // Try to get memory from the current chunk.
125 uint8* Result = Align( Top, Alignment );
126 uint8* NewTop = Result + AllocSize;
127
128 // Make sure we didn't overflow.
129 if ( NewTop <= End )
130 {
131 Top = NewTop;
132 }
133 else
134 {
135 // We'd pass the end of the current chunk, so allocate a new one.
136 AllocateNewChunk( AllocSize + Alignment );
137 Result = Align( Top, Alignment );
138 NewTop = Result + AllocSize;
139 Top = NewTop;
140 }
141 return Result;
142 }
143
144 /** return true if this stack is empty. */
145 FORCEINLINE bool IsEmpty() const
146 {
147 return TopChunk == nullptr;
148 }
149
151 {
152 check(!NumMarks);
153 FreeChunks(nullptr);
154 }
156 {
157 return NumMarks;
158 }
159 /** @return the number of bytes allocated for this FMemStack that are currently in use. */
160 int32 GetByteCount() const;
161
162 // Returns true if the pointer was allocated using this allocator
163 bool ContainsPointer(const void* Pointer) const;
164
165 // Friends.
166 friend class FMemMark;
167 friend void* operator new(size_t Size, FMemStackBase& Mem, int32 Count);
168 friend void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, int32 Count);
169 friend void* operator new(size_t Size, FMemStackBase& Mem, EMemZeroed Tag, int32 Count);
170 friend void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemZeroed Tag, int32 Count);
171 friend void* operator new(size_t Size, FMemStackBase& Mem, EMemOned Tag, int32 Count);
172 friend void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemOned Tag, int32 Count);
173 friend void* operator new[](size_t Size, FMemStackBase& Mem, int32 Count);
174 friend void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, int32 Count);
175 friend void* operator new[](size_t Size, FMemStackBase& Mem, EMemZeroed Tag, int32 Count);
176 friend void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemZeroed Tag, int32 Count);
177 friend void* operator new[](size_t Size, FMemStackBase& Mem, EMemOned Tag, int32 Count);
178 friend void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemOned Tag, int32 Count);
179
180 // Types.
182 {
184 int32 DataSize;
185
186 uint8 *Data() const
187 {
188 return ((uint8*)this) + sizeof(FTaggedMemory);
189 }
190 };
191
192private:
193
194 /**
195 * Allocate a new chunk of memory of at least MinSize size,
196 * updates the memory stack's Chunks table and ActiveChunks counter.
197 */
198 void AllocateNewChunk( int32 MinSize );
199
200 /** Frees the chunks above the specified chunk on the stack. */
201 void FreeChunks( FTaggedMemory* NewTopChunk );
202
203 // Variables.
204 uint8* Top; // Top of current chunk (Top<=End).
205 uint8* End; // End of current chunk.
206 FTaggedMemory* TopChunk; // Only chunks 0..ActiveChunks-1 are valid.
207
208 /** The top mark on the stack. */
210
211 /** The number of marks on this stack. */
212 int32 NumMarks;
213
214
215protected:
217};
218
219
221{
222public:
224 {
226 }
227};
228
229
230/*-----------------------------------------------------------------------------
231 FMemStack templates.
232-----------------------------------------------------------------------------*/
233
234// Operator new for typesafe memory stack allocation.
235template <class T> inline T* New(FMemStackBase& Mem, int32 Count = 1, int32 Align = DEFAULT_ALIGNMENT)
236{
237 return (T*)Mem.PushBytes( Count*sizeof(T), Align );
238}
239template <class T> inline T* NewZeroed(FMemStackBase& Mem, int32 Count = 1, int32 Align = DEFAULT_ALIGNMENT)
240{
241 uint8* Result = Mem.PushBytes( Count*sizeof(T), Align );
242 FMemory::Memzero( Result, Count*sizeof(T) );
243 return (T*)Result;
244}
245template <class T> inline T* NewOned(FMemStackBase& Mem, int32 Count = 1, int32 Align = DEFAULT_ALIGNMENT)
246{
247 uint8* Result = Mem.PushBytes( Count*sizeof(T), Align );
248 FMemory::Memset( Result, 0xff, Count*sizeof(T) );
249 return (T*)Result;
250}
251
252
253/*-----------------------------------------------------------------------------
254 FMemStack operator new's.
255-----------------------------------------------------------------------------*/
256
257// Operator new for typesafe memory stack allocation.
258inline void* operator new(size_t Size, FMemStackBase& Mem, int32 Count = 1)
259{
260 // Get uninitialized memory.
261 const size_t SizeInBytes = Size * Count;
262 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
263 return Mem.PushBytes( SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
264}
265inline void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, int32 Count = 1) // c++17
266{
267 // Get uninitialized memory.
268 const size_t SizeInBytes = Size * Count;
269 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
270 return Mem.PushBytes(SizeInBytes, (size_t)Align);
271}
272inline void* operator new(size_t Size, FMemStackBase& Mem, EMemZeroed Tag, int32 Count = 1)
273{
274 // Get zero-filled memory.
275 const size_t SizeInBytes = Size * Count;
276 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
277 uint8* Result = Mem.PushBytes( SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
278 FMemory::Memzero( Result, SizeInBytes );
279 return Result;
280}
281inline void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemZeroed Tag, int32 Count = 1) // c++17
282{
283 // Get zero-filled memory.
284 const size_t SizeInBytes = Size * Count;
285 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
286 uint8* Result = Mem.PushBytes(SizeInBytes, (size_t)Align);
287 FMemory::Memzero(Result, SizeInBytes);
288 return Result;
289}
290inline void* operator new(size_t Size, FMemStackBase& Mem, EMemOned Tag, int32 Count = 1)
291{
292 // Get one-filled memory.
293 const size_t SizeInBytes = Size * Count;
294 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
295 uint8* Result = Mem.PushBytes( SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
296 FMemory::Memset( Result, 0xff, SizeInBytes );
297 return Result;
298}
299inline void* operator new(size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemOned Tag, int32 Count = 1) // c++17
300{
301 // Get one-filled memory.
302 const size_t SizeInBytes = Size * Count;
303 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
304 uint8* Result = Mem.PushBytes(SizeInBytes, (size_t)Align);
305 FMemory::Memset(Result, 0xff, SizeInBytes);
306 return Result;
307}
308inline void* operator new[](size_t Size, FMemStackBase& Mem, int32 Count = 1)
309{
310 // Get uninitialized memory.
311 const size_t SizeInBytes = Size * Count;
312 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
313 return Mem.PushBytes( SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
314}
315inline void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, int32 Count = 1) // c++17
316{
317 // Get uninitialized memory.
318 const size_t SizeInBytes = Size * Count;
319 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
320 return Mem.PushBytes(SizeInBytes, (size_t)Align);
321}
322inline void* operator new[](size_t Size, FMemStackBase& Mem, EMemZeroed Tag, int32 Count = 1)
323{
324 // Get zero-filled memory.
325 const size_t SizeInBytes = Size * Count;
326 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
327 uint8* Result = Mem.PushBytes(SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
328 FMemory::Memzero( Result, SizeInBytes );
329 return Result;
330}
331inline void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemZeroed Tag, int32 Count = 1) // c++17
332{
333 // Get zero-filled memory.
334 const size_t SizeInBytes = Size * Count;
335 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
336 uint8* Result = Mem.PushBytes(SizeInBytes, (size_t)Align);
337 FMemory::Memzero(Result, SizeInBytes);
338 return Result;
339}
340inline void* operator new[](size_t Size, FMemStackBase& Mem, EMemOned Tag, int32 Count = 1)
341{
342 // Get one-filled memory.
343 const size_t SizeInBytes = Size * Count;
344 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
345 uint8* Result = Mem.PushBytes( SizeInBytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
346 FMemory::Memset( Result, 0xff, SizeInBytes );
347 return Result;
348}
349inline void* operator new[](size_t Size, std::align_val_t Align, FMemStackBase& Mem, EMemOned Tag, int32 Count = 1) // c++17
350{
351 // Get one-filled memory.
352 const size_t SizeInBytes = Size * Count;
353 checkSlow(SizeInBytes <= (size_t)TNumericLimits<int32>::Max());
354 uint8* Result = Mem.PushBytes(SizeInBytes, (size_t)Align);
355 FMemory::Memset(Result, 0xff, SizeInBytes);
356 return Result;
357}
358
359
360/** A container allocator that allocates from a mem-stack. */
361template<uint32 Alignment = DEFAULT_ALIGNMENT>
363{
364public:
365 using SizeType = int32;
366
367 enum { NeedsElementType = true };
368 enum { RequireRangeCheck = true };
369
370 template<typename ElementType>
372 {
373 public:
374
375 /** Default constructor. */
377 Data(nullptr)
378 {}
379
380 /**
381 * Moves the state of another allocator into this one.
382 * Assumes that the allocator is currently empty, i.e. memory may be allocated but any existing elements have already been destructed (if necessary).
383 * @param Other - The allocator to move the state from. This allocator should be left in a valid empty state.
384 */
386 {
387 checkSlow(this != &Other);
388
389 Data = Other.Data;
390 Other.Data = nullptr;
391 }
392
393 // FContainerAllocatorInterface
394 FORCEINLINE ElementType* GetAllocation() const
395 {
396 return Data;
397 }
398
399 //@TODO: FLOATPRECISION: Takes SIZE_T input but doesn't actually support it
400 void ResizeAllocation(SizeType PreviousNumElements, SizeType NumElements,SIZE_T NumBytesPerElement)
401 {
402 void* OldData = Data;
403 if( NumElements )
404 {
405 // Allocate memory from the stack.
409 );
410
411 // If the container previously held elements, copy them into the new allocation.
413 {
416 }
417 }
418 }
419 FORCEINLINE SizeType CalculateSlackReserve(SizeType NumElements, SIZE_T NumBytesPerElement) const
420 {
422 }
423 FORCEINLINE SizeType CalculateSlackShrink(SizeType NumElements, SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
424 {
426 }
427 FORCEINLINE SizeType CalculateSlackGrow(SizeType NumElements, SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
428 {
430 }
431
432 FORCEINLINE SIZE_T GetAllocatedSize(SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
433 {
435 }
436
437 bool HasAllocation() const
438 {
439 return !!Data;
440 }
441
442 SizeType GetInitialCapacity() const
443 {
444 return 0;
445 }
446
447 private:
448
449 /** A pointer to the container's elements. */
450 ElementType* Data;
451 };
452
454};
455
456template <uint32 Alignment>
458{
459 enum { IsZeroConstruct = true };
460};
461
462
463/**
464 * FMemMark marks a top-of-stack position in the memory stack.
465 * When the marker is constructed or initialized with a particular memory
466 * stack, it saves the stack's current position. When marker is popped, it
467 * pops all items that were added to the stack subsequent to initialization.
468 */
470{
471public:
472 // Constructors.
474 : Mem(InMem)
475 , Top(InMem.Top)
476 , SavedChunk(InMem.TopChunk)
477 , bPopped(false)
479 {
480 Mem.TopMark = this;
481
482 // Track the number of outstanding marks on the stack.
483 Mem.NumMarks++;
484 }
485
486 /** Destructor. */
488 {
489 Pop();
490 }
491
492 /** Free the memory allocated after the mark was created. */
493 void Pop()
494 {
495 if(!bPopped)
496 {
497 check(Mem.TopMark == this);
498 bPopped = true;
499
500 // Track the number of outstanding marks on the stack.
501 --Mem.NumMarks;
502
503 // Unlock any new chunks that were allocated.
504 if( SavedChunk != Mem.TopChunk )
505 {
507 }
508
509 // Restore the memory stack's state.
510 Mem.Top = Top;
512
513 // Ensure that the mark is only popped once by clearing the top pointer.
514 Top = nullptr;
515 }
516 }
517
518private:
519
520 // Implementation variables.
522 uint8* Top;
526};
#define checkSlow(expr)
#define check(expr)
#define UE_BUILD_SHIPPING
Definition Build.h:4
#define STATS
Definition Build.h:317
EMemOned
Definition Enums.h:20782
EMemZeroed
Definition Enums.h:18215
T * NewOned(FMemStackBase &Mem, int32 Count=1, int32 Align=DEFAULT_ALIGNMENT)
Definition MemStack.h:245
@ MEM_Oned
Definition MemStack.h:31
@ MEM_Zeroed
Definition MemStack.h:25
T * New(FMemStackBase &Mem, int32 Count=1, int32 Align=DEFAULT_ALIGNMENT)
Definition MemStack.h:235
T * NewZeroed(FMemStackBase &Mem, int32 Count=1, int32 Align=DEFAULT_ALIGNMENT)
Definition MemStack.h:239
@ DEFAULT_ALIGNMENT
Definition MemoryBase.h:24
#define FORCEINLINE
Definition Platform.h:644
#define PLATFORM_CACHE_LINE_SIZE
Definition Platform.h:818
FMemMark * NextTopmostMark
Definition MemStack.h:525
FMemMark(FMemStackBase &InMem)
Definition MemStack.h:473
uint8 * Top
Definition MemStack.h:522
void Pop()
Definition MemStack.h:493
FMemStackBase::FTaggedMemory * SavedChunk
Definition MemStack.h:523
~FMemMark()
Definition MemStack.h:487
FMemStackBase & Mem
Definition MemStack.h:521
bool bPopped
Definition MemStack.h:524
FORCEINLINE void * Alloc(size_t AllocSize, size_t Alignment)
Definition MemStack.h:116
bool ContainsPointer(const void *Pointer) const
bool bShouldEnforceAllocMarks
Definition MemStack.h:216
friend void * operator new(size_t Size, FMemStackBase &Mem, EMemZeroed Tag, int32 Count)
Definition MemStack.h:272
friend void * operator new[](size_t Size, std::align_val_t Align, FMemStackBase &Mem, EMemOned Tag, int32 Count)
Definition MemStack.h:349
FMemStackBase(const FMemStackBase &)=delete
FORCEINLINE void Flush()
Definition MemStack.h:150
uint8 * Top
Definition MemStack.h:204
FMemStackBase & operator=(FMemStackBase &&Other)
Definition MemStack.h:89
friend void * operator new(size_t Size, std::align_val_t Align, FMemStackBase &Mem, EMemOned Tag, int32 Count)
Definition MemStack.h:299
void AllocateNewChunk(int32 MinSize)
FTaggedMemory * TopChunk
Definition MemStack.h:206
friend void * operator new(size_t Size, std::align_val_t Align, FMemStackBase &Mem, int32 Count)
Definition MemStack.h:265
uint8 * End
Definition MemStack.h:205
friend void * operator new[](size_t Size, FMemStackBase &Mem, int32 Count)
Definition MemStack.h:308
FORCEINLINE bool IsEmpty() const
Definition MemStack.h:145
friend void * operator new(size_t Size, std::align_val_t Align, FMemStackBase &Mem, EMemZeroed Tag, int32 Count)
Definition MemStack.h:281
FMemStackBase(FMemStackBase &&Other)
Definition MemStack.h:84
friend void * operator new(size_t Size, FMemStackBase &Mem, EMemOned Tag, int32 Count)
Definition MemStack.h:290
int32 NumMarks
Definition MemStack.h:212
friend void * operator new[](size_t Size, std::align_val_t Align, FMemStackBase &Mem, int32 Count)
Definition MemStack.h:315
int32 GetByteCount() const
FORCEINLINE uint8 * PushBytes(size_t AllocSize, size_t Alignment)
Definition MemStack.h:111
friend void * operator new(size_t Size, FMemStackBase &Mem, int32 Count)
Definition MemStack.h:258
friend void * operator new[](size_t Size, FMemStackBase &Mem, EMemZeroed Tag, int32 Count)
Definition MemStack.h:322
friend void * operator new[](size_t Size, std::align_val_t Align, FMemStackBase &Mem, EMemZeroed Tag, int32 Count)
Definition MemStack.h:331
FORCEINLINE int32 GetNumMarks()
Definition MemStack.h:155
class FMemMark * TopMark
Definition MemStack.h:209
friend void * operator new[](size_t Size, FMemStackBase &Mem, EMemOned Tag, int32 Count)
Definition MemStack.h:340
void FreeChunks(FTaggedMemory *NewTopChunk)
void * AllocSmall()
static FPageAllocator & Get()
void * Alloc()
uint64 BytesUsed()
uint64 BytesFree()
void Free(void *Mem)
static FPageAllocator * Instance
Definition MemStack.h:61
TPageAllocator TheAllocator
Definition MemStack.h:68
void LatchProtectedMode()
static FPageAllocator & Construct()
void FreeSmall(void *Mem)
FORCEINLINE void MoveToEmpty(ForElementType &Other)
Definition MemStack.h:385
FORCEINLINE SizeType CalculateSlackShrink(SizeType NumElements, SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
Definition MemStack.h:423
FORCEINLINE SIZE_T GetAllocatedSize(SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
Definition MemStack.h:432
FORCEINLINE SizeType CalculateSlackGrow(SizeType NumElements, SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const
Definition MemStack.h:427
FORCEINLINE ElementType * GetAllocation() const
Definition MemStack.h:394
FORCEINLINE SizeType CalculateSlackReserve(SizeType NumElements, SIZE_T NumBytesPerElement) const
Definition MemStack.h:419
void ResizeAllocation(SizeType PreviousNumElements, SizeType NumElements, SIZE_T NumBytesPerElement)
Definition MemStack.h:400
SizeType GetInitialCapacity() const
Definition MemStack.h:442
ForElementType< FScriptContainerElement > ForAnyElementType
Definition MemStack.h:453
static FORCEINLINE void * Memzero(void *Dest, SIZE_T Count)
static FORCEINLINE void * Memset(void *Dest, uint8 Char, SIZE_T Count)