|
@@ -0,0 +1,329 @@
|
|
|
|
|
+// Copyright 2019 TuxSH
|
|
|
|
|
+// Licensed under GPLv2 or any later version
|
|
|
|
|
+// Refer to the license.txt file included.
|
|
|
|
|
+
|
|
|
|
|
+#pragma once
|
|
|
|
|
+
|
|
|
|
|
+#include <array>
|
|
|
|
|
+#include <iterator>
|
|
|
|
|
+#include <list>
|
|
|
|
|
+
|
|
|
|
|
+#include "common/bit_util.h"
|
|
|
|
|
+#include "common/common_types.h"
|
|
|
|
|
+
|
|
|
|
|
+namespace Common {
|
|
|
|
|
+
|
|
|
|
|
+template <typename T, std::size_t Depth>
|
|
|
|
|
+class MultiLevelQueue {
|
|
|
|
|
+public:
|
|
|
|
|
+ using value_type = T;
|
|
|
|
|
+ using reference = value_type&;
|
|
|
|
|
+ using const_reference = const value_type&;
|
|
|
|
|
+ using pointer = value_type*;
|
|
|
|
|
+ using const_pointer = const value_type*;
|
|
|
|
|
+
|
|
|
|
|
+ using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
|
|
|
+ using size_type = std::size_t;
|
|
|
|
|
+
|
|
|
|
|
+ template <bool is_constant>
|
|
|
|
|
+ class iterator_impl {
|
|
|
|
|
+ public:
|
|
|
|
|
+ using iterator_category = std::bidirectional_iterator_tag;
|
|
|
|
|
+ using value_type = T;
|
|
|
|
|
+ using pointer = std::conditional_t<is_constant, T*, const T*>;
|
|
|
|
|
+ using reference = std::conditional_t<is_constant, const T&, T&>;
|
|
|
|
|
+ using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
|
|
|
+
|
|
|
|
|
+ friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
|
|
|
+ return (lhs.IsEnd() && rhs.IsEnd()) || lhs.it == rhs.it;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
|
|
|
+ return !operator==(lhs, rhs);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ reference operator*() const {
|
|
|
|
|
+ return *it;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pointer operator->() const {
|
|
|
|
|
+ return it.operator->();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator_impl& operator++() {
|
|
|
|
|
+ if (IsEnd()) {
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ++it;
|
|
|
|
|
+
|
|
|
|
|
+ if (it == GetEndItForPrio()) {
|
|
|
|
|
+ u64 prios = mlq.used_priorities;
|
|
|
|
|
+ prios &= ~((1ULL << (current_priority + 1)) - 1);
|
|
|
|
|
+ if (prios == 0) {
|
|
|
|
|
+ current_priority = mlq.depth();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ current_priority = CountTrailingZeroes64(prios);
|
|
|
|
|
+ it = GetBeginItForPrio();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator_impl& operator--() {
|
|
|
|
|
+ if (IsEnd()) {
|
|
|
|
|
+ if (mlq.used_priorities != 0) {
|
|
|
|
|
+ current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
|
|
|
|
|
+ it = GetEndItForPrio();
|
|
|
|
|
+ --it;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (it == GetBeginItForPrio()) {
|
|
|
|
|
+ u64 prios = mlq.used_priorities;
|
|
|
|
|
+ prios &= (1ULL << current_priority) - 1;
|
|
|
|
|
+ if (prios != 0) {
|
|
|
|
|
+ current_priority = CountTrailingZeroes64(prios);
|
|
|
|
|
+ it = GetEndItForPrio();
|
|
|
|
|
+ --it;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ --it;
|
|
|
|
|
+ }
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator_impl operator++(int) {
|
|
|
|
|
+ const iterator_impl v{*this};
|
|
|
|
|
+ ++(*this);
|
|
|
|
|
+ return v;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator_impl operator--(int) {
|
|
|
|
|
+ const iterator_impl v{*this};
|
|
|
|
|
+ --(*this);
|
|
|
|
|
+ return v;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // allow implicit const->non-const
|
|
|
|
|
+ iterator_impl(const iterator_impl<false>& other)
|
|
|
|
|
+ : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
|
|
|
|
+
|
|
|
|
|
+ iterator_impl& operator=(const iterator_impl<false>& other) {
|
|
|
|
|
+ mlq = other.mlq;
|
|
|
|
|
+ it = other.it;
|
|
|
|
|
+ current_priority = other.current_priority;
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ friend class iterator_impl<true>;
|
|
|
|
|
+ iterator_impl() = default;
|
|
|
|
|
+
|
|
|
|
|
+ private:
|
|
|
|
|
+ friend class MultiLevelQueue;
|
|
|
|
|
+ using container_ref =
|
|
|
|
|
+ std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
|
|
|
|
|
+ using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
|
|
|
|
|
+ typename std::list<T>::iterator>;
|
|
|
|
|
+
|
|
|
|
|
+ explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
|
|
|
|
|
+ : mlq(mlq), it(it), current_priority(current_priority) {}
|
|
|
|
|
+ explicit iterator_impl(container_ref mlq, u32 current_priority)
|
|
|
|
|
+ : mlq(mlq), it(), current_priority(current_priority) {}
|
|
|
|
|
+
|
|
|
|
|
+ bool IsEnd() const {
|
|
|
|
|
+ return current_priority == mlq.depth();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ list_iterator GetBeginItForPrio() const {
|
|
|
|
|
+ return mlq.levels[current_priority].begin();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ list_iterator GetEndItForPrio() const {
|
|
|
|
|
+ return mlq.levels[current_priority].end();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ container_ref mlq;
|
|
|
|
|
+ list_iterator it;
|
|
|
|
|
+ u32 current_priority;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ using iterator = iterator_impl<false>;
|
|
|
|
|
+ using const_iterator = iterator_impl<true>;
|
|
|
|
|
+
|
|
|
|
|
+ void add(T& element, u32 priority, bool send_back = true) {
|
|
|
|
|
+ if (send_back)
|
|
|
|
|
+ levels[priority].push_back(element);
|
|
|
|
|
+ else
|
|
|
|
|
+ levels[priority].push_front(element);
|
|
|
|
|
+ used_priorities |= 1ULL << priority;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void remove(const T& element, u32 priority) {
|
|
|
|
|
+ levels[priority].erase(ListIterateTo(levels[priority], element));
|
|
|
|
|
+ if (levels[priority].empty()) {
|
|
|
|
|
+ used_priorities &= ~(1ULL << priority);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
|
|
|
+ const auto new_next =
|
|
|
|
|
+ adjust_front ? levels[new_priority].cbegin() : levels[new_priority].cend();
|
|
|
|
|
+ ListSplice(levels[new_priority], new_next, levels[old_priority],
|
|
|
|
|
+ ListIterateTo(levels[old_priority], element));
|
|
|
|
|
+
|
|
|
|
|
+ used_priorities |= 1ULL << new_priority;
|
|
|
|
|
+
|
|
|
|
|
+ if (levels[old_priority].empty()) {
|
|
|
|
|
+ used_priorities &= ~(1ULL << old_priority);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
|
|
|
+ adjust(*it, old_priority, new_priority, adjust_front);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
|
|
|
+ ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
|
|
|
|
|
+ ListIterateTo(levels[priority], element));
|
|
|
|
|
+
|
|
|
|
|
+ other.used_priorities |= 1ULL << priority;
|
|
|
|
|
+
|
|
|
|
|
+ if (levels[priority].empty()) {
|
|
|
|
|
+ used_priorities &= ~(1ULL << priority);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
|
|
|
+ transfer_to_front(*it, priority, other);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
|
|
|
+ ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
|
|
|
|
|
+ ListIterateTo(levels[priority], element));
|
|
|
|
|
+
|
|
|
|
|
+ other.used_priorities |= 1ULL << priority;
|
|
|
|
|
+
|
|
|
|
|
+ if (levels[priority].empty()) {
|
|
|
|
|
+ used_priorities &= ~(1ULL << priority);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
|
|
|
+ transfer_to_back(*it, priority, other);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void yield(u32 priority, std::size_t n = 1) {
|
|
|
|
|
+ ListShiftForward(levels[priority], n);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::size_t depth() const {
|
|
|
|
|
+ return Depth;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::size_t size(u32 priority) const {
|
|
|
|
|
+ return levels[priority].size();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::size_t size() const {
|
|
|
|
|
+ u64 priorities = used_priorities;
|
|
|
|
|
+ std::size_t size = 0;
|
|
|
|
|
+ while (priorities != 0) {
|
|
|
|
|
+ const u64 current_priority = CountTrailingZeroes64(priorities);
|
|
|
|
|
+ size += levels[current_priority].size();
|
|
|
|
|
+ priorities &= ~(1ULL << current_priority);
|
|
|
|
|
+ }
|
|
|
|
|
+ return size;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool empty() const {
|
|
|
|
|
+ return used_priorities == 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool empty(u32 priority) const {
|
|
|
|
|
+ return (used_priorities & (1ULL << priority)) == 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ u32 highest_priority_set(u32 max_priority = 0) const {
|
|
|
|
|
+ const u64 priorities =
|
|
|
|
|
+ max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
|
|
|
|
|
+ return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
|
|
|
|
|
+ const u64 priorities = min_priority >= Depth - 1
|
|
|
|
|
+ ? used_priorities
|
|
|
|
|
+ : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
|
|
|
|
|
+ return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const_iterator cbegin(u32 max_prio = 0) const {
|
|
|
|
|
+ const u32 priority = highest_priority_set(max_prio);
|
|
|
|
|
+ return priority == Depth ? cend()
|
|
|
|
|
+ : const_iterator{*this, levels[priority].cbegin(), priority};
|
|
|
|
|
+ }
|
|
|
|
|
+ const_iterator begin(u32 max_prio = 0) const {
|
|
|
|
|
+ return cbegin(max_prio);
|
|
|
|
|
+ }
|
|
|
|
|
+ iterator begin(u32 max_prio = 0) {
|
|
|
|
|
+ const u32 priority = highest_priority_set(max_prio);
|
|
|
|
|
+ return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const_iterator cend(u32 min_prio = Depth - 1) const {
|
|
|
|
|
+ return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ const_iterator end(u32 min_prio = Depth - 1) const {
|
|
|
|
|
+ return cend(min_prio);
|
|
|
|
|
+ }
|
|
|
|
|
+ iterator end(u32 min_prio = Depth - 1) {
|
|
|
|
|
+ return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T& front(u32 max_priority = 0) {
|
|
|
|
|
+ const u32 priority = highest_priority_set(max_priority);
|
|
|
|
|
+ return levels[priority == Depth ? 0 : priority].front();
|
|
|
|
|
+ }
|
|
|
|
|
+ const T& front(u32 max_priority = 0) const {
|
|
|
|
|
+ const u32 priority = highest_priority_set(max_priority);
|
|
|
|
|
+ return levels[priority == Depth ? 0 : priority].front();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T back(u32 min_priority = Depth - 1) {
|
|
|
|
|
+ const u32 priority = lowest_priority_set(min_priority); // intended
|
|
|
|
|
+ return levels[priority == Depth ? 63 : priority].back();
|
|
|
|
|
+ }
|
|
|
|
|
+ const T& back(u32 min_priority = Depth - 1) const {
|
|
|
|
|
+ const u32 priority = lowest_priority_set(min_priority); // intended
|
|
|
|
|
+ return levels[priority == Depth ? 63 : priority].back();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+private:
|
|
|
|
|
+ using const_list_iterator = typename std::list<T>::const_iterator;
|
|
|
|
|
+
|
|
|
|
|
+ static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
|
|
|
|
|
+ // NOTE: May want to consider making this an assertion or something
|
|
|
|
|
+ if (shift >= list.size()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const auto begin_range = list.begin();
|
|
|
|
|
+ const auto end_range = std::next(begin_range, shift);
|
|
|
|
|
+ list.splice(list.end(), list, begin_range, end_range);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static void ListSplice(std::list<T>& in_list, const_list_iterator position,
|
|
|
|
|
+ std::list<T>& out_list, const_list_iterator element) {
|
|
|
|
|
+ in_list.splice(position, out_list, element);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
|
|
|
|
|
+ auto it = list.cbegin();
|
|
|
|
|
+ while (it != list.cend() && *it != element) {
|
|
|
|
|
+ ++it;
|
|
|
|
|
+ }
|
|
|
|
|
+ return it;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::array<std::list<T>, Depth> levels;
|
|
|
|
|
+ u64 used_priorities = 0;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+} // namespace Common
|