committed by
FernandoS27
3 changed files with 349 additions and 0 deletions
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue