Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
source/EventQueue/PriorityQueue.h
- Committer:
- roywant
- Date:
- 2016-09-19
- Revision:
- 0:ed0152b5c495
File content as of revision 0:ed0152b5c495:
/*
* Copyright (c) 2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef EVENTQUEUE_STACKPRIORITYQUEUE_H_
#define EVENTQUEUE_STACKPRIORITYQUEUE_H_
#include <cstddef>
#include "AlignedStorage.h"
namespace eq {
/**
* Priority queue of Ts.
* Ts are ordered from the smaller to the bigger ( < ).
* Elements in the queue are mutable (this is a design choice).
* After a mutation the function update should be called to ensure that the
* queue is still properly sorted.
* @tparam T type of elements in this queue
* @param capacity Number of elements that this queue can contain
*/
template<typename T, std::size_t Capacity>
class PriorityQueue {
public:
/**
* Type of the nodes in this queue.
*/
struct Node {
AlignedStorage<T> storage; /// storage for the T
Node* next; /// pointer to the next node
};
/**
* Iterator for elements of the queue.
*/
class Iterator {
friend PriorityQueue;
/// Construct an iterator from a Node.
/// This constructor is private and can only be invoked from the PriorityQueue.
Iterator(Node* current) :
_current(current) {
}
public:
/// Indirection operator.
/// return a reference to the inner T
T& operator*() {
return _current->storage.get();
}
/// Const version of indirection operator.
/// return a reference to the inner T
const T& operator*() const {
return _current->storage.get();
}
/// dereference operator.
/// Will invoke the operation on the inner T
T* operator->() {
return &(_current->storage.get());
}
/// const dereference operator.
/// Will invoke the operation on the inner T
const T* operator->() const {
return &(_current->storage.get());
}
/// pre incrementation to the next T in the list
Iterator& operator++() {
_current = _current->next;
return *this;
}
/// post incrementation to the next T in the list
Iterator operator++(int) {
Iterator tmp(*this);
_current = _current->next;
return tmp;
}
/// Equality operator
friend bool operator==(const Iterator& lhs, const Iterator& rhs) {
return lhs._current == rhs._current;
}
/// Unequality operator
friend bool operator!=(const Iterator& lhs, const Iterator& rhs) {
return !(lhs == rhs);
}
/// return the internal node.
Node* get_node() {
return _current;
}
private:
Node* _current;
};
typedef Iterator iterator;
/// Construct an empty priority queue.
PriorityQueue() : nodes(), free_nodes(NULL), head(NULL), used_nodes_count(0) {
initialize();
}
/// Copy construct a priority queue.
/// The queue will have the same content has other.
PriorityQueue(const PriorityQueue& other) :
nodes(), free_nodes(NULL), head(NULL), used_nodes_count(0) {
initialize();
copy(other);
}
/// destroy a priority queue.
~PriorityQueue() {
clear();
}
/// Copy assignemnent from another priority queue.
/// The content of the queue will be destroyed then the content from
/// other will be copied.
PriorityQueue& operator=(const PriorityQueue& other) {
if (&other == this) {
return *this;
}
copy(other);
return *this;
}
/// Push a new element to the queue.
/// It will be added before the first element p in the queue where
/// element < p == true.
/// @return An iterator to the inserted element.
iterator push(const T& element) {
if (full()) {
return NULL;
}
// get a free node
Node* new_node = free_nodes;
free_nodes = free_nodes->next;
new_node->next = NULL;
++used_nodes_count;
// copy content
new (new_node->storage.get_storage()) T(element);
// if there is no node in the queue, just link the head
// to the new node and return
if (head == NULL) {
head = new_node;
return new_node;
}
// if the new node has an higher priority than the node in head
// just link it as the head
if (element < head->storage.get()) {
new_node->next = head;
head = new_node;
return new_node;
}
// insert the node after head
insert_after(head, new_node);
return new_node;
}
/// pop the head of the queue.
bool pop() {
if (!head) {
return false;
}
Node* target = head;
target->storage.get().~T();
head = target->next;
target->next = free_nodes;
free_nodes = target;
--used_nodes_count;
return true;
}
/// If the content of an element is updated is updated after the insertion
/// then, the list can be in an unordered state.
/// This function help; it update the position of an iterator in the list.
void update(iterator it) {
Node* target = it.get_node();
Node* hint = head;
if (target == NULL) {
return;
}
// remove the node from the list
if (target == head) {
// if it is the only node in the list, just return
// it is not needed to update its position
if (target->next == NULL) {
return;
}
// if the order is already correct, just return
if (target->storage.get() < target->next->storage.get()) {
return;
}
// otherwise remove the node from the list
// and update the hint
head = target->next;
hint = head;
} else {
bool node_found = false;
for (Node* current = head; current != NULL; current = current->next) {
if (current->next == target) {
// check if it is needed to move the node
if (current->storage.get() < target->storage.get()) {
if (target->next == NULL) {
return;
}
if (target->storage.get() < target->next->storage.get()) {
return;
}
// there is no need to iterate again the whole list
// just mark the hint has the current node
hint = current;
}
// remove the node from the list and break out of the loop
current->next = target->next;
node_found = true;
break;
}
}
// the node in parameter doesn't belong to this queue
if (!node_found) {
return;
}
}
// insert the node after hint
insert_after(hint, target);
}
/// return an iterator to the begining of the queue.
iterator begin() {
return head;
}
/// return an iterator to the end of the queue.
/// @note can't be dereferenced
iterator end() {
return NULL;
}
/// erase an iterator from the list
bool erase(iterator it) {
return erase(it.get_node());
}
/// erase a node from the list
bool erase(Node* n) {
if (n == NULL) {
return false;
}
if (head == n) {
return pop();
}
Node* current = head;
while (current->next) {
if (current->next == n) {
current->next = n->next;
n->storage.get().~T();
n->next = free_nodes;
free_nodes = n;
--used_nodes_count;
return true;
}
current = current->next;
}
return false;
}
/**
* Indicate if the queue is empty or not.
* @return true if the queue is empty and false otherwise.
* @invariant the queue remains untouched.
*/
bool empty() const {
return head == NULL;
}
/**
* Indicate if the true is full or not.
* @return true if the queue is full and false otherwise.
* @invariant the queue remains untouched.
*/
bool full() const {
return free_nodes == NULL;
}
/**
* Indicate the number of elements in the queue.
* @return the number of elements currently held by the queue.
* @invariant the queue remains untouched.
*/
std::size_t size() const {
return used_nodes_count;
}
/**
* Expose the capacity of the queue in terms of number of elements the
* queue can hold.
* @return the capacity of the queue.
* @invariant this function should always return Capacity.
*/
std::size_t capacity() const {
return Capacity;
}
/**
* Clear the queue from all its elements.
*/
void clear() {
while (head) {
head->storage.get().~T();
Node* tmp = head;
head = head->next;
tmp->next = free_nodes;
free_nodes = tmp;
}
used_nodes_count = 0;
}
private:
void initialize() {
/// link all the nodes together
for (std::size_t i = 0; i < (Capacity - 1); ++i) {
nodes[i].next = &nodes[i + 1];
}
/// the last node does not have a next node
nodes[Capacity - 1].next = NULL;
/// set all the nodes as free
free_nodes = nodes;
}
void copy(const PriorityQueue& other) {
if (empty() == false) {
clear();
}
Node *to_copy = other.head;
Node *previous = NULL;
while (to_copy) {
// pick a free node
Node* new_node = free_nodes;
free_nodes = free_nodes->next;
new_node->next = NULL;
// copy content
new (new_node->storage.get_storage()) T(to_copy->storage.get());
// link into the queue or update head then update previous pointer
if (previous) {
previous->next = new_node;
} else {
head = new_node;
}
previous = new_node;
// update the node to copy
to_copy = to_copy->next;
}
used_nodes_count = other.used_nodes_count;
}
void insert_after(Node* prev, Node* to_insert) {
for (; prev != NULL; prev = prev->next) {
if (prev->next == NULL || to_insert->storage.get() < prev->next->storage.get()) {
to_insert->next = prev->next;
prev->next = to_insert;
break;
}
}
}
Node nodes[Capacity]; //< Nodes of the queue
Node *free_nodes; //< entry point for the list of free nodes
Node *head; //< head of the queue
std::size_t used_nodes_count; // number of nodes used
};
} // namespace eq
#endif /* EVENTQUEUE_STACKPRIORITYQUEUE_H_ */