| /* |
| * Copyright (c) 2017-2018 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef __BASE_CIRCULAR_QUEUE_HH__ |
| #define __BASE_CIRCULAR_QUEUE_HH__ |
| |
| #include <cassert> |
| #include <cstddef> |
| #include <cstdint> |
| #include <iterator> |
| #include <type_traits> |
| #include <vector> |
| |
| namespace gem5 |
| { |
| |
| /** Circular queue. |
| * Circular queue implemented in a standard vector. All indices are |
| * monotonically increasing, and modulo is used at access time to alias them |
| * down to the actual storage. |
| * |
| * The queue keeps track of two pieces of state, a head index, which is the |
| * index of the next element to come out of the queue, and a size which is how |
| * many valid elements are currently in the queue. Size can increase to, but |
| * never exceed, the capacity of the queue. |
| * |
| * In theory the index may overflow at some point, but since it's a 64 bit |
| * value that would take a very long time. |
| * |
| * @tparam T Type of the elements in the queue |
| * |
| * @ingroup api_base_utils |
| */ |
| template <typename T> |
| class CircularQueue |
| { |
| protected: |
| std::vector<T> data; |
| |
| using reference = typename std::vector<T>::reference; |
| using const_reference = typename std::vector<T>::const_reference; |
| const size_t _capacity; |
| size_t _size = 0; |
| size_t _head = 1; |
| |
| /** Iterator to the circular queue. |
| * iterator implementation to provide wrap around indexing which the |
| * vector iterator does not. |
| */ |
| public: |
| struct iterator |
| { |
| public: |
| CircularQueue* _cq = nullptr; |
| size_t _idx = 0; |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator(CircularQueue* cq, size_t idx) : _cq(cq), _idx(idx) {} |
| iterator() = default; |
| |
| /** |
| * Iterator Traits |
| * |
| * @ingroup api_base_utils |
| * @{ |
| */ |
| using value_type = T; |
| using difference_type = std::ptrdiff_t; |
| using reference = value_type&; |
| using const_reference = const value_type&; |
| using pointer = value_type*; |
| using const_pointer = const value_type*; |
| using iterator_category = std::random_access_iterator_tag; |
| /** @} */ // end of api_base_utils |
| |
| /** Trait reference type |
| * iterator satisfies OutputIterator, therefore reference |
| * must be T& */ |
| static_assert(std::is_same_v<reference, T&>, |
| "reference type is not assignable as required"); |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator(const iterator& it) : _cq(it._cq), _idx(it._idx) {} |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator& |
| operator=(const iterator& it) |
| { |
| _cq = it._cq; |
| _idx = it._idx; |
| return *this; |
| } |
| |
| /** |
| * Test dereferenceability. |
| * An iterator is dereferenceable if it is pointing to a non-null |
| * circular queue, and the index is within the current range of the |
| * queue. |
| * |
| * @ingroup api_base_utils |
| */ |
| bool |
| dereferenceable() const |
| { |
| return _cq != nullptr && _cq->isValidIdx(_idx); |
| } |
| |
| /** InputIterator. */ |
| |
| /** |
| * Equality operator. |
| * Two iterators must point to the same, possibly null, circular |
| * queue and the same element on it to be equal. |
| * |
| * @ingroup api_base_utils |
| */ |
| bool |
| operator==(const iterator& that) const |
| { |
| return _cq == that._cq && _idx == that._idx; |
| } |
| |
| /** |
| * Inequality operator. |
| * Conversely, two iterators are different if they both point to |
| * different circular queues or they point to different elements. |
| * |
| * @ingroup api_base_utils |
| */ |
| bool operator!=(const iterator& that) { return !(*this == that); } |
| |
| /** |
| * Dereference operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| reference |
| operator*() |
| { |
| // This has to be dereferenceable. |
| return (*_cq)[_idx]; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| const_reference |
| operator*() const |
| { |
| // This has to be dereferenceable. |
| return (*_cq)[_idx]; |
| } |
| |
| /** |
| * Dereference operator. |
| * Rely on operator* to check for dereferenceability. |
| * |
| * @ingroup api_base_utils |
| */ |
| pointer operator->() { return &**this; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| const_pointer operator->() const { return &**this; } |
| |
| /** |
| * Pre-increment operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator& |
| operator++() |
| { |
| ++_idx; |
| return *this; |
| } |
| |
| /** |
| * Post-increment operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator |
| operator++(int) |
| { |
| iterator t = *this; |
| ++*this; |
| return t; |
| } |
| |
| /** ForwardIterator |
| * The multipass guarantee is provided by the reliance on _idx. |
| */ |
| |
| /** BidirectionalIterator requirements. */ |
| public: |
| /** |
| * Pre-decrement operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator& |
| operator--() |
| { |
| /* this has to be decrementable. */ |
| assert(_cq && _idx > _cq->head()); |
| --_idx; |
| return *this; |
| } |
| |
| /** |
| * Post-decrement operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator |
| operator--(int) |
| { |
| iterator t = *this; |
| --*this; |
| return t; |
| } |
| |
| /** |
| * RandomAccessIterator requirements. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator& |
| operator+=(const difference_type& t) |
| { |
| _idx += t; |
| return *this; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator& |
| operator-=(const difference_type& t) |
| { |
| assert(_cq && _idx >= _cq->head() + t); |
| _idx -= t; |
| return *this; |
| } |
| |
| /** |
| * Addition operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator |
| operator+(const difference_type& t) |
| { |
| iterator ret(*this); |
| return ret += t; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| friend iterator |
| operator+(const difference_type& t, iterator& it) |
| { |
| iterator ret = it; |
| return ret += t; |
| } |
| |
| /** |
| * Substraction operator. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator |
| operator-(const difference_type& t) |
| { |
| iterator ret(*this); |
| return ret -= t; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| friend iterator |
| operator-(const difference_type& t, iterator& it) |
| { |
| iterator ret = it; |
| return ret -= t; |
| } |
| |
| /** |
| * Difference operator. |
| * that + ret == this |
| * |
| * @ingroup api_base_utils |
| */ |
| difference_type |
| operator-(const iterator& that) |
| { |
| return (ssize_t)_idx - (ssize_t)that._idx; |
| } |
| |
| /** |
| * Index operator. |
| * The use of * tests for dereferenceability. |
| * |
| * @ingroup api_base_utils |
| */ |
| template<typename Idx> |
| typename std::enable_if_t<std::is_integral_v<Idx>, reference> |
| operator[](const Idx& index) |
| { |
| return *(*this + index); |
| } |
| |
| /** |
| * Comparisons. |
| * |
| * @ingroup api_base_utils |
| */ |
| bool |
| operator<(const iterator& that) const |
| { |
| return _idx < that._idx; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| bool operator>(const iterator& that) const { return !(*this <= that); } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| bool operator>=(const iterator& that) const { return !(*this < that); } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| bool operator<=(const iterator& that) const { return !(that < *this); } |
| |
| /** |
| * OutputIterator has no extra requirements. |
| */ |
| size_t idx() const { return _idx; } |
| }; |
| |
| public: |
| /** |
| * @ingroup api_base_utils |
| */ |
| template <typename Idx> |
| typename std::enable_if_t<std::is_integral_v<Idx>, reference> |
| operator[](const Idx& index) |
| { |
| assert(index >= 0); |
| return data[index % _capacity]; |
| } |
| |
| template <typename Idx> |
| typename std::enable_if_t<std::is_integral_v<Idx>, const_reference> |
| operator[](const Idx& index) const |
| { |
| assert(index >= 0); |
| return data[index % _capacity]; |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| explicit CircularQueue(size_t size=0) : data(size), _capacity(size) {} |
| |
| /** |
| * Remove all the elements in the queue. |
| * |
| * Note: This does not actually remove elements from the backing |
| * store. |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| flush() |
| { |
| _head = 1; |
| _size = 0; |
| } |
| |
| /** |
| * Test if the index is in the range of valid elements. |
| */ |
| bool |
| isValidIdx(size_t idx) const |
| { |
| return _head <= idx && idx < (_head + _size); |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| reference front() { return (*this)[head()]; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| reference back() { return (*this)[tail()]; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| size_t head() const { return _head; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| size_t tail() const { return _head + _size - 1; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| size_t capacity() const { return _capacity; } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| size_t size() const { return _size; } |
| |
| /** Circularly increase the head pointer. |
| * By increasing the head pointer we are removing elements from |
| * the begin of the circular queue. |
| * |
| * @params num_elem number of elements to remove |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| pop_front(size_t num_elem=1) |
| { |
| assert(num_elem <= size()); |
| _head += num_elem; |
| _size -= num_elem; |
| } |
| |
| /** |
| * Circularly decrease the tail pointer. |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| pop_back() |
| { |
| assert(!empty()); |
| --_size; |
| } |
| |
| /** |
| * Pushes an element at the end of the queue. |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| push_back(typename std::vector<T>::value_type val) |
| { |
| advance_tail(); |
| back() = val; |
| } |
| |
| /** |
| * Increases the tail by one. This may overwrite the head if the queue is |
| * full. |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| advance_tail() |
| { |
| if (full()) |
| ++_head; |
| else |
| ++_size; |
| } |
| |
| /** |
| * Increases the tail by a specified number of steps. This may overwrite |
| * one or more entries starting at the head if the queue is full. |
| * |
| * @param len Number of steps |
| * |
| * @ingroup api_base_utils |
| */ |
| void |
| advance_tail(size_t len) |
| { |
| size_t remaining = _capacity - _size; |
| if (len > remaining) { |
| size_t overflow = len - remaining; |
| _head += overflow; |
| len -= overflow; |
| } |
| _size += len; |
| } |
| |
| /** |
| * Is the queue empty? |
| * |
| * @ingroup api_base_utils |
| */ |
| bool empty() const { return _size == 0; } |
| |
| /** |
| * Is the queue full? |
| * A queue is full if the head is the 0^{th} element and the tail is |
| * the (size-1)^{th} element, or if the head is the n^{th} element and |
| * the tail the (n-1)^{th} element. |
| * |
| * @ingroup api_base_utils |
| */ |
| bool full() const { return _size == _capacity; } |
| |
| /** |
| * Iterators. |
| * |
| * @ingroup api_base_utils |
| */ |
| iterator begin() { return iterator(this, _head); } |
| |
| /* TODO: This should return a const_iterator. */ |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator |
| begin() const |
| { |
| return iterator(const_cast<CircularQueue*>(this), _head); |
| } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator end() { return iterator(this, tail() + 1); } |
| |
| /** |
| * @ingroup api_base_utils |
| */ |
| iterator |
| end() const |
| { |
| return iterator(const_cast<CircularQueue*>(this), tail() + 1); |
| } |
| |
| /** Return an iterator to an index in the queue. */ |
| iterator getIterator(size_t idx) { return iterator(this, idx); } |
| }; |
| |
| } // namespace gem5 |
| |
| #endif /* __BASE_CIRCULARQUEUE_HH__ */ |