Plasma Engine  2.0
Loading...
Searching...
No Matches
MemoryUtils_inl.h
1
2#define PL_CHECK_CLASS(T) \
3 static_assert(!std::is_trivial<T>::value, \
4 "Trivial POD type is treated as class. Use PL_DECLARE_POD_TYPE(YourClass) or PL_DEFINE_AS_POD_TYPE(ExternalClass) to mark it as POD.")
5
6template <plConstructionMode mode, typename T>
7PL_ALWAYS_INLINE void plMemoryUtils::Construct(T* pDestination, size_t uiCount)
8{
9 if constexpr (mode == SkipTrivialTypes && std::is_trivial<T>::value)
10 {
11 // do nothing
12 }
13 else
14 {
15 for (size_t i = 0; i < uiCount; i++)
16 {
17 ::new (pDestination + i) T();
18 }
19 }
20}
21
22template <plConstructionMode mode, typename T>
23PL_ALWAYS_INLINE plMemoryUtils::ConstructorFunction plMemoryUtils::MakeConstructorFunction()
24{
25 if constexpr (mode == SkipTrivialTypes && std::is_trivial<T>::value)
26 {
27 return nullptr;
28 }
29 else
30 {
31 struct Helper
32 {
33 static void Construct(void* pDestination) { plMemoryUtils::Construct<mode>(static_cast<T*>(pDestination), 1); }
34 };
35
36 return &Helper::Construct;
37 }
38}
39
40template <typename Destination, typename Source>
41PL_ALWAYS_INLINE void plMemoryUtils::CopyConstruct(Destination* pDestination, const Source& copy, size_t uiCount)
42{
44 {
45 static_assert(std::is_same<Destination, Source>::value ||
46 (std::is_base_of<Destination, Source>::value == false && std::is_base_of<Source, Destination>::value == false),
47 "Can't copy POD types that are derived from each other. Are you certain any of these types should be POD?");
48
49 const Destination& copyConverted = copy;
50 for (size_t i = 0; i < uiCount; i++)
51 {
52 memcpy(pDestination + i, &copyConverted, sizeof(Destination));
53 }
54 }
55 else
56 {
57 PL_CHECK_CLASS(Destination);
58
59 for (size_t i = 0; i < uiCount; i++)
60 {
61 ::new (pDestination + i) Destination(copy); // Note that until now copy has not been converted to Destination. This allows for calling
62 // specialized constructors if available.
63 }
64 }
65}
66
67template <typename T>
68PL_ALWAYS_INLINE void plMemoryUtils::CopyConstructArray(T* pDestination, const T* pSource, size_t uiCount)
69{
70 PL_ASSERT_DEV(pDestination + uiCount <= pSource || pSource + uiCount <= pDestination, "Memory regions must not overlap when using CopyConstruct.");
71
72 if constexpr (plIsPodType<T>::value)
73 {
74 memcpy(pDestination, pSource, uiCount * sizeof(T));
75 }
76 else
77 {
78 PL_CHECK_CLASS(T);
79
80 for (size_t i = 0; i < uiCount; i++)
81 {
82 ::new (pDestination + i) T(pSource[i]);
83 }
84 }
85}
86
87template <typename T>
88PL_ALWAYS_INLINE plMemoryUtils::CopyConstructorFunction plMemoryUtils::MakeCopyConstructorFunction()
89{
90 struct Helper
91 {
92 static void CopyConstruct(void* pDestination, const void* pSource)
93 {
94 plMemoryUtils::CopyConstruct(static_cast<T*>(pDestination), *static_cast<const T*>(pSource), 1);
95 }
96 };
97
98 return &Helper::CopyConstruct;
99}
100
101template <typename T>
102PL_ALWAYS_INLINE void plMemoryUtils::MoveConstruct(T* pDestination, T&& source)
103{
104 // Make sure source is actually an rvalue reference (T&& is a universal reference).
105 static_assert(std::is_rvalue_reference<decltype(source)>::value, "'source' parameter is not an rvalue reference.");
106 ::new (pDestination) T(std::forward<T>(source));
107}
108
109template <typename T>
110PL_ALWAYS_INLINE void plMemoryUtils::MoveConstruct(T* pDestination, T* pSource, size_t uiCount)
111{
112 PL_ASSERT_DEV(pDestination + uiCount <= pSource || pSource + uiCount <= pDestination, "Memory regions must not overlap when using MoveConstruct.");
113
114 // Enforce move construction.
115 static_assert(std::is_move_constructible<T>::value, "Type is not move constructible!");
116
117 for (size_t i = 0; i < uiCount; ++i)
118 {
119 ::new (pDestination + i) T(std::move(pSource[i]));
120 }
121}
122
123template <typename Destination, typename Source>
124PL_ALWAYS_INLINE void plMemoryUtils::CopyOrMoveConstruct(Destination* pDestination, Source&& source)
125{
126 if constexpr (std::is_rvalue_reference<decltype(source)>::value)
127 {
128 static_assert(std::is_rvalue_reference<decltype(source)>::value, "This version of CopyOrMoveConstruct should only be called with a rvalue reference!");
129
130 ::new (pDestination) Destination(std::move(source));
131 }
132 else
133 {
134 CopyConstruct<Destination, Source>(pDestination, source, 1);
135 }
136}
137
138template <typename T>
139PL_ALWAYS_INLINE void plMemoryUtils::RelocateConstruct(T* pDestination, T* pSource, size_t uiCount)
140{
141 PL_ASSERT_DEV(pDestination + uiCount <= pSource || pSource + uiCount <= pDestination, "Memory regions must not overlap when using RelocateConstruct.");
142
143 if constexpr (plGetTypeClass<T>::value != 0) // POD or mem-relocatable
144 {
145 memcpy(pDestination, pSource, uiCount * sizeof(T));
146 }
147 else // class
148 {
149 PL_CHECK_CLASS(T);
150
151 for (size_t i = 0; i < uiCount; i++)
152 {
153 // Note that this calls the move constructor only if available and will copy otherwise.
154 ::new (pDestination + i) T(std::move(pSource[i]));
155 }
156
157 Destruct(pSource, uiCount);
158 }
159}
160
161template <typename T>
162PL_ALWAYS_INLINE void plMemoryUtils::Destruct(T* pDestination, size_t uiCount)
163{
164 if constexpr (plIsPodType<T>::value == 1)
165 {
166 static_assert(std::is_trivially_destructible<T>::value != 0, "Class is declared as POD but has a non-trivial destructor. Remove the destructor or don't declare it as POD.");
167 }
168 else if constexpr (std::is_trivially_destructible<T>::value == 0)
169 {
170 for (size_t i = 0; i < uiCount; ++i)
171 {
172 pDestination[i].~T();
173 }
174 }
175}
176
177template <typename T>
178PL_ALWAYS_INLINE plMemoryUtils::DestructorFunction plMemoryUtils::MakeDestructorFunction()
179{
180 if constexpr (plIsPodType<T>::value)
181 {
182 return nullptr;
183 }
184 else
185 {
186 PL_CHECK_CLASS(T);
187
188 struct Helper
189 {
190 static void Destruct(void* pDestination) { plMemoryUtils::Destruct(static_cast<T*>(pDestination), 1); }
191 };
192
193 return &Helper::Destruct;
194 }
195}
196
197PL_ALWAYS_INLINE void plMemoryUtils::RawByteCopy(void* pDestination, const void* pSource, size_t uiNumBytesToCopy)
198{
199 memcpy(pDestination, pSource, uiNumBytesToCopy);
200}
201
202template <typename T>
203PL_ALWAYS_INLINE void plMemoryUtils::Copy(T* pDestination, const T* pSource, size_t uiCount)
204{
205 PL_ASSERT_DEV(pDestination < pSource || pSource + uiCount <= pDestination, "Memory regions must not overlap when using Copy. Use CopyOverlapped instead.");
206
207 if constexpr (plIsPodType<T>::value)
208 {
209 memcpy(pDestination, pSource, uiCount * sizeof(T));
210 }
211 else
212 {
213 PL_CHECK_CLASS(T);
214
215 for (size_t i = 0; i < uiCount; i++)
216 {
217 pDestination[i] = pSource[i];
218 }
219 }
220}
221
222template <typename T>
223PL_ALWAYS_INLINE void plMemoryUtils::CopyOverlapped(T* pDestination, const T* pSource, size_t uiCount)
224{
225 if constexpr (plIsPodType<T>::value)
226 {
227 memmove(pDestination, pSource, uiCount * sizeof(T));
228 }
229 else
230 {
231 PL_CHECK_CLASS(T);
232
233 if (pDestination == pSource)
234 return;
235
236 if (pDestination < pSource)
237 {
238 for (size_t i = 0; i < uiCount; i++)
239 {
240 pDestination[i] = pSource[i];
241 }
242 }
243 else
244 {
245 for (size_t i = uiCount; i > 0; --i)
246 {
247 pDestination[i - 1] = pSource[i - 1];
248 }
249 }
250 }
251}
252
253template <typename T>
254PL_ALWAYS_INLINE void plMemoryUtils::Relocate(T* pDestination, T* pSource, size_t uiCount)
255{
256 PL_ASSERT_DEV(pDestination + uiCount <= pSource || pSource + uiCount <= pDestination, "Memory regions must not overlap when using Relocate.");
257
258 if constexpr (plGetTypeClass<T>::value != 0) // POD or mem-relocatable
259 {
260 memcpy(pDestination, pSource, uiCount * sizeof(T));
261 }
262 else // class
263 {
264 PL_CHECK_CLASS(T);
265
266 for (size_t i = 0; i < uiCount; i++)
267 {
268 // Note that this calls the move constructor only if available and will copy otherwise.
269 pDestination[i] = std::move(pSource[i]);
270 }
271
272 Destruct(pSource, uiCount);
273 }
274}
275
276template <typename T>
277PL_ALWAYS_INLINE void plMemoryUtils::RelocateOverlapped(T* pDestination, T* pSource, size_t uiCount)
278{
279 if constexpr (plGetTypeClass<T>::value == 2) // mem-relocatable
280 {
281 if (pDestination < pSource)
282 {
283 size_t uiDestructCount = pSource - pDestination;
284 Destruct(pDestination, uiDestructCount);
285 }
286 else
287 {
288 size_t uiDestructCount = pDestination - pSource;
289 Destruct(pSource + uiCount, uiDestructCount);
290 }
291 memmove(pDestination, pSource, uiCount * sizeof(T));
292 }
293 else if constexpr (plGetTypeClass<T>::value == 1) // POD
294 {
295 memmove(pDestination, pSource, uiCount * sizeof(T));
296 }
297 else
298 {
299 PL_CHECK_CLASS(T);
300
301 if (pDestination == pSource)
302 return;
303
304 if (pDestination < pSource)
305 {
306 for (size_t i = 0; i < uiCount; i++)
307 {
308 pDestination[i] = std::move(pSource[i]);
309 }
310
311 size_t uiDestructCount = pSource - pDestination;
312 Destruct(pSource + uiCount - uiDestructCount, uiDestructCount);
313 }
314 else
315 {
316 for (size_t i = uiCount; i > 0; --i)
317 {
318 pDestination[i - 1] = std::move(pSource[i - 1]);
319 }
320
321 size_t uiDestructCount = pDestination - pSource;
322 Destruct(pSource, uiDestructCount);
323 }
324 }
325}
326
327template <typename T>
328PL_ALWAYS_INLINE void plMemoryUtils::Prepend(T* pDestination, const T& source, size_t uiCount)
329{
330 if constexpr (plGetTypeClass<T>::value != 0) // POD or mem-relocatable
331 {
332 memmove(pDestination + 1, pDestination, uiCount * sizeof(T));
333 CopyConstruct(pDestination, source, 1);
334 }
335 else // class
336 {
337 PL_CHECK_CLASS(T);
338
339 if (uiCount > 0)
340 {
341 MoveConstruct(pDestination + uiCount, std::move(pDestination[uiCount - 1]));
342
343 for (size_t i = uiCount - 1; i > 0; --i)
344 {
345 pDestination[i] = std::move(pDestination[i - 1]);
346 }
347
348 *pDestination = source;
349 }
350 else
351 {
352 CopyConstruct(pDestination, source, 1);
353 }
354 }
355}
356
357template <typename T>
358PL_ALWAYS_INLINE void plMemoryUtils::Prepend(T* pDestination, T&& source, size_t uiCount)
359{
360 if constexpr (plGetTypeClass<T>::value != 0) // POD or mem-relocatable
361 {
362 memmove(pDestination + 1, pDestination, uiCount * sizeof(T));
363 MoveConstruct(pDestination, std::move(source));
364 }
365 else // class
366 {
367 PL_CHECK_CLASS(T);
368
369 if (uiCount > 0)
370 {
371 MoveConstruct(pDestination + uiCount, std::move(pDestination[uiCount - 1]));
372
373 for (size_t i = uiCount - 1; i > 0; --i)
374 {
375 pDestination[i] = std::move(pDestination[i - 1]);
376 }
377
378 *pDestination = std::move(source);
379 }
380 else
381 {
382 MoveConstruct(pDestination, std::move(source));
383 }
384 }
385}
386
387template <typename T>
388PL_ALWAYS_INLINE void plMemoryUtils::Prepend(T* pDestination, const T* pSource, size_t uiSourceCount, size_t uiCount)
389{
390 if constexpr (plGetTypeClass<T>::value != 0) // POD or mem-relocatable
391 {
392 memmove(pDestination + uiSourceCount, pDestination, uiCount * sizeof(T));
393 CopyConstructArray(pDestination, pSource, uiSourceCount);
394 }
395 else // class
396 {
397 PL_CHECK_CLASS(T);
398
399 if (uiCount > 0)
400 {
401 MoveConstruct(pDestination + uiSourceCount, pDestination, uiCount);
402 CopyConstructArray(pDestination, pSource, uiSourceCount);
403 }
404 else
405 {
406 CopyConstructArray(pDestination, pSource, uiSourceCount);
407 }
408 }
409}
410
411template <typename T>
412PL_ALWAYS_INLINE bool plMemoryUtils::IsEqual(const T* a, const T* b, size_t uiCount /*= 1*/)
413{
414 if constexpr (plIsPodType<T>::value)
415 {
416 return memcmp(a, b, uiCount * sizeof(T)) == 0;
417 }
418 else
419 {
420 PL_CHECK_CLASS(T);
421
422 for (size_t i = 0; i < uiCount; i++)
423 {
424 if (!(a[i] == b[i]))
425 return false;
426 }
427 return true;
428 }
429}
430
431template <typename T>
432PL_ALWAYS_INLINE void plMemoryUtils::ZeroFill(T* pDestination, size_t uiCount)
433{
434 memset(pDestination, 0, uiCount * sizeof(T));
435}
436
437template <typename T, size_t N>
438PL_ALWAYS_INLINE void plMemoryUtils::ZeroFillArray(T (&destination)[N])
439{
440 return ZeroFill(destination, N);
441}
442
443template <typename T>
444PL_ALWAYS_INLINE void plMemoryUtils::PatternFill(T* pDestination, plUInt8 uiBytePattern, size_t uiCount)
445{
446 memset(pDestination, uiBytePattern, uiCount * sizeof(T));
447}
448
449template <typename T, size_t N>
450PL_ALWAYS_INLINE void plMemoryUtils::PatternFillArray(T (&destination)[N], plUInt8 uiBytePattern)
451{
452 return PatternFill(destination, uiBytePattern, N);
453}
454
455template <typename T>
456PL_ALWAYS_INLINE plInt32 plMemoryUtils::Compare(const T* a, const T* b, size_t uiCount /*= 1*/)
457{
458 return memcmp(a, b, uiCount * sizeof(T));
459}
460
461PL_ALWAYS_INLINE plInt32 plMemoryUtils::RawByteCompare(const void* a, const void* b, size_t uiNumBytesToCompare)
462{
463 return memcmp(a, b, uiNumBytesToCompare);
464}
465
466template <typename T>
467PL_ALWAYS_INLINE T* plMemoryUtils::AddByteOffset(T* pPtr, std::ptrdiff_t offset)
468{
469 return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(pPtr) + offset);
470}
471
472template <typename T>
473PL_ALWAYS_INLINE T* plMemoryUtils::AlignBackwards(T* pPtr, size_t uiAlignment)
474{
475 return reinterpret_cast<T*>(reinterpret_cast<size_t>(pPtr) & ~(uiAlignment - 1));
476}
477
478template <typename T>
479PL_ALWAYS_INLINE T* plMemoryUtils::AlignForwards(T* pPtr, size_t uiAlignment)
480{
481 return reinterpret_cast<T*>((reinterpret_cast<size_t>(pPtr) + uiAlignment - 1) & ~(uiAlignment - 1));
482}
483
484template <typename T>
485PL_ALWAYS_INLINE T plMemoryUtils::AlignSize(T uiSize, T uiAlignment)
486{
487 return ((uiSize + (uiAlignment - 1)) & ~(uiAlignment - 1));
488}
489
490template <typename T>
491PL_ALWAYS_INLINE bool plMemoryUtils::IsAligned(const T* pPtr, size_t uiAlignment)
492{
493 return (reinterpret_cast<size_t>(pPtr) & (uiAlignment - 1)) == 0;
494}
495
496template <typename T>
497PL_ALWAYS_INLINE bool plMemoryUtils::IsSizeAligned(T uiSize, T uiAlignment)
498{
499 return (uiSize & (uiAlignment - 1)) == 0;
500}
501
502#undef PL_CHECK_CLASS
static T * AlignForwards(T *pPtr, size_t uiAlignment)
Aligns the pointer ptr by moving its address forwards to the next multiple of uiAlignment.
static void CopyConstruct(Destination *pDestination, const Source &copy, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination, by creating uiCount copies of c...
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.
static void CopyConstructArray(T *pDestination, const T *pSource, size_t uiCount)
Constructs uiCount objects of type T in a raw buffer at pDestination from an existing array of object...
static T * AlignBackwards(T *pPtr, size_t uiAlignment)
Aligns the pointer ptr by moving its address backwards to the previous multiple of uiAlignment.
static ConstructorFunction MakeConstructorFunction()
Returns a function pointer to construct an instance of T. Returns nullptr for trivial types.
static void CopyOrMoveConstruct(Destination *pDestination, Source &&source)
This function will either move call MoveConstruct or CopyConstruct for a single element source,...
static bool IsEqual(const T *a, const T *b, size_t uiCount=1)
Tests if objects of type T from pSource and pDestination are equal.
static CopyConstructorFunction MakeCopyConstructorFunction()
Returns a function pointer to copy construct an instance of T.
static void ZeroFillArray(T(&destination)[N])
Zeros every byte in the provided memory buffer.
static void RelocateConstruct(T *pDestination, T *pSource, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination from an existing array of object...
static void Construct(T *pDestination, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination.
static DestructorFunction MakeDestructorFunction()
Returns a function pointer to destruct an instance of T. Returns nullptr for POD-types.
static bool IsSizeAligned(T uiSize, T uiAlignment)
Checks whether the given size is aligned.
static void RawByteCopy(void *pDestination, const void *pSource, size_t uiNumBytesToCopy)
Copies exactly uiNumBytesToCopy from pSource to pDestination, independent of the involved types and t...
Definition MemoryUtils_inl.h:197
static void PatternFill(T *pDestination, plUInt8 uiBytePattern, size_t uiCount=1)
Fills every byte of the provided buffer with the given value.
static void Prepend(T *pDestination, const T &source, size_t uiCount)
Moves uiCount objects in pDestination by one object and copies source to the free space.
static plInt32 RawByteCompare(const void *a, const void *b, size_t uiNumBytesToCompare)
Compares exactly uiNumBytesToCompare from a and b, independent of the involved types and their sizes.
Definition MemoryUtils_inl.h:461
static void Copy(T *pDestination, const T *pSource, size_t uiCount=1)
Copies objects of type T from pSource to pDestination.
static void PatternFillArray(T(&destination)[N], plUInt8 uiBytePattern)
Fills every byte of the provided buffer with the given value.
static void MoveConstruct(T *pDestination, T &&source)
Constructs an object of type T in a raw buffer at pDestination, by using move construction from sourc...
static plInt32 Compare(const T *a, const T *b, size_t uiCount=1)
Compares two buffers of raw memory byte wise.
static bool IsAligned(const T *pPtr, size_t uiAlignment)
Checks whether ptr is aligned to a memory address that is a multiple of uiAlignment.
static void RelocateOverlapped(T *pDestination, T *pSource, size_t uiCount=1)
Moves objects of type T from pSource to pDestination.
static void CopyOverlapped(T *pDestination, const T *pSource, size_t uiCount=1)
Copies objects of type T from pSource to pDestination.
static void ZeroFill(T *pDestination, size_t uiCount=1)
Zeros every byte in the provided memory buffer.
static T AlignSize(T uiSize, T uiAlignment)
Aligns the given size uiSize by rounding up to the next multiple of the size.
static void Relocate(T *pDestination, T *pSource, size_t uiCount=1)
Moves objects of type T from pSource to pDestination.
static void Destruct(T *pDestination, size_t uiCount=1)
Destructs uiCount objects of type T at pDestination.
If there is an % operator which takes a plTypeIsMemRelocatable and returns a CompileTimeTrueType T is...
Definition TypeTraits.h:67
If there is an % operator which takes a TypeIsPod and returns a CompileTimeTrueType T is Pod....
Definition TypeTraits.h:43