Cogs.Core
FixedIndexQueue.h
1#pragma once
2#include "../Memory/MemoryBuffer.h"
3#include "../BitTwiddling/PowerOfTwo.h"
4
5#include <type_traits>
6#include <cassert>
7
8namespace Cogs
9{
10 namespace Collections
11 {
12
16 template<typename Element, typename IndexBaseType>
18 {
20 typedef typename std::make_signed<IndexBaseType>::type SignedIndexType;
21 public:
23 typedef typename std::make_unsigned<IndexBaseType>::type IndexType;
24
26 FixedIndexQueue() { storage.resize(capacity(), false); }
27
29 const IndexType capacity() const { return sizeMask + 1u; }
30
33 {
34 IndexType frontOffsetWrap = frontOffset & sizeMask;
35 IndexType backOffsetWrap = backOffset & sizeMask;
36 return frontOffsetWrap == backOffsetWrap;
37 }
38
41 {
42 IndexType frontOffsetWrap = frontOffset & sizeMask;
43 IndexType backOffsetWrap = backOffset & sizeMask;
44 return backOffsetWrap - frontOffsetWrap + (backOffsetWrap < frontOffsetWrap ? capacity() : static_cast<IndexType>(0));
45 }
46
48 void clear()
49 {
50 offsetToFixedIndexShift += backOffset;
51 frontOffset = 0;
52 backOffset = 0;
53 }
54
56 void reset()
57 {
58 frontOffset = 0;
59 backOffset = 0;
60 offsetToFixedIndexShift = 0;
61 }
62
64 void shrink()
65 {
66 IndexType sizeL2New = static_cast<IndexType>(roundUpToPowerOfTwoShift(count() + 1u));
67 if (sizeL2New == sizeL2) return;
68
69 resize(sizeL2New);
70 }
71
73 void pushBack(Element e)
74 {
75 if (noCapacity()) grow();
76
77 storage[backOffset & sizeMask] = e;
78 backOffset++;
79 }
80
82 void pushFront(Element e)
83 {
84 if (noCapacity()) grow();
85
86 frontOffset--;
87 storage[frontOffset & sizeMask] = e;
88 }
89
91 void popFront()
92 {
93 if (empty()) {
94 assert(false && "queue is empty");
95 return;
96 }
97 frontOffset++;
98 }
99
101 Element& operator[](IndexType fixedIndex)
102 {
103 assert(empty() == false);
104 assert(0 <= static_cast<SignedIndexType>(fixedIndex - frontIndex()));
105 assert(static_cast<SignedIndexType>(fixedIndex - backIndex()) <= 0);
106 IndexType offset = fixedIndex - offsetToFixedIndexShift;
107 return storage[offset & sizeMask];
108 }
109
111 const Element& operator[](IndexType fixedIndex) const
112 {
113 assert(empty() == false);
114 assert(0 <= static_cast<SignedIndexType>(fixedIndex - frontIndex()));
115 assert(static_cast<SignedIndexType>(fixedIndex - backIndex()) <= 0);
116 IndexType offset = fixedIndex - offsetToFixedIndexShift;
117 return storage[offset & sizeMask];
118 }
119
121 Element& front() { assert(empty() == false); return storage[frontOffset & sizeMask]; }
122
124 const Element& front() const { assert(empty() == false); return storage[frontOffset & sizeMask]; }
125
127 Element& back() { assert(empty() == false); return storage[(backOffset - IndexType(1)) & sizeMask]; }
128
130 const Element& back() const { assert(empty() == false); return storage[(backOffset - IndexType(1)) & sizeMask]; }
131
133 IndexType frontIndex() const { assert(empty() == false); return frontOffset + offsetToFixedIndexShift; }
134
136 IndexType backIndex() const { assert(empty() == false); return backOffset - IndexType(1) + offsetToFixedIndexShift; }
137
139 bool validIndex(uint32_t fixedIndex) const
140 {
141 if (empty()) return false;
142 // Correct as long as distance is less than SignedIndexType::Max.
143 if (static_cast<SignedIndexType>(fixedIndex - frontIndex()) < 0) return false;
144 if (0 < static_cast<SignedIndexType>(fixedIndex - backIndex())) return false;
145 return true;
146 }
147
148 private:
149 IndexType sizeL2 = 0; // size is 1<<0 = 1.
150 IndexType sizeMask = 0;
151 IndexType frontOffset = 0;
152 IndexType backOffset = 0;
153 IndexType offsetToFixedIndexShift = 0;
155
157 bool noCapacity() const { return ((frontOffset & sizeMask) == ((backOffset + 1) & sizeMask)); }
158
160 void grow()
161 {
162 IndexType sizeL2New = sizeL2 + 1u;
163 IndexType sizeNew = (1u << sizeL2New);
164 assert(capacity() < sizeNew); // Check if we grow beyond domain of representation.
165 resize(sizeL2New);
166 }
167
168 void resize(IndexType sizeL2New)
169 {
170 IndexType sizeNew = 1u << sizeL2New;
171 assert(isPowerOfTwo(sizeNew));
172
173 IndexType N = count();
174 assert(N < sizeNew);
175
176 Cogs::Memory::TypedBuffer<Element> storageNew(sizeNew);
177 for (uint32_t i = 0; i < N; i++) {
178 storageNew[i] = storage[(frontOffset + i) & sizeMask];
179 }
180 offsetToFixedIndexShift += frontOffset;
181 frontOffset = 0;
182 backOffset = N;
183
184 sizeL2 = sizeL2New;
185 sizeMask = sizeNew - 1u;
186 storage.swap(storageNew);
187 }
188
189 };
190
191 }
192}
Indexable queue where an element index is fixed through arbitrary enqueues and dequeues.
void pushFront(Element e)
Adds a new element to the front of the queue.
IndexType count() const
Returns number of elements currently in the queue.
bool validIndex(uint32_t fixedIndex) const
Check if a fixed index is valid, that is, refers to an element in the queue.
Element & operator[](IndexType fixedIndex)
Look up an element in the queue by a fixed index.
void clear()
Removes all entries from the queue, but will continue with new unused indices.
Element & front()
Returns front element.
const Element & front() const
Returns front element.
void shrink()
Reduce capacity to the minimum needed to preserve the contents.
bool noCapacity() const
True if there is no free capacity left.
void pushBack(Element e)
Adds a new element to the back of the queue.
IndexType frontIndex() const
Returns fixed index of front.
void reset()
Removes all entries from the queue, recycles indices and starts from zero.
IndexType empty() const
Returns true if there are currently no elements in the queue.
const IndexType capacity() const
Returns the current capacity of the queue.
const Element & operator[](IndexType fixedIndex) const
Look up an element in the queue by a fixed index.
Element & back()
Returns back element.
std::make_signed< IndexBaseType >::type SignedIndexType
Signed helper type.
void grow()
Double the capacity of the queue.
const Element & back() const
Returns back element.
void popFront()
Removes the front from the queue.
IndexType backIndex() const
Returns fixed index of current back element.
std::make_unsigned< IndexBaseType >::type IndexType
Unsigned type used for indices, typically uint32_t or uin64_t.
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
bool isPowerOfTwo(T x)
Definition: PowerOfTwo.h:15
uint8_t roundUpToPowerOfTwoShift(uint8_t x)
Definition: PowerOfTwo.h:170