Now that we're using c++17, the type_traits with a ::value member have a _v alias which reduces verbosity. Or on other words std::is_integral<T>::value can be replaced with std::is_integral_v<T> Make this substitution throughout the code base. In places where gem5 introduced it's own similar templates, add a V alias, spelled differently to match gem5's internal style. gem5: :IsVarArgs<T>::value => gem5::IsVarArgsV<T> Change-Id: I1d84ffc4a236ad699471569e7916ec17fe5f109a Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/48604 Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com>
598 lines
15 KiB
C++
598 lines
15 KiB
C++
/*
|
|
* 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__ */
|