gz-cpp-util 1.3
A c++20 library containing various utilities
queue.hpp
1#pragma once
2
3#include "../util/util.hpp"
4
5#include <iostream>
6#include <vector>
7#include <iterator>
8#include <algorithm>
9#include <concepts>
10#include <thread>
11
12namespace gz {
13
14 // from util.hpp
15 template<std::unsigned_integral I, std::unsigned_integral S>
16 inline void incrementIndex(I& i, const S containerSize) {
17 if (i < containerSize - 1) { i++; }
18 else { i = 0; }
19 }
20 template<std::unsigned_integral I, std::unsigned_integral S>
21 inline void decrementIndex(I& i, const S containerSize) {
22 if (i > 0) { i--; }
23 else { i = containerSize - 1; }
24 }
25 template<std::unsigned_integral I, std::unsigned_integral S>
26 inline I getIncrementedIndex(const I i, const S containerSize) {
27 if (i < containerSize - 1) { return i + 1; }
28 else { return 0; }
29 }
30 template<std::unsigned_integral I, std::unsigned_integral S>
31 inline I getDecrementedIndex(const I i, const S containerSize) {
32 if (i > 0) { return i - 1; }
33 else { return containerSize - 1; }
34 }
36 template<std::integral I, std::unsigned_integral S>
37 size_t getValidIndex(const I i, const S containerSize) {
38 if (i < 0) {
39 return containerSize - (-i) % containerSize - 1;
40 }
41 else if (i >= static_cast<int>(containerSize)) {
42 return i % containerSize;
43 }
44 return i;
45 }
59 template<std::swappable T>
60 class Queue {
61 public:
67 Queue(size_t size=10, size_t maxSize=-1);
68
69 void push_back(T& t);
70 void push_back(T&& t) { push_back(t); };
71 void emplace_back(T&& t);
72
76 bool hasElement();
83 T& getRef();
89 T getCopy();
90
94 void clear();
95
96 std::vector<T>& getInternalBuffer() { return buffer; }
97 private:
103 void resize();
104
105 size_t writeIndex;
106 size_t readIndex;
107 std::vector<T> buffer;
108 size_t vectorCapacity;
109 size_t maxSize;
110 std::mutex mtx;
111 };
112
113 template<std::swappable T>
114 Queue<T>::Queue(size_t capacity, size_t maxSize)
115 : vectorCapacity(capacity), maxSize(maxSize) {
116 buffer.reserve(capacity);
117 /* buffer.resize(2); */
118
119 writeIndex = capacity - 1;
120 readIndex = capacity - 1;
121 }
122
123
124 template<std::swappable T>
126 // if vector is at maxSize, "loose" the oldest element
127 if (buffer.size() == maxSize) {
128 incrementIndex(readIndex, buffer.size());
129 return;
130 }
131 // if not at end of vector rotate so that oldest element is first
132 if (writeIndex != vectorCapacity - 1) {
133 // if not at end, reserve more space and move elements
134 std::rotate(buffer.begin(), buffer.begin() + readIndex, buffer.end());
135 readIndex = 0;
136 writeIndex = vectorCapacity - 1;
137 }
138 // reserve 10% more space (at least space for 3 more elements).
139 buffer.reserve(std::min(std::max(static_cast<size_t>(1.1 * vectorCapacity), vectorCapacity + 3), maxSize));
140 vectorCapacity = buffer.capacity();
141 }
142
143
144 template<std::swappable T>
145 void Queue<T>::push_back(T& t) {
146 mtx.lock();
147 // check if this would write into oldest element
148 if (readIndex == getIncrementedIndex(writeIndex, vectorCapacity)) { resize(); }
149
150 util::incrementIndex(writeIndex, vectorCapacity);
151 if (buffer.size() < vectorCapacity) {
152 buffer.push_back(t);
153 }
154 else {
155 buffer[writeIndex] = t;
156 }
157 mtx.unlock();
158 /* std::cout << "queue after pushback. ri: " << readIndex << " - wi: " << writeIndex << " - size: " << buffer.size() << " - cap: " << vectorCapacity << "\n"; */
159 }
160
161
162 template<std::swappable T>
163 void Queue<T>::emplace_back(T&& t) {
164 mtx.lock();
165 if (readIndex == getIncrementedIndex(writeIndex, vectorCapacity)) { resize(); }
166
167 util::incrementIndex(writeIndex, vectorCapacity);
168 if (buffer.size() < vectorCapacity) {
169 buffer.emplace_back(std::move(t));
170 }
171 else {
172 buffer[writeIndex] = std::move(t);
173 }
174 mtx.unlock();
175 }
176
177
178 template<std::swappable T>
180 mtx.lock();
181 bool hasElement = writeIndex != readIndex;
182 mtx.unlock();
183 return hasElement;
184 }
185
186
187 template<std::swappable T>
189 mtx.lock();
190 incrementIndex(readIndex, vectorCapacity);
191 size_t i = readIndex;
192 T& element = buffer[i];
193 mtx.unlock();
194 return buffer[i];
195 }
196
197
198 template<std::swappable T>
200 mtx.lock();
201 incrementIndex(readIndex, vectorCapacity);
202 size_t i = readIndex;
203 mtx.unlock();
204 return std::move(buffer[i]);
205 }
206
207
208 template<std::swappable T>
210 mtx.lock();
211 writeIndex = 0;
212 readIndex = 0;
213 mtx.unlock();
214 }
215} // namespace gz
A thread-safe queue with a dynamic size up until a maximum size.
Definition: queue.hpp:60
void clear()
Remove all elements.
Definition: queue.hpp:209
T & getRef()
Get a reference to the oldest element.
Definition: queue.hpp:188
size_t writeIndex
Points to the element that was last written.
Definition: queue.hpp:105
size_t readIndex
Points to the element that was last read.
Definition: queue.hpp:106
Queue(size_t size=10, size_t maxSize=-1)
Create a new queue.
Definition: queue.hpp:114
void resize()
Resize the queue (if possible)
Definition: queue.hpp:125
bool hasElement()
Check if the contains has an element that can be retrieved by get()
Definition: queue.hpp:179
T getCopy()
Get a copy of the oldest element.
Definition: queue.hpp:199
void incrementIndex(I &i, const S containerSize)
Increment an index. Up to containerSize, then restart at 0.
Definition: util.hpp:14
void decrementIndex(I &i, const S containerSize)
Decrement an index. Down to 0, then restart at containerSize - 1.
Definition: util.hpp:22
I getDecrementedIndex(const I i, const S containerSize)
Like decrementIndex, but returns a new number.
Definition: util.hpp:38
std::size_t getValidIndex(const I i, const S containerSize)
Wrap an index around, to make it valid.
Definition: util.hpp:51
I getIncrementedIndex(const I i, const S containerSize)
Like incrementIndex, but returns a new number.
Definition: util.hpp:30