summaryrefslogtreecommitdiff
path: root/src/common/multi_level_queue.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/multi_level_queue.h329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
new file mode 100644
index 000000000..fc72a8238
--- /dev/null
+++ b/src/common/multi_level_queue.h
@@ -0,0 +1,329 @@
1// Copyright 2019 TuxSH
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <iterator>
9#include <list>
10
11#include "common/bit_util.h"
12#include "common/common_types.h"
13
14namespace Common {
15
16template <typename T, std::size_t Depth>
17class MultiLevelQueue {
18public:
19 using value_type = T;
20 using reference = value_type&;
21 using const_reference = const value_type&;
22 using pointer = value_type*;
23 using const_pointer = const value_type*;
24
25 using difference_type = typename std::pointer_traits<pointer>::difference_type;
26 using size_type = std::size_t;
27
28 template <bool is_constant>
29 class iterator_impl {
30 public:
31 using iterator_category = std::bidirectional_iterator_tag;
32 using value_type = T;
33 using pointer = std::conditional_t<is_constant, T*, const T*>;
34 using reference = std::conditional_t<is_constant, const T&, T&>;
35 using difference_type = typename std::pointer_traits<pointer>::difference_type;
36
37 friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
38 return (lhs.IsEnd() && rhs.IsEnd()) || lhs.it == rhs.it;
39 }
40
41 friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
42 return !operator==(lhs, rhs);
43 }
44
45 reference operator*() const {
46 return *it;
47 }
48
49 pointer operator->() const {
50 return it.operator->();
51 }
52
53 iterator_impl& operator++() {
54 if (IsEnd()) {
55 return *this;
56 }
57
58 ++it;
59
60 if (it == GetEndItForPrio()) {
61 u64 prios = mlq.used_priorities;
62 prios &= ~((1ULL << (current_priority + 1)) - 1);
63 if (prios == 0) {
64 current_priority = mlq.depth();
65 } else {
66 current_priority = CountTrailingZeroes64(prios);
67 it = GetBeginItForPrio();
68 }
69 }
70 return *this;
71 }
72
73 iterator_impl& operator--() {
74 if (IsEnd()) {
75 if (mlq.used_priorities != 0) {
76 current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
77 it = GetEndItForPrio();
78 --it;
79 }
80 } else if (it == GetBeginItForPrio()) {
81 u64 prios = mlq.used_priorities;
82 prios &= (1ULL << current_priority) - 1;
83 if (prios != 0) {
84 current_priority = CountTrailingZeroes64(prios);
85 it = GetEndItForPrio();
86 --it;
87 }
88 } else {
89 --it;
90 }
91 return *this;
92 }
93
94 iterator_impl operator++(int) {
95 const iterator_impl v{*this};
96 ++(*this);
97 return v;
98 }
99
100 iterator_impl operator--(int) {
101 const iterator_impl v{*this};
102 --(*this);
103 return v;
104 }
105
106 // allow implicit const->non-const
107 iterator_impl(const iterator_impl<false>& other)
108 : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
109
110 iterator_impl& operator=(const iterator_impl<false>& other) {
111 mlq = other.mlq;
112 it = other.it;
113 current_priority = other.current_priority;
114 return *this;
115 }
116
117 friend class iterator_impl<true>;
118 iterator_impl() = default;
119
120 private:
121 friend class MultiLevelQueue;
122 using container_ref =
123 std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
124 using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
125 typename std::list<T>::iterator>;
126
127 explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
128 : mlq(mlq), it(it), current_priority(current_priority) {}
129 explicit iterator_impl(container_ref mlq, u32 current_priority)
130 : mlq(mlq), it(), current_priority(current_priority) {}
131
132 bool IsEnd() const {
133 return current_priority == mlq.depth();
134 }
135
136 list_iterator GetBeginItForPrio() const {
137 return mlq.levels[current_priority].begin();
138 }
139
140 list_iterator GetEndItForPrio() const {
141 return mlq.levels[current_priority].end();
142 }
143
144 container_ref mlq;
145 list_iterator it;
146 u32 current_priority;
147 };
148
149 using iterator = iterator_impl<false>;
150 using const_iterator = iterator_impl<true>;
151
152 void add(T& element, u32 priority, bool send_back = true) {
153 if (send_back)
154 levels[priority].push_back(element);
155 else
156 levels[priority].push_front(element);
157 used_priorities |= 1ULL << priority;
158 }
159
160 void remove(const T& element, u32 priority) {
161 levels[priority].erase(ListIterateTo(levels[priority], element));
162 if (levels[priority].empty()) {
163 used_priorities &= ~(1ULL << priority);
164 }
165 }
166
167 void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
168 const auto new_next =
169 adjust_front ? levels[new_priority].cbegin() : levels[new_priority].cend();
170 ListSplice(levels[new_priority], new_next, levels[old_priority],
171 ListIterateTo(levels[old_priority], element));
172
173 used_priorities |= 1ULL << new_priority;
174
175 if (levels[old_priority].empty()) {
176 used_priorities &= ~(1ULL << old_priority);
177 }
178 }
179 void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
180 adjust(*it, old_priority, new_priority, adjust_front);
181 }
182
183 void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
184 ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
185 ListIterateTo(levels[priority], element));
186
187 other.used_priorities |= 1ULL << priority;
188
189 if (levels[priority].empty()) {
190 used_priorities &= ~(1ULL << priority);
191 }
192 }
193
194 void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
195 transfer_to_front(*it, priority, other);
196 }
197
198 void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
199 ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
200 ListIterateTo(levels[priority], element));
201
202 other.used_priorities |= 1ULL << priority;
203
204 if (levels[priority].empty()) {
205 used_priorities &= ~(1ULL << priority);
206 }
207 }
208
209 void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
210 transfer_to_back(*it, priority, other);
211 }
212
213 void yield(u32 priority, std::size_t n = 1) {
214 ListShiftForward(levels[priority], n);
215 }
216
217 std::size_t depth() const {
218 return Depth;
219 }
220
221 std::size_t size(u32 priority) const {
222 return levels[priority].size();
223 }
224
225 std::size_t size() const {
226 u64 priorities = used_priorities;
227 std::size_t size = 0;
228 while (priorities != 0) {
229 const u64 current_priority = CountTrailingZeroes64(priorities);
230 size += levels[current_priority].size();
231 priorities &= ~(1ULL << current_priority);
232 }
233 return size;
234 }
235
236 bool empty() const {
237 return used_priorities == 0;
238 }
239
240 bool empty(u32 priority) const {
241 return (used_priorities & (1ULL << priority)) == 0;
242 }
243
244 u32 highest_priority_set(u32 max_priority = 0) const {
245 const u64 priorities =
246 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
247 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
248 }
249
250 u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
251 const u64 priorities = min_priority >= Depth - 1
252 ? used_priorities
253 : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
254 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
255 }
256
257 const_iterator cbegin(u32 max_prio = 0) const {
258 const u32 priority = highest_priority_set(max_prio);
259 return priority == Depth ? cend()
260 : const_iterator{*this, levels[priority].cbegin(), priority};
261 }
262 const_iterator begin(u32 max_prio = 0) const {
263 return cbegin(max_prio);
264 }
265 iterator begin(u32 max_prio = 0) {
266 const u32 priority = highest_priority_set(max_prio);
267 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
268 }
269
270 const_iterator cend(u32 min_prio = Depth - 1) const {
271 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
272 }
273 const_iterator end(u32 min_prio = Depth - 1) const {
274 return cend(min_prio);
275 }
276 iterator end(u32 min_prio = Depth - 1) {
277 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
278 }
279
280 T& front(u32 max_priority = 0) {
281 const u32 priority = highest_priority_set(max_priority);
282 return levels[priority == Depth ? 0 : priority].front();
283 }
284 const T& front(u32 max_priority = 0) const {
285 const u32 priority = highest_priority_set(max_priority);
286 return levels[priority == Depth ? 0 : priority].front();
287 }
288
289 T back(u32 min_priority = Depth - 1) {
290 const u32 priority = lowest_priority_set(min_priority); // intended
291 return levels[priority == Depth ? 63 : priority].back();
292 }
293 const T& back(u32 min_priority = Depth - 1) const {
294 const u32 priority = lowest_priority_set(min_priority); // intended
295 return levels[priority == Depth ? 63 : priority].back();
296 }
297
298private:
299 using const_list_iterator = typename std::list<T>::const_iterator;
300
301 static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
302 // NOTE: May want to consider making this an assertion or something
303 if (shift >= list.size()) {
304 return;
305 }
306
307 const auto begin_range = list.begin();
308 const auto end_range = std::next(begin_range, shift);
309 list.splice(list.end(), list, begin_range, end_range);
310 }
311
312 static void ListSplice(std::list<T>& in_list, const_list_iterator position,
313 std::list<T>& out_list, const_list_iterator element) {
314 in_list.splice(position, out_list, element);
315 }
316
317 static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
318 auto it = list.cbegin();
319 while (it != list.cend() && *it != element) {
320 ++it;
321 }
322 return it;
323 }
324
325 std::array<std::list<T>, Depth> levels;
326 u64 used_priorities = 0;
327};
328
329} // namespace Common