Plasma Engine  2.0
Loading...
Searching...
No Matches
Deque_inl.h
1#pragma once
2
3#include <Foundation/Math/Math.h>
4
5#define REDUCE_SIZE(iReduction) \
6 m_iReduceSizeTimer -= iReduction; \
7 if (m_iReduceSizeTimer <= 0) \
8 ReduceSize(0);
9
10#define RESERVE(uiCount) \
11 if (uiCount > m_uiCount) \
12 { \
13 m_uiMaxCount = plMath::Max(m_uiMaxCount, uiCount); \
14 if ((m_uiFirstElement <= 0) || (GetCurMaxCount() < uiCount)) \
15 Reserve(uiCount); \
16 }
17
18#define CHUNK_SIZE(Type) (4096 / sizeof(Type) < 32 ? 32 : 4096 / sizeof(Type))
19//(sizeof(Type) <= 8 ? 256 : (sizeof(Type) <= 16 ? 128 : (sizeof(Type) <= 32 ? 64 : 32))) // although this is Pow(2), this is slower than just having
20// larger chunks
21
22template <typename T, bool Construct>
24{
25 m_pAllocator = pAllocator;
26 m_pChunks = nullptr;
27 m_uiChunks = 0;
28 m_uiFirstElement = 0;
29 m_uiCount = 0;
30 m_uiAllocatedChunks = 0;
31 m_uiMaxCount = 0;
32
33 ResetReduceSizeCounter();
34
35#if PL_ENABLED(PL_COMPILE_FOR_DEBUG)
36 m_uiChunkSize = CHUNK_SIZE(T);
37#endif
38}
40template <typename T, bool Construct>
43 Constructor(pAllocator);
44}
46template <typename T, bool Construct>
48{
49 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
50
51 Constructor(pAllocator);
52
53 *this = rhs;
54}
55
56template <typename T, bool Construct>
58{
59 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
61 Constructor(pAllocator);
62
63 *this = std::move(rhs);
64}
65
66template <typename T, bool Construct>
69 DeallocateAll();
70}
72template <typename T, bool Construct>
74{
75 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
76
77 Clear(); // does not deallocate anything
78 RESERVE(rhs.m_uiCount); // allocates data, if required
79 m_uiCount = rhs.m_uiCount;
80
81 // copy construct all the elements
82 for (plUInt32 i = 0; i < rhs.m_uiCount; ++i)
83 plMemoryUtils::CopyConstruct(&ElementAt(i), rhs[i], 1);
84}
85
86template <typename T, bool Construct>
88{
89 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
90
91 if (m_pAllocator != rhs.m_pAllocator)
92 operator=(static_cast<plDequeBase<T, Construct>&>(rhs));
93 else
94 {
95 DeallocateAll();
96
97 m_uiCount = rhs.m_uiCount;
98 m_iReduceSizeTimer = rhs.m_iReduceSizeTimer;
99 m_pChunks = rhs.m_pChunks;
100 m_uiAllocatedChunks = rhs.m_uiAllocatedChunks;
101 m_uiChunks = rhs.m_uiChunks;
102 m_uiFirstElement = rhs.m_uiFirstElement;
103 m_uiMaxCount = rhs.m_uiMaxCount;
105 rhs.m_uiCount = 0;
106 rhs.m_pChunks = nullptr;
107 rhs.m_uiAllocatedChunks = 0;
108 rhs.m_uiChunks = 0;
109 rhs.m_uiFirstElement = 0;
110 rhs.m_uiMaxCount = 0;
111 }
112}
114template <typename T, bool Construct>
117 if (GetCount() != rhs.GetCount())
118 return false;
120 for (plUInt32 i = 0; i < GetCount(); ++i)
121 {
122 if ((*this)[i] != rhs[i])
123 return false;
124 }
126 return true;
127}
129template <typename T, bool Construct>
132 if (Construct)
133 {
134 for (plUInt32 i = 0; i < m_uiCount; ++i)
135 plMemoryUtils::Destruct<T>(&operator[](i), 1);
136 }
138 m_uiCount = 0;
139
140 // since it is much more likely that data is appended at the back of the deque,
141 // we do not use the center of the chunk index array, but instead set the first element
142 // somewhere more at the front
144 // set the first element to a position that allows to add elements at the front
145 if (m_uiChunks > 30)
146 m_uiFirstElement = CHUNK_SIZE(T) * 16;
147 else if (m_uiChunks > 8)
148 m_uiFirstElement = CHUNK_SIZE(T) * 4;
149 else if (m_uiChunks > 1)
150 m_uiFirstElement = CHUNK_SIZE(T) * 1;
151 else if (m_uiChunks > 0)
152 m_uiFirstElement = 1; // with the current implementation this case should not be possible.
153 else
154 m_uiFirstElement = 0; // must also work, if Clear is called on a deallocated (not yet allocated) deque
156
157template <typename T, bool Construct>
159{
160 // This is the function where all the complicated stuff happens.
161 // The basic idea is as follows:
162 // * do not do anything unless necessary
163 // * if the index array (for the redirection) is already large enough to handle the 'address space', try to reuse it
164 // by moving data around (shift it left or right), if necessary
165 // * if the chunk index array is not large enough to handle the required amount of redirections, allocate a new
166 // index array and move the old data over
167 // This function does not allocate any of the chunks itself (that's what 'ElementAt' does), it only takes care
168 // that the amount of reserved elements can be redirected once the deque is enlarged accordingly.
169
170 // no need to change anything in this case
171 if (uiCount <= m_uiCount)
172 return;
173
174 // keeps track of the largest amount of used elements since the last memory reduction
175 m_uiMaxCount = plMath::Max(m_uiMaxCount, uiCount);
176
177 // if there is enough room to hold all requested elements AND one can prepend at least one element (PushFront)
178 // do not reallocate
179 if ((m_uiFirstElement > 0) && (GetCurMaxCount() >= uiCount))
180 return;
182 const plUInt32 uiCurFirstChunk = GetFirstUsedChunk();
183 const plUInt32 uiRequiredChunks = GetRequiredChunks(uiCount);
184
185 // if we already have enough chunks, just rearrange them
186 if (m_uiChunks > uiRequiredChunks + 1) // have at least one spare chunk for the front, and one for the back
187 {
188 const plUInt32 uiSpareChunks = m_uiChunks - uiRequiredChunks;
189 const plUInt32 uiSpareChunksStart = uiSpareChunks / 2;
190
191 PL_ASSERT_DEBUG(uiSpareChunksStart > 0, "Implementation error.");
192
193 // always leave one spare chunk at the front, to ensure that one can prepend elements
194
195 PL_ASSERT_DEBUG(uiSpareChunksStart != uiCurFirstChunk, "No rearrangement possible.");
196
197 // if the new first active chunk is to the left
198 if (uiSpareChunksStart < uiCurFirstChunk)
199 MoveIndexChunksLeft(uiCurFirstChunk - uiSpareChunksStart);
200 else
201 MoveIndexChunksRight(uiSpareChunksStart - uiCurFirstChunk);
202
203 PL_ASSERT_DEBUG(m_uiFirstElement > 0, "Did not achieve the desired effect.");
204 PL_ASSERT_DEBUG(GetCurMaxCount() >= uiCount, "Did not achieve the desired effect ({0} >= {1}).", GetCurMaxCount(), uiCount);
205 }
206 else
207 {
208 const plUInt32 uiReallocSize = 16 + uiRequiredChunks + 16;
209
210 T** pNewChunksArray = PL_NEW_RAW_BUFFER(m_pAllocator, T*, uiReallocSize);
211 plMemoryUtils::ZeroFill(pNewChunksArray, uiReallocSize);
212
213 const plUInt32 uiFirstUsedChunk = m_uiFirstElement / CHUNK_SIZE(T);
214
215 // move all old chunks over
216 plUInt32 pos = 16;
217
218 // first the used chunks at the start of the new array
219 for (plUInt32 i = 0; i < m_uiChunks - uiFirstUsedChunk; ++i)
220 {
221 pNewChunksArray[pos] = m_pChunks[uiFirstUsedChunk + i];
222 ++pos;
223 }
224
225 m_uiFirstElement -= uiFirstUsedChunk * CHUNK_SIZE(T);
226
227 // then the unused chunks at the end of the new array
228 for (plUInt32 i = 0; i < uiFirstUsedChunk; ++i)
229 {
230 pNewChunksArray[pos] = m_pChunks[i];
231 ++pos;
232 }
233
234 m_uiFirstElement += 16 * CHUNK_SIZE(T);
235
236 PL_ASSERT_DEBUG(m_uiFirstElement == (16 * CHUNK_SIZE(T)) + (m_uiFirstElement % CHUNK_SIZE(T)), "");
237
238
239 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks);
240 m_pChunks = pNewChunksArray;
241 m_uiChunks = uiReallocSize;
242 }
243}
244
245template <typename T, bool Construct>
247{
248 ResetReduceSizeCounter();
249
250 if (IsEmpty())
251 {
252 DeallocateAll();
253 return;
254 }
255
256 // this will deallocate ALL unused chunks
257 DeallocateUnusedChunks(GetRequiredChunks(m_uiCount));
258
259 // reduces the size of the index array, but keeps some spare pointers, so that scaling up is still possible without reallocation
260 CompactIndexArray(0);
261}
262
263template <typename T, bool Construct>
265{
266 plMath::Swap(this->m_pAllocator, other.m_pAllocator);
267 plMath::Swap(this->m_pChunks, other.m_pChunks);
268 plMath::Swap(this->m_uiChunks, other.m_uiChunks);
269 plMath::Swap(this->m_uiFirstElement, other.m_uiFirstElement);
270 plMath::Swap(this->m_uiCount, other.m_uiCount);
271 plMath::Swap(this->m_uiAllocatedChunks, other.m_uiAllocatedChunks);
272 plMath::Swap(this->m_iReduceSizeTimer, other.m_iReduceSizeTimer);
273 plMath::Swap(this->m_uiMaxCount, other.m_uiMaxCount);
274}
275
276template <typename T, bool Construct>
277void plDequeBase<T, Construct>::CompactIndexArray(plUInt32 uiMinChunksToKeep)
278{
279 const plUInt32 uiRequiredChunks = plMath::Max<plUInt32>(1, GetRequiredChunks(m_uiCount));
280 uiMinChunksToKeep = plMath::Max(uiRequiredChunks, uiMinChunksToKeep);
281
282 // keep some spare pointers for scaling the deque up again
283 const plUInt32 uiChunksToKeep = 16 + uiMinChunksToKeep + 16;
284
285 // only reduce the index array, if we can reduce its size at least to half (the +4 is for the very small cases)
286 if (uiChunksToKeep + 4 >= m_uiChunks / 2)
287 return;
288
289 T** pNewChunkArray = PL_NEW_RAW_BUFFER(m_pAllocator, T*, uiChunksToKeep);
290 plMemoryUtils::ZeroFill<T*>(pNewChunkArray, uiChunksToKeep);
291
292 const plUInt32 uiFirstChunk = GetFirstUsedChunk();
293
294 // makes sure that no more than this amount of chunks is still allocated -> those can be copied over
295 DeallocateUnusedChunks(uiChunksToKeep);
296
297 // moves the used chunks into the new array
298 for (plUInt32 i = 0; i < uiRequiredChunks; ++i)
299 {
300 pNewChunkArray[16 + i] = m_pChunks[uiFirstChunk + i];
301 m_pChunks[uiFirstChunk + i] = nullptr;
302 }
303
304 // copy all still allocated chunks over to the new index array
305 // since we just deallocated enough chunks, all that are found can be copied over as spare chunks
306 {
307 plUInt32 iPos = 0;
308 for (plUInt32 i = 0; i < uiFirstChunk; ++i)
309 {
310 if (m_pChunks[i])
311 {
312 PL_ASSERT_DEBUG(iPos < 16 || ((iPos >= 16 + uiRequiredChunks) && (iPos < uiChunksToKeep)), "Implementation error.");
313
314 pNewChunkArray[iPos] = m_pChunks[i];
315 m_pChunks[i] = nullptr;
316 ++iPos;
317
318 if (iPos == 16)
319 iPos += uiRequiredChunks;
320 }
321 }
322
323 for (plUInt32 i = GetLastUsedChunk() + 1; i < m_uiChunks; ++i)
324 {
325 if (m_pChunks[i])
326 {
327 PL_ASSERT_DEBUG(iPos < 16 || ((iPos >= 16 + uiRequiredChunks) && (iPos < uiChunksToKeep)), "Implementation error.");
328
329 pNewChunkArray[iPos] = m_pChunks[i];
330 m_pChunks[i] = nullptr;
331 ++iPos;
332
333 if (iPos == 16)
334 iPos += uiRequiredChunks;
335 }
336 }
337 }
338
339 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks);
340 m_pChunks = pNewChunkArray;
341 m_uiChunks = uiChunksToKeep;
342 m_uiFirstElement = (16 * CHUNK_SIZE(T)) + (m_uiFirstElement % CHUNK_SIZE(T));
343}
344
345template <typename T, bool Construct>
347{
348 const plUInt32 uiOldCount = m_uiCount;
349 const plUInt32 uiNewCount = uiCount;
350
351 if (uiNewCount > uiOldCount)
352 {
353 // grow the deque
354
355 RESERVE(uiNewCount);
356 m_uiCount = uiNewCount;
357
358 if (Construct)
359 {
360 // default construct the new elements
361 for (plUInt32 i = uiOldCount; i < uiNewCount; ++i)
363 }
364 else
365 {
366 for (plUInt32 i = uiOldCount; i < uiNewCount; ++i)
367 ElementAt(i);
368 }
369 }
370 else
371 {
372 if (Construct)
373 {
374 // destruct elements at the end of the deque
375 for (plUInt32 i = uiNewCount; i < uiOldCount; ++i)
376 plMemoryUtils::Destruct(&operator[](i), 1);
377 }
378
379 m_uiCount = uiNewCount;
380
381 // if enough elements have been destructed, trigger a size reduction (the first time will not deallocate anything though)
382 ReduceSize(uiOldCount - uiNewCount);
383 }
384}
385
386template <typename T, bool Construct>
387template <typename> // Second template needed so that the compiler does only instantiate it when called. Otherwise the static_assert would trigger
388// early.
390{
391 static_assert(plIsPodType<T>::value == plTypeIsPod::value, "SetCountUninitialized is only supported for POD types.");
392
393 const plUInt32 uiOldCount = m_uiCount;
394 const plUInt32 uiNewCount = uiCount;
395
396 if (uiNewCount > uiOldCount)
397 {
398 // grow the deque
399
400 RESERVE(uiNewCount);
401 m_uiCount = uiNewCount;
402
403 for (plUInt32 i = uiOldCount; i < uiNewCount; ++i)
404 ElementAt(i);
405 }
406 else
407 {
408 if (Construct)
409 {
410 // destruct elements at the end of the deque
411 for (plUInt32 i = uiNewCount; i < uiOldCount; ++i)
412 plMemoryUtils::Destruct(&operator[](i), 1);
413 }
414
415 m_uiCount = uiNewCount;
416
417 // if enough elements have been destructed, trigger a size reduction (the first time will not deallocate anything though)
418 ReduceSize(uiOldCount - uiNewCount);
419 }
420}
421
422template <typename T, bool Construct>
424{
425 if (uiCount > m_uiCount)
426 {
427 SetCount(uiCount);
428 }
429}
430
431template <typename T, bool Construct>
432inline plUInt32 plDequeBase<T, Construct>::GetContiguousRange(plUInt32 uiIndex) const
433{
434 PL_ASSERT_DEV(uiIndex < m_uiCount, "The deque has {0} elements. Cannot access element {1}.", m_uiCount, uiIndex);
435
436 const plUInt32 uiChunkSize = CHUNK_SIZE(T);
437
438 const plUInt32 uiRealIndex = m_uiFirstElement + uiIndex;
439 const plUInt32 uiChunkOffset = uiRealIndex % uiChunkSize;
440
441 const plUInt32 uiRange = uiChunkSize - uiChunkOffset;
442
443 return plMath::Min(uiRange, GetCount() - uiIndex);
444}
445
446template <typename T, bool Construct>
447inline T& plDequeBase<T, Construct>::operator[](plUInt32 uiIndex)
448{
449 PL_ASSERT_DEBUG(uiIndex < m_uiCount, "The deque has {0} elements. Cannot access element {1}.", m_uiCount, uiIndex);
450
451 const plUInt32 uiRealIndex = m_uiFirstElement + uiIndex;
452
453 const plUInt32 uiChunkIndex = uiRealIndex / CHUNK_SIZE(T);
454 const plUInt32 uiChunkOffset = uiRealIndex % CHUNK_SIZE(T);
455
456 return m_pChunks[uiChunkIndex][uiChunkOffset];
457}
458
459template <typename T, bool Construct>
460inline const T& plDequeBase<T, Construct>::operator[](plUInt32 uiIndex) const
461{
462 PL_ASSERT_DEBUG(uiIndex < m_uiCount, "The deque has {0} elements. Cannot access element {1}.", m_uiCount, uiIndex);
463
464 const plUInt32 uiRealIndex = m_uiFirstElement + uiIndex;
465
466 const plUInt32 uiChunkIndex = uiRealIndex / CHUNK_SIZE(T);
467 const plUInt32 uiChunkOffset = uiRealIndex % CHUNK_SIZE(T);
468
469 return m_pChunks[uiChunkIndex][uiChunkOffset];
470}
471
472template <typename T, bool Construct>
474{
475 RESERVE(m_uiCount + 1);
476 ++m_uiCount;
477
478 T* pElement = &ElementAt(m_uiCount - 1);
479
480 if (Construct)
481 {
483 }
484
485 return *pElement;
486}
487
488template <typename T, bool Construct>
490{
491 RESERVE(m_uiCount + 1);
492 ++m_uiCount;
493
494 T* pElement = &ElementAt(m_uiCount - 1);
495
496 if (Construct)
497 {
499 }
500}
501
502template <typename T, bool Construct>
503inline void plDequeBase<T, Construct>::PushBack(const T& element)
504{
505 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
506
507 RESERVE(m_uiCount + 1);
508 ++m_uiCount;
509
510 plMemoryUtils::CopyConstruct(&ElementAt(m_uiCount - 1), element, 1);
511}
512
513template <typename T, bool Construct>
515{
516 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
517
518 RESERVE(m_uiCount + 1);
519 ++m_uiCount;
520
521 plMemoryUtils::MoveConstruct<T>(&ElementAt(m_uiCount - 1), std::move(element));
522}
523
524template <typename T, bool Construct>
525inline void plDequeBase<T, Construct>::PopBack(plUInt32 uiElements)
526{
527 PL_ASSERT_DEV(uiElements <= GetCount(), "Cannot remove {0} elements, the deque only contains {1} elements.", uiElements, GetCount());
528
529 for (plUInt32 i = 0; i < uiElements; ++i)
530 {
531 if (Construct)
532 plMemoryUtils::Destruct(&operator[](m_uiCount - 1), 1);
533
534 --m_uiCount;
535 }
536
537 // might trigger a memory reduction
538 REDUCE_SIZE(uiElements);
539}
540
541template <typename T, bool Construct>
542inline void plDequeBase<T, Construct>::PushFront(const T& element)
543{
544 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
545
546 RESERVE(m_uiCount + 1);
547 ++m_uiCount;
548 --m_uiFirstElement;
549
550 plMemoryUtils::CopyConstruct(&ElementAt(0), element, 1);
551}
552
553template <typename T, bool Construct>
555{
556 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
557
558 RESERVE(m_uiCount + 1);
559 ++m_uiCount;
560 --m_uiFirstElement;
561
562 plMemoryUtils::MoveConstruct<T>(&ElementAt(0), std::move(element));
563}
564
565template <typename T, bool Construct>
567{
568 RESERVE(m_uiCount + 1);
569 ++m_uiCount;
570 --m_uiFirstElement;
571
572 T* pElement = &ElementAt(0);
573
574 if (Construct)
575 {
577 }
578}
579
580template <typename T, bool Construct>
581inline void plDequeBase<T, Construct>::PopFront(plUInt32 uiElements)
582{
583 PL_ASSERT_DEV(uiElements <= GetCount(), "Cannot remove {0} elements, the deque only contains {1} elements.", uiElements, GetCount());
584
585 for (plUInt32 i = 0; i < uiElements; ++i)
586 {
587 if (Construct)
588 {
589 plMemoryUtils::Destruct(&operator[](0), 1);
590 }
591
592 --m_uiCount;
593 ++m_uiFirstElement;
594 }
595
596 // might trigger a memory reduction
597 REDUCE_SIZE(uiElements);
598}
599
600template <typename T, bool Construct>
601PL_ALWAYS_INLINE bool plDequeBase<T, Construct>::IsEmpty() const
602{
603 return m_uiCount == 0;
604}
605
606template <typename T, bool Construct>
607PL_ALWAYS_INLINE plUInt32 plDequeBase<T, Construct>::GetCount() const
608{
609 return m_uiCount;
610}
611
612template <typename T, bool Construct>
613PL_ALWAYS_INLINE const T& plDequeBase<T, Construct>::PeekFront() const
614{
615 return operator[](0);
616}
617
618template <typename T, bool Construct>
620{
621 return operator[](0);
622}
623
624template <typename T, bool Construct>
625PL_ALWAYS_INLINE const T& plDequeBase<T, Construct>::PeekBack() const
626{
627 return operator[](m_uiCount - 1);
628}
629
630template <typename T, bool Construct>
632{
633 return operator[](m_uiCount - 1);
634}
635
636template <typename T, bool Construct>
637PL_ALWAYS_INLINE bool plDequeBase<T, Construct>::Contains(const T& value) const
638{
639 return IndexOf(value) != plInvalidIndex;
640}
641
642template <typename T, bool Construct>
643plUInt32 plDequeBase<T, Construct>::IndexOf(const T& value, plUInt32 uiStartIndex) const
644{
645 for (plUInt32 i = uiStartIndex; i < m_uiCount; ++i)
646 {
647 if (plMemoryUtils::IsEqual(&operator[](i), &value))
648 return i;
649 }
650
651 return plInvalidIndex;
652}
653
654template <typename T, bool Construct>
655plUInt32 plDequeBase<T, Construct>::LastIndexOf(const T& value, plUInt32 uiStartIndex) const
656{
657 for (plUInt32 i = plMath::Min(uiStartIndex, m_uiCount); i-- > 0;)
658 {
659 if (plMemoryUtils::IsEqual(&operator[](i), &value))
660 return i;
661 }
662 return plInvalidIndex;
663}
664
665template <typename T, bool Construct>
667{
668 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
669
670 PL_ASSERT_DEV(uiIndex < m_uiCount, "Cannot remove element {0}, the deque only contains {1} elements.", uiIndex, m_uiCount);
671
672 if (uiIndex + 1 < m_uiCount) // do not copy over the same element, if uiIndex is actually the last element
673 operator[](uiIndex) = PeekBack();
674
675 PopBack();
676}
677
678template <typename T, bool Construct>
679PL_FORCE_INLINE void plDequeBase<T, Construct>::MoveIndexChunksLeft(plUInt32 uiChunkDiff)
680{
681 const plUInt32 uiCurFirstChunk = GetFirstUsedChunk();
682 const plUInt32 uiRemainingChunks = m_uiChunks - uiCurFirstChunk;
683 const plUInt32 uiNewFirstChunk = uiCurFirstChunk - uiChunkDiff;
684
685 // ripple the chunks from the back to the front (in place)
686 for (plUInt32 front = 0; front < uiRemainingChunks; ++front)
687 plMath::Swap(m_pChunks[uiNewFirstChunk + front], m_pChunks[front + uiCurFirstChunk]);
688
689 // just ensures that the following subtraction is possible
690 PL_ASSERT_DEBUG(m_uiFirstElement > uiChunkDiff * CHUNK_SIZE(T), "");
691
692 // adjust which element is the first by how much the index array has been moved
693 m_uiFirstElement -= uiChunkDiff * CHUNK_SIZE(T);
694}
695
696template <typename T, bool Construct>
697PL_FORCE_INLINE void plDequeBase<T, Construct>::MoveIndexChunksRight(plUInt32 uiChunkDiff)
698{
699 const plUInt32 uiCurFirstChunk = GetFirstUsedChunk();
700 const plUInt32 uiLastChunk = (m_uiCount == 0) ? (m_uiFirstElement / CHUNK_SIZE(T)) : ((m_uiFirstElement + m_uiCount - 1) / CHUNK_SIZE(T));
701 const plUInt32 uiCopyChunks = (uiLastChunk - uiCurFirstChunk) + 1;
702
703 // ripple the chunks from the front to the back (in place)
704 for (plUInt32 i = 0; i < uiCopyChunks; ++i)
705 plMath::Swap(m_pChunks[uiLastChunk - i], m_pChunks[uiLastChunk + uiChunkDiff - i]);
706
707 // adjust which element is the first by how much the index array has been moved
708 m_uiFirstElement += uiChunkDiff * CHUNK_SIZE(T);
709}
710
711template <typename T, bool Construct>
712PL_ALWAYS_INLINE plUInt32 plDequeBase<T, Construct>::GetFirstUsedChunk() const
713{
714 return m_uiFirstElement / CHUNK_SIZE(T);
715}
716
717template <typename T, bool Construct>
718PL_FORCE_INLINE plUInt32 plDequeBase<T, Construct>::GetLastUsedChunk(plUInt32 uiAtSize) const
719{
720 if (uiAtSize == 0)
721 return GetFirstUsedChunk();
722
723 return (m_uiFirstElement + uiAtSize - 1) / CHUNK_SIZE(T);
724}
725
726template <typename T, bool Construct>
727PL_ALWAYS_INLINE plUInt32 plDequeBase<T, Construct>::GetLastUsedChunk() const
728{
729 return GetLastUsedChunk(m_uiCount);
730}
731
732template <typename T, bool Construct>
733PL_FORCE_INLINE plUInt32 plDequeBase<T, Construct>::GetRequiredChunks(plUInt32 uiAtSize) const
734{
735 if (uiAtSize == 0)
736 return 0;
737
738 return GetLastUsedChunk(uiAtSize) - GetFirstUsedChunk() + 1;
739}
740
741template <typename T, bool Construct>
743{
744 if (m_uiAllocatedChunks <= uiMaxChunks)
745 return;
746
747 // check all unused chunks at the end, deallocate all that are allocated
748 for (plUInt32 i = GetLastUsedChunk() + 1; i < m_uiChunks; ++i)
749 {
750 if (m_pChunks[i])
751 {
752 --m_uiAllocatedChunks;
753 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks[i]);
754
755 if (m_uiAllocatedChunks <= uiMaxChunks)
756 return;
757 }
758 }
759
760 // check all unused chunks at the front, deallocate all that are allocated
761 const plUInt32 uiFirstChunk = GetFirstUsedChunk();
762
763 for (plUInt32 i = 0; i < uiFirstChunk; ++i)
764 {
765 if (m_pChunks[i])
766 {
767 --m_uiAllocatedChunks;
768 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks[i]);
769
770 if (m_uiAllocatedChunks <= uiMaxChunks)
771 return;
772 }
773 }
774}
775
776template <typename T, bool Construct>
778{
779 m_iReduceSizeTimer = CHUNK_SIZE(T) * 8; // every time 8 chunks might be unused -> check whether to reduce the deque's size
780}
781
782template <typename T, bool Construct>
783void plDequeBase<T, Construct>::ReduceSize(plInt32 iReduction)
784{
785 m_iReduceSizeTimer -= iReduction;
786
787 // only trigger the size reduction every once in a while (after enough size reduction that actually a few chunks might be unused)
788 if (m_iReduceSizeTimer > 0)
789 return;
790
791 ResetReduceSizeCounter();
792
793 // we keep this amount of chunks
794 // m_uiMaxCount will be adjusted over time
795 // if the deque is shrunk and operates in this state long enough, m_uiMaxCount will be reduced more and more
796 const plUInt32 uiMaxChunks = (m_uiMaxCount / CHUNK_SIZE(T)) + 3; // +1 because of rounding, +2 spare chunks
797
798 PL_ASSERT_DEBUG(uiMaxChunks >= GetRequiredChunks(m_uiCount), "Implementation Error.");
799
800 DeallocateUnusedChunks(uiMaxChunks);
801
802 // lerp between the current MaxCount and the actually active number of elements
803 // m_uiMaxCount is never smaller than m_uiCount, but m_uiCount might be smaller
804 // thus m_uiMaxCount might be reduced over time
805 m_uiMaxCount = plMath::Max(m_uiCount, (m_uiMaxCount / 2) + (m_uiCount / 2));
806
807 // Should we really adjust the size of the index array here?
808 CompactIndexArray(uiMaxChunks);
809}
810
811template <typename T, bool Construct>
812PL_ALWAYS_INLINE plUInt32 plDequeBase<T, Construct>::GetCurMaxCount() const
813{
814 return m_uiChunks * CHUNK_SIZE(T) - m_uiFirstElement;
815}
816
817template <typename T, bool Construct>
819{
820 // first search for an unused, but already allocated, chunk and reuse it, if possible
821 const plUInt32 uiCurFirstChunk = GetFirstUsedChunk();
822
823 // search the unused blocks at the start
824 for (plUInt32 i = 0; i < uiCurFirstChunk; ++i)
825 {
826 if (m_pChunks[i])
827 {
828 T* pChunk = m_pChunks[i];
829 m_pChunks[i] = nullptr;
830 return pChunk;
831 }
832 }
833
834 const plUInt32 uiCurLastChunk = GetLastUsedChunk();
835
836 // search the unused blocks at the end
837 for (plUInt32 i = m_uiChunks - 1; i > uiCurLastChunk; --i)
838 {
839 if (m_pChunks[i])
840 {
841 T* pChunk = m_pChunks[i];
842 m_pChunks[i] = nullptr;
843 return pChunk;
844 }
845 }
846
847 // nothing unused found, allocate a new block
848 ResetReduceSizeCounter();
849 ++m_uiAllocatedChunks;
850 return PL_NEW_RAW_BUFFER(m_pAllocator, T, CHUNK_SIZE(T));
851}
852
853template <typename T, bool Construct>
854T& plDequeBase<T, Construct>::ElementAt(plUInt32 uiIndex)
855{
856 PL_ASSERT_DEBUG(uiIndex < m_uiCount, "");
857
858 const plUInt32 uiRealIndex = m_uiFirstElement + uiIndex;
859
860 const plUInt32 uiChunkIndex = uiRealIndex / CHUNK_SIZE(T);
861 const plUInt32 uiChunkOffset = uiRealIndex % CHUNK_SIZE(T);
862
863 PL_ASSERT_DEBUG(uiChunkIndex < m_uiChunks, "");
864
865 if (m_pChunks[uiChunkIndex] == nullptr)
866 m_pChunks[uiChunkIndex] = GetUnusedChunk();
867
868 return m_pChunks[uiChunkIndex][uiChunkOffset];
869}
870
871template <typename T, bool Construct>
873{
874 Clear();
875
876 plUInt32 i = 0;
877 while (m_uiAllocatedChunks > 0)
878 {
879 if (m_pChunks[i])
880 {
881 --m_uiAllocatedChunks;
882 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks[i]);
883 }
884
885 ++i;
886 }
887
888 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pChunks);
889
890 Constructor(m_pAllocator);
891}
892
893template <typename T, bool Construct>
895{
896 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
897
898 PL_ASSERT_DEV(uiIndex < m_uiCount, "Out of bounds access. Array has {0} elements, trying to remove element at index {1}.", m_uiCount, uiIndex);
899
900 for (plUInt32 i = uiIndex + 1; i < m_uiCount; ++i)
901 {
902 plMemoryUtils::CopyOverlapped(&operator[](i - 1), &operator[](i), 1);
903 }
904
905 PopBack();
906}
907
908template <typename T, bool Construct>
910{
911 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
912
913 plUInt32 uiIndex = IndexOf(value);
914
915 if (uiIndex == plInvalidIndex)
916 return false;
917
918 RemoveAtAndCopy(uiIndex);
919 return true;
920}
921
922template <typename T, bool Construct>
924{
925 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
926
927 plUInt32 uiIndex = IndexOf(value);
928
929 if (uiIndex == plInvalidIndex)
930 return false;
931
932 RemoveAtAndSwap(uiIndex);
933 return true;
934}
935
936template <typename T, bool Construct>
937void plDequeBase<T, Construct>::InsertAt(plUInt32 uiIndex, const T& value)
938{
939 static_assert(Construct, "This function is not supported on Deques that do not construct their data.");
940
941 // Index 0 inserts before the first element, Index m_uiCount inserts after the last element.
942 PL_ASSERT_DEV(uiIndex <= m_uiCount, "The deque has {0} elements. Cannot insert an element at index {1}.", m_uiCount, uiIndex);
943
944 PushBack();
945
946 for (plUInt32 i = m_uiCount - 1; i > uiIndex; --i)
947 {
948 plMemoryUtils::Copy(&operator[](i), &operator[](i - 1), 1);
949 }
950
951 plMemoryUtils::Copy(&operator[](uiIndex), &value, 1);
952}
953
954template <typename T, bool Construct>
955template <typename Comparer>
956void plDequeBase<T, Construct>::Sort(const Comparer& comparer)
957{
958 if (m_uiCount > 1)
959 plSorting::QuickSort(*this, comparer);
960}
961
962template <typename T, bool Construct>
964{
965 if (m_uiCount > 1)
967}
968
969template <typename T, bool Construct>
971{
972 if (m_pChunks == nullptr)
973 return 0;
974
975 plUInt64 res = m_uiChunks * sizeof(T*);
976
977 for (plUInt32 i = 0; i < m_uiChunks; ++i)
978 {
979 if (m_pChunks[i] != nullptr)
980 {
981 res += (plUInt64)(CHUNK_SIZE(T)) * (plUInt64)sizeof(T);
982 }
983 }
984
985 return res;
986}
987
988#undef REDUCE_SIZE
989#undef RESERVE
990
991
992template <typename T, typename A, bool Construct>
994 : plDequeBase<T, Construct>(A::GetAllocator())
995{
996}
997
998template <typename T, typename A, bool Construct>
1000 : plDequeBase<T, Construct>(pAllocator)
1001{
1002}
1003
1004template <typename T, typename A, bool Construct>
1006 : plDequeBase<T, Construct>(other, A::GetAllocator())
1007{
1008}
1009
1010template <typename T, typename A, bool Construct>
1012 : plDequeBase<T, Construct>(std::move(other), other.GetAllocator())
1013{
1014}
1015
1016template <typename T, typename A, bool Construct>
1018 : plDequeBase<T, Construct>(other, A::GetAllocator())
1019{
1020}
1021
1022template <typename T, typename A, bool Construct>
1024 : plDequeBase<T, Construct>(std::move(other), other.GetAllocator())
1025{
1026}
1027
1028template <typename T, typename A, bool Construct>
1030{
1032}
1033
1034template <typename T, typename A, bool Construct>
1036{
1038}
1039
1040template <typename T, typename A, bool Construct>
1042{
1044}
1045
1046template <typename T, typename A, bool Construct>
1048{
1050}
Base class for all memory allocators.
Definition Allocator.h:23
A double ended queue container.
Definition Deque.h:27
void Swap(plDequeBase< T, Construct > &other)
swaps the contents of this deque with another one
Definition Deque_inl.h:264
~plDequeBase()
Destructor.
Definition Deque_inl.h:67
T & operator[](plUInt32 uiIndex)
Accesses the n-th element in the deque.
Definition Deque_inl.h:447
void PopBack(plUInt32 uiElements=1)
Removes the last element from the deque.
Definition Deque_inl.h:525
void Clear()
Destructs all elements and sets the count to zero. Does not deallocate any data.
Definition Deque_inl.h:130
void Sort()
Sort with default comparer.
Definition Deque_inl.h:963
plUInt32 IndexOf(const T &value, plUInt32 uiStartIndex=0) const
Returns the first index at which an element with the given value could be found or plInvalidIndex if ...
Definition Deque_inl.h:643
void Reserve(plUInt32 uiCount)
Rearranges the internal data structures such that the amount of reserved elements can be appended wit...
Definition Deque_inl.h:158
void RemoveAtAndSwap(plUInt32 uiIndex)
Removes the element at the given index and fills the gap with the last element in the deque.
Definition Deque_inl.h:666
plUInt32 LastIndexOf(const T &value, plUInt32 uiStartIndex=plInvalidIndex) const
Returns the last index at which an element with the given value could be found or plInvalidIndex if n...
Definition Deque_inl.h:655
void PushFront()
Adds one default constructed element to the front of the deque.
Definition Deque_inl.h:566
plUInt64 GetHeapMemoryUsage() const
Returns the amount of bytes that are currently allocated on the heap.
Definition Deque_inl.h:970
plUInt32 GetCount() const
Returns the number of active elements in the deque.
Definition Deque_inl.h:607
bool operator==(const plDequeBase< T, Construct > &rhs) const
Comparison operator.
Definition Deque_inl.h:115
void InsertAt(plUInt32 uiIndex, const T &value)
Inserts value at index by shifting all following elements. Valid insert positions are [0; GetCount].
Definition Deque_inl.h:937
plUInt32 GetContiguousRange(plUInt32 uiStartIndex) const
Returns the number of elements after uiStartIndex that are stored in contiguous memory.
Definition Deque_inl.h:432
void Compact()
This function deallocates as much memory as possible to shrink the deque to the bare minimum size tha...
Definition Deque_inl.h:246
T & ExpandAndGetRef()
Grows the deque by one element and returns a reference to the newly created element.
Definition Deque_inl.h:473
void SetCountUninitialized(plUInt32 uiCount)
\Same as SetCount(), but new elements do not get default constructed.
Definition Deque_inl.h:389
void RemoveAtAndCopy(plUInt32 uiIndex)
Removes the element at index and fills the gap by shifting all following elements.
Definition Deque_inl.h:894
void PushBack()
Adds one default constructed element to the back of the deque.
Definition Deque_inl.h:489
void EnsureCount(plUInt32 uiCount)
Ensures the container has at least uiCount elements. Ie. calls SetCount() if the container has fewer ...
Definition Deque_inl.h:423
bool RemoveAndSwap(const T &value)
Removes the first occurrence of value and fills the gap with the last element in the deque.
Definition Deque_inl.h:923
bool Contains(const T &value) const
Checks whether there is any element in the deque with the given value.
Definition Deque_inl.h:637
const T & PeekFront() const
Returns the first element.
Definition Deque_inl.h:613
plDequeBase(plAllocator *pAllocator)
No memory is allocated during construction.
Definition Deque_inl.h:41
void PopFront(plUInt32 uiElements=1)
Removes the first element from the deque.
Definition Deque_inl.h:581
void operator=(const plDequeBase< T, Construct > &rhs)
Assignment operator.
Definition Deque_inl.h:73
const T & PeekBack() const
Returns the last element.
Definition Deque_inl.h:625
bool IsEmpty() const
Checks whether no elements are active in the deque.
Definition Deque_inl.h:601
bool RemoveAndCopy(const T &value)
Removes the first occurrence of value and fills the gap by shifting all following elements.
Definition Deque_inl.h:909
void SetCount(plUInt32 uiCount)
Sets the number of active elements in the deque. All new elements are default constructed....
Definition Deque_inl.h:346
Definition Deque.h:270
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 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 void Construct(T *pDestination, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination.
static void Copy(T *pDestination, const T *pSource, size_t uiCount=1)
Copies objects of type T from pSource to pDestination.
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 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 void Destruct(T *pDestination, size_t uiCount=1)
Destructs uiCount objects of type T at pDestination.
static void QuickSort(Container &inout_container, const Comparer &comparer=Comparer())
Sorts the elements in container using a in-place quick sort implementation (not stable).
Definition Sorting_inl.h:3
constexpr PL_ALWAYS_INLINE T Min(T f1, T f2)
Returns the smaller value, f1 or f2.
Definition Math_inl.h:27
PL_ALWAYS_INLINE void Swap(T &ref_f1, T &ref_f2)
Swaps the values in the two variables f1 and f2.
Definition Math_inl.h:224
constexpr PL_ALWAYS_INLINE T Max(T f1, T f2)
Returns the greater value, f1 or f2.
Definition Math_inl.h:39
A comparer object is used in sorting algorithms to compare to objects of the same type.
Definition Comparer.h:7
If there is an % operator which takes a TypeIsPod and returns a CompileTimeTrueType T is Pod....
Definition TypeTraits.h:43