Nebula
Loading...
Searching...
No Matches
queue.h
Go to the documentation of this file.
1#pragma once
2//------------------------------------------------------------------------------
11#include "core/types.h"
12#include "math/scalar.h"
13#include "util/round.h"
14#include <type_traits>
15#include <new>
16
17
18//------------------------------------------------------------------------------
19namespace Util
20{
21template<class TYPE> class Queue
22{
23public:
29 Queue(const Queue<TYPE>& rhs);
31 Queue(Queue<TYPE>&& rhs);
32
34 void operator=(const Queue<TYPE>& rhs);
36 void operator=(Queue<TYPE>&& rhs);
38 TYPE& operator[](IndexT index) const;
40 bool operator==(const Queue<TYPE>& rhs) const;
42 bool operator!=(const Queue<TYPE>& rhs) const;
44 void Reserve(SizeT num);
46 void Grow();
48 SizeT Size() const;
50 SizeT Capacity() const;
52 bool IsEmpty() const;
54 void Clear();
56 bool Contains(const TYPE& e) const;
58 void EraseIndex(const IndexT i);
59
61 void Enqueue(const TYPE& e);
62 void Enqueue(TYPE&& e);
64 TYPE Dequeue();
66 TYPE& Peek() const;
67
68protected:
69 template<typename X>
70 __forceinline
71 typename std::enable_if<std::is_trivially_destructible<X>::value == false>::type
73 {
74 this->data[idx].~X();
75 }
76
77 template<typename X>
78 __forceinline
79 typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type
81 {
82 // empty
83 }
84
85 template<typename X>
86 __forceinline
87 typename std::enable_if<std::is_trivially_destructible<X>::value == false>::type
89 {
90 for (IndexT i = 0; i < this->size; i++)
91 {
92 this->data[this->MapIndex(i)].~X();
93 }
94 }
95
96 template<typename X>
97 __forceinline
98 typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type
100 {
101 // empty
102 }
103
105 IndexT MapIndex(IndexT index) const;
106 TYPE * data;
107
108 static const SizeT MinGrowSize = 16;
109 static const SizeT MaxGrowSize = 65536;
114};
115
116//------------------------------------------------------------------------------
119template<class TYPE>
121 data(nullptr),
122 grow(16),
123 start(0),
124 size(0),
125 capacity(0)
126{
127 // empty
128}
129
130//------------------------------------------------------------------------------
133template<class TYPE>
135{
136 if (this->data)
137 {
138 this->Clear();
139 ::operator delete[](this->data);
140 }
141 this->data = nullptr;
142}
143
144
145//------------------------------------------------------------------------------
148template<class TYPE>
150 data(nullptr),
151 grow(16),
152 start(0),
153 size(0),
154 capacity(0)
155{
156 this->Reserve(rhs.size);
157 for (IndexT i = 0; i < rhs.size; i++)
158 {
159 ::new ((void*)&this->data[i]) TYPE(rhs[i]);
160 }
161 this->size = rhs.size;
162 this->grow = rhs.grow;
163 this->start = 0;
164}
165
166//------------------------------------------------------------------------------
169template<class TYPE>
171 data(rhs.data),
172 capacity(rhs.capacity),
173 size(rhs.size),
174 grow(rhs.grow),
175 start(rhs.start)
176{
177 rhs.data = nullptr;
178 rhs.capacity = 0;
179 rhs.size = 0;
180}
181
182//------------------------------------------------------------------------------
185template<class TYPE>
186void
188{
189 if (this != &rhs)
190 {
191 this->Clear();
192 this->Reserve(rhs.size);
193 for (IndexT i = 0; i < rhs.size; i++)
194 {
195 ::new ((void*)&this->data[i]) TYPE(rhs[i]);
196 }
197 this->size = rhs.size;
198 this->grow = rhs.grow;
199 this->start = 0;
200 }
201}
202
203//------------------------------------------------------------------------------
206template<class TYPE>
207void
209{
210 if (this != &rhs)
211 {
212 if (this->data)
213 {
214 this->Clear();
215 ::operator delete[](this->data);
216 }
217 this->data = rhs.data;
218 this->size = rhs.size;
219 this->grow = rhs.grow;
220 this->capacity = rhs.capacity;
221 this->start = rhs.start;
222 rhs.data = nullptr;
223 rhs.size = 0;
224 rhs.capacity = 0;
225 }
226}
227
228//------------------------------------------------------------------------------
231template<class TYPE>
232__forceinline
233TYPE&
235{
236 n_assert(index < this->size);
237 return this->data[MapIndex(index)];
238}
239
240//------------------------------------------------------------------------------
243template<class TYPE>
244bool
246{
247 if(this->size != rhs.size) return false;
248
249 for(IndexT i= 0; i < this->size; i++)
250 {
251 if(!((*this)[i] == rhs[i] ))
252 {
253 return false;
254 }
255 }
256 return true;
257}
258
259//------------------------------------------------------------------------------
262template<class TYPE>
263bool
265{
266 return ! this->operator==(rhs);
267}
268
269//------------------------------------------------------------------------------
272template<class TYPE>
273bool
274Queue<TYPE>::Contains(const TYPE& e) const
275{
276 for(IndexT i = 0 ; i<this->size; i++)
277 {
278 if (this->operator[](i) == e)
279 return true;
280 }
281 return false;
282}
283
284//------------------------------------------------------------------------------
287template<class TYPE>
288void
290{
291 n_assert(i < this->size);
292
293 if (i == 0)
294 {
295 this->Dequeue();
296 }
297 else
298 {
299 IndexT idx = this->MapIndex(i);
300
301 if constexpr(std::is_trivially_copyable_v<TYPE>)
302 {
303 // Trivially-copyable: plain byte assignment is safe; no construction/destruction needed
304 // check if wrapped around
305 if (idx < this->start)
306 {
307 for (IndexT j = 0; j < this->size - i - 1; ++j)
308 {
309 this->data[idx] = this->data[idx + 1];
310 idx++;
311 }
312 }
313 else
314 {
315 for (IndexT j = 0; j < i; j++)
316 {
317 this->data[idx] = this->data[idx - 1];
318 --idx;
319 }
320 this->start = this->MapIndex(1);
321 }
322 }
323 else
324 {
325 // Non-trivially-copyable: erased slot is dead after DestroyElement.
326 // Use placement-new for the first write into the dead slot, move-assign for the rest,
327 // then explicitly destroy the vacated tail slot.
328 this->DestroyElement<TYPE>(idx);
329 // check if wrapped around
330 if (idx < this->start)
331 {
332 SizeT remaining = this->size - i - 1;
333 if (remaining > 0)
334 {
335 ::new ((void*)&this->data[idx]) TYPE(std::move(this->data[idx + 1]));
336 idx++;
337 for (IndexT j = 1; j < remaining; ++j)
338 {
339 this->data[idx] = std::move(this->data[idx + 1]);
340 idx++;
341 }
342 this->data[idx].~TYPE();
343 }
344 }
345 else
346 {
347 ::new ((void*)&this->data[idx]) TYPE(std::move(this->data[idx - 1]));
348 --idx;
349 for (IndexT j = 1; j < i; ++j)
350 {
351 this->data[idx] = std::move(this->data[idx - 1]);
352 --idx;
353 }
354 this->data[idx].~TYPE();
355 this->start = this->MapIndex(1);
356 }
357 }
358 --this->size;
359 }
360}
361
362//------------------------------------------------------------------------------
365template<class TYPE>
366void
368{
369 this->ClearAll<TYPE>();
370 this->size = 0;
371 this->start = 0;
372}
373
374//------------------------------------------------------------------------------
377template<class TYPE>
378void
380{
381 if (num > this->capacity)
382 {
383 // round up to next multiple of 64
384 num = num<64? num: Util::Round::RoundUp(num, 64);
385
386 TYPE * newdata = (TYPE*)::operator new[](num * sizeof(TYPE));
387
388 // check if empty
389 if (this->capacity > 0)
390 {
391 // we could use SFINAE here as well, but as its a single if in a (rare) call its not worth the bother
392 if constexpr(std::is_trivially_copyable_v<TYPE>)
393 {
394 if (this->size > 0)
395 {
396 SizeT upper = this->capacity - this->start;
397 SizeT lower = this->size - (this->capacity - this->start);
398
399 if (lower < 0)
400 {
401 Memory::Copy(&this->data[this->start], newdata, this->size * sizeof(TYPE));
402 }
403 else
404 {
405 Memory::Copy(&this->data[this->start], newdata, upper * sizeof(TYPE));
406 Memory::Copy(this->data, &newdata[upper], lower * sizeof(TYPE));
407 }
408 }
409 if (this->data != nullptr)
410 {
411 ::operator delete[](this->data);
412 }
413 }
414 else
415 {
416 for (IndexT i = 0; i < this->size; i++)
417 {
418 IndexT idx = this->MapIndex(i);
419 n_assert(idx < this->capacity);
420 ::new ((void*)&newdata[i]) TYPE(std::move(this->data[idx]));
421 this->DestroyElement<TYPE>(idx);
422 }
423 if (this->data != nullptr)
424 {
425 ::operator delete[](this->data);
426 }
427 }
428 }
429 this->data = newdata;
430 this->capacity = num;
431 this->start = 0;
432 }
433}
434
435//------------------------------------------------------------------------------
438template<class TYPE> void
440{
441 SizeT growToSize;
442 if (0 == this->capacity)
443 {
444 growToSize = this->grow;
445 }
446 else
447 {
448 // grow by half of the current capacity, but never more then MaxGrowSize
449 SizeT growBy = this->capacity >> 1;
450
451 if (growBy == 0)
452 {
453 growBy = MinGrowSize;
454 }
455 else if (growBy > MaxGrowSize)
456 {
457 growBy = MaxGrowSize;
458 }
459 growToSize = this->capacity + growBy;
460 }
461 this->Reserve(growToSize);
462}
463
464//------------------------------------------------------------------------------
467template<class TYPE>
468__forceinline
469SizeT
471{
472 return this->size;
473}
474
475//------------------------------------------------------------------------------
478template<class TYPE>
479__forceinline
480SizeT
482{
483 return this->capacity;
484}
485
486//------------------------------------------------------------------------------
489template<class TYPE>
490__forceinline
491bool
493{
494 return this->size == 0;
495}
496
497//------------------------------------------------------------------------------
500template<class TYPE>
501__forceinline
502void
504{
505 if (this->size == this->capacity)
506 {
507 this->Grow();
508 }
509 ::new ((void*)&this->data[this->MapIndex(this->size++)]) TYPE(e);
510}
511
512//------------------------------------------------------------------------------
515template<class TYPE>
516__forceinline
517void
519{
520 if (this->size == this->capacity)
521 {
522 this->Grow();
523 }
524
525 ::new ((void*)&this->data[this->MapIndex(this->size++)]) TYPE(std::move(e));
526}
527
528//------------------------------------------------------------------------------
531template<class TYPE>
532__forceinline
533TYPE
535{
536 n_assert(this->size > 0);
537
538 TYPE t = std::move(this->data[this->start]);
539 this->DestroyElement<TYPE>(this->start);
540 this->start = this->MapIndex(1);
541 --this->size;
542 return t;
543}
544
545//------------------------------------------------------------------------------
548template<class TYPE>
549__forceinline
550TYPE&
552{
553 n_assert(this->size > 0);
554 return this->data[this->start];
555}
556
557//------------------------------------------------------------------------------
562
563template<class TYPE>
564__forceinline
565IndexT
567{
568 idx = (idx + this->start) % this->capacity;
569 if (idx > 0)
570 {
571 return idx;
572 }
573 else
574 {
575 return (idx + this->capacity) % this->capacity;
576 }
577}
578
579
580} // namespace Util
581//------------------------------------------------------------------------------
void Enqueue(const TYPE &e)
add element to the back of the Dequeue, can trigger grow
Definition queue.h:503
bool Contains(const TYPE &e) const
return true if Dequeue contains element
Definition queue.h:274
__forceinline std::enable_if< std::is_trivially_destructible< X >::value==true >::type DestroyElement(IndexT idx)
Definition queue.h:80
static const SizeT MinGrowSize
Definition queue.h:108
SizeT capacity
Definition queue.h:113
bool operator!=(const Queue< TYPE > &rhs) const
inequality operator
Definition queue.h:264
bool operator==(const Queue< TYPE > &rhs) const
equality operator
Definition queue.h:245
SizeT size
Definition queue.h:112
void Clear()
remove all elements from the Dequeue
Definition queue.h:367
__forceinline std::enable_if< std::is_trivially_destructible< X >::value==true >::type ClearAll()
Definition queue.h:99
void Grow()
grow Dequeue by internal growing rules (slow)
Definition queue.h:439
~Queue()
destructor
Definition queue.h:134
Queue()
constructor
Definition queue.h:120
SizeT Capacity() const
returns allocation of elements in the Dequeue
Definition queue.h:481
void EraseIndex(const IndexT i)
erase element at index (slow!!)
Definition queue.h:289
bool IsEmpty() const
return true if Dequeue is empty
Definition queue.h:492
TYPE * data
Definition queue.h:106
__forceinline std::enable_if< std::is_trivially_destructible< X >::value==false >::type DestroyElement(IndexT idx)
Definition queue.h:72
void operator=(const Queue< TYPE > &rhs)
assignment operator
Definition queue.h:187
__forceinline std::enable_if< std::is_trivially_destructible< X >::value==false >::type ClearAll()
Definition queue.h:88
SizeT grow
Definition queue.h:110
static const SizeT MaxGrowSize
Definition queue.h:109
void Reserve(SizeT num)
increase capacity to fit N more elements into the Dequeue (slow)
Definition queue.h:379
TYPE & Peek() const
access to element at front of Dequeue without removing it
Definition queue.h:551
SizeT start
Definition queue.h:111
TYPE Dequeue()
remove the element from the front of the Dequeue
Definition queue.h:534
IndexT MapIndex(IndexT index) const
maps index to actual item position using wrapping
Definition queue.h:566
TYPE & operator[](IndexT index) const
access element by index, 0 is the frontmost element (next to be dequeued)
Definition queue.h:234
SizeT Size() const
returns number of elements in the Dequeue
Definition queue.h:470
static uint RoundUp(uint val, uint boundary)
generic roundup
Definition round.h:83
#define n_assert(exp)
Definition debug.h:50
void Copy(const void *from, void *to, size_t numBytes)
Copy a chunk of memory (note the argument order is different from memcpy()!
Definition osxmemory.cc:213
A quad tree designed to return regions of free 2D space.
Definition String.cs:6
bool operator==(const String &a, const String &b)
Definition string.cc:848
Nebula's scalar datatype.
int SizeT
Definition types.h:42
int IndexT
Definition types.h:41