gem5  v20.0.0.3
circular_queue.hh
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2018 ARM Limited
3  * All rights reserved
4  *
5  * The license below extends only to copyright in the software and shall
6  * not be construed as granting a license to any other intellectual
7  * property including but not limited to intellectual property relating
8  * to a hardware implementation of the functionality of the software
9  * licensed hereunder. You may use the software subject to the license
10  * terms below provided that you ensure that this notice is replicated
11  * unmodified and in its entirety in all distributions of the software,
12  * modified or unmodified, in source code or in binary form.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions are
16  * met: redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer;
18  * redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in the
20  * documentation and/or other materials provided with the distribution;
21  * neither the name of the copyright holders nor the names of its
22  * contributors may be used to endorse or promote products derived from
23  * this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #ifndef __BASE_CIRCULAR_QUEUE_HH__
39 #define __BASE_CIRCULAR_QUEUE_HH__
40 
41 #include <cassert>
42 #include <cstddef>
43 #include <cstdint>
44 #include <iterator>
45 #include <type_traits>
46 #include <vector>
47 
81 template <typename T>
82 class CircularQueue : private std::vector<T>
83 {
84  protected:
86  using typename Base::reference;
87  using typename Base::const_reference;
88  const uint32_t _capacity;
89  uint32_t _head;
90  uint32_t _tail;
91  uint32_t _empty;
92 
99  uint32_t _round;
100 
102  static uint32_t
103  moduloAdd(uint32_t op1, uint32_t op2, uint32_t size)
104  {
105  return (op1 + op2) % size;
106  }
107 
109  static uint32_t
110  moduloSub(uint32_t op1, uint32_t op2, uint32_t size)
111  {
112  int32_t ret = sub(op1, op2, size);
113  return ret >= 0 ? ret : ret + size;
114  }
115 
116  static int32_t
117  sub(uint32_t op1, uint32_t op2, uint32_t size)
118  {
119  if (op1 > op2)
120  return (op1 - op2) % size;
121  else
122  return -((op2 - op1) % size);
123  }
124 
125  void increase(uint32_t& v, size_t delta = 1)
126  {
127  v = moduloAdd(v, delta, _capacity);
128  }
129 
130  void decrease(uint32_t& v)
131  {
132  v = (v ? v : _capacity) - 1;
133  }
134 
150  public:
151  struct iterator {
153  uint32_t _idx;
154  uint32_t _round;
155 
156  public:
157  iterator(CircularQueue* cq, uint32_t idx, uint32_t round)
158  : _cq(cq), _idx(idx), _round(round) {}
159 
161  using value_type = T;
162  using difference_type = std::ptrdiff_t;
164  using const_reference = const value_type&;
165  using pointer = value_type*;
166  using const_pointer = const value_type*;
167  using iterator_category = std::random_access_iterator_tag;
168 
172  static_assert(std::is_same<reference, T&>::value,
173  "reference type is not assignable as required");
174 
175  iterator() : _cq(nullptr), _idx(0), _round(0) { }
176 
177  iterator(const iterator& it)
178  : _cq(it._cq), _idx(it._idx), _round(it._round) {}
179 
180  iterator&
181  operator=(const iterator& it)
182  {
183  _cq = it._cq;
184  _idx = it._idx;
185  _round = it._round;
186  return *this;
187  }
188 
189  ~iterator() { _cq = nullptr; _idx = 0; _round = 0; }
190 
204  bool
206  {
207  return _cq != nullptr && _cq->isValidIdx(_idx, _round);
208  }
209 
218  bool operator==(const iterator& that) const
219  {
220  return _cq == that._cq && _idx == that._idx &&
221  _round == that._round;
222  }
223 
228  bool operator!=(const iterator& that)
229  {
230  return !(*this == that);
231  }
232 
235  {
236  /* this has to be dereferenceable. */
237  return (*_cq)[_idx];
238  }
239 
241  {
242  /* this has to be dereferenceable. */
243  return (*_cq)[_idx];
244  }
245 
250  {
251  return &((*_cq)[_idx]);
252  }
253 
255  {
256  return &((*_cq)[_idx]);
257  }
258 
261  {
262  /* this has to be dereferenceable. */
263  _cq->increase(_idx);
264  if (_idx == 0)
265  ++_round;
266  return *this;
267  }
268 
270  iterator
272  {
273  iterator t = *this;
274  ++*this;
275  return t;
276  }
277 
283  private:
291  bool
293  {
294  return _cq && !(_idx == _cq->head() &&
295  (_cq->empty() ||
296  (_idx == 0 && _round != _cq->_round + 1) ||
297  (_idx !=0 && _round != _cq->_round)));
298  }
299 
300  public:
303  {
304  /* this has to be decrementable. */
305  assert(decrementable());
306  if (_idx == 0)
307  --_round;
308  _cq->decrease(_idx);
309  return *this;
310  }
311 
313  iterator operator--(int ) { iterator t = *this; --*this; return t; }
314 
317  {
318  assert(_cq);
319  _round += (t + _idx) / _cq->capacity();
320  _idx = _cq->moduloAdd(_idx, t);
321  return *this;
322  }
323 
325  {
326  assert(_cq);
327 
328  /* C does not do euclidean division, so we have to adjust */
329  if (t >= 0) {
330  _round += (-t + _idx) / _cq->capacity();
331  _idx = _cq->moduloSub(_idx, t);
332  } else {
333  *this += -t;
334  }
335  return *this;
336  }
337 
340  {
341  iterator ret(*this);
342  return ret += t;
343  }
344 
346  {
347  iterator ret = it;
348  return ret += t;
349  }
350 
353  {
354  iterator ret(*this);
355  return ret -= t;
356  }
357 
359  {
360  iterator ret = it;
361  return ret -= t;
362  }
363 
368  {
369  /* If a is already at the end, we can safely return 0. */
370  auto ret = _cq->sub(this->_idx, that._idx, _cq->capacity());
371 
372  if (this->_round != that._round) {
373  ret += ((this->_round - that._round) * _cq->capacity());
374  }
375  return ret;
376  }
377 
381  template<typename Idx>
382  typename std::enable_if<std::is_integral<Idx>::value,reference>::type
383  operator[](const Idx& index) { return *(*this + index); }
384 
386  bool
387  operator<(const iterator& that) const
388  {
389  assert(_cq && that._cq == _cq);
390  return (this->_round < that._round) ||
391  (this->_round == that._round && _idx < that._idx);
392  }
393 
394  bool
395  operator>(const iterator& that) const
396  { return !(*this <= that); }
397 
398  bool operator>=(const iterator& that) const
399  { return !(*this < that); }
400 
401  bool operator<=(const iterator& that) const
402  { return !(that < *this); }
403 
405  size_t idx() const { return _idx; }
406  };
407 
408  public:
409  using Base::operator[];
410 
411  explicit CircularQueue(uint32_t size = 0)
412  : _capacity(size), _head(1), _tail(0), _empty(true), _round(0)
413  {
414  Base::resize(size);
415  }
416 
423  void flush()
424  {
425  _head = 1;
426  _round = 0;
427  _tail = 0;
428  _empty = true;
429  }
430 
432  bool isValidIdx(size_t idx) const
433  {
434  /* An index is invalid if:
435  * - The queue is empty.
436  * (6,T,3,2): |-|-|-]|[-|-|x|
437  * - head is small than tail and:
438  * - It is greater than both head and tail.
439  * (6,F,1,3): |-|[o|o|o]|-|x|
440  * - It is less than both head and tail.
441  * (6,F,1,3): |x|[o|o|o]|-|-|
442  * - It is greater than the tail and not than the head.
443  * (6,F,4,1): |o|o]|-|x|[o|o|
444  */
445  return !(_empty || (
446  (_head < _tail) && (
447  (_head < idx && _tail < idx) ||
448  (_head > idx && _tail > idx)
449  )) || (_tail < idx && idx < _head));
450  }
451 
455  bool isValidIdx(size_t idx, uint32_t round) const
456  {
457  /* An index is valid if:
458  * - The queue is not empty.
459  * - round == R and
460  * - index <= tail (if index > tail, that would be PTE)
461  * - Either:
462  * - head <= index
463  * (6,F,1,3,R): |-|[o|(*,r)|o]|-|-|
464  * - head > tail
465  * (6,F,5,3,R): |o|o|(*,r)|o]|-|[o|
466  * The remaining case means the the iterator is BTB:
467  * (6,F,3,4,R): |-|-|(x,r)|[o|o]|-|
468  * - round + 1 == R and:
469  * - index > tail. If index <= tail, that would be BTB:
470  * (6,F,2,3,r): | -|- |[(*,r)|o]|-|-|
471  * (6,F,0,1,r+1): |[o|o]| (x,r)|- |-|-|
472  * (6,F,0,3,r+1): |[o|o | (*,r)|o]|-|-|
473  * - index >= head. If index < head, that would be BTB:
474  * (6,F,5,2,R): |o|o]|-|-|(x,r)|[o|
475  * - head > tail. If head <= tail, that would be BTB:
476  * (6,F,3,4,R): |[o|o]|(x,r)|-|-|-|
477  * Other values of the round meand that the index is PTE or BTB
478  */
479  return (!_empty && (
480  (round == _round && idx <= _tail && (
481  _head <= idx || _head > _tail)) ||
482  (round + 1 == _round &&
483  idx > _tail &&
484  idx >= _head &&
485  _head > _tail)
486  ));
487  }
488 
489  reference front() { return (*this)[_head]; }
490  reference back() { return (*this)[_tail]; }
491  uint32_t head() const { return _head; }
492  uint32_t tail() const { return _tail; }
493  size_t capacity() const { return _capacity; }
494 
495  uint32_t size() const
496  {
497  if (_empty)
498  return 0;
499  else if (_head <= _tail)
500  return _tail - _head + 1;
501  else
502  return _capacity - _head + _tail + 1;
503  }
504 
505  uint32_t moduloAdd(uint32_t s1, uint32_t s2) const
506  {
507  return moduloAdd(s1, s2, _capacity);
508  }
509 
510  uint32_t moduloSub(uint32_t s1, uint32_t s2) const
511  {
512  return moduloSub(s1, s2, _capacity);
513  }
514 
523  void pop_front(size_t num_elem = 1)
524  {
525  if (num_elem == 0) return;
526  auto hIt = begin();
527  hIt += num_elem;
528  assert(hIt <= end());
529  _empty = hIt == end();
530  _head = hIt._idx;
531  }
532 
534  void pop_back()
535  {
536  assert (!_empty);
537  _empty = _head == _tail;
538  if (_tail == 0)
539  --_round;
540  decrease(_tail);
541  }
542 
544  void push_back(typename Base::value_type val)
545  {
546  advance_tail();
547  (*this)[_tail] = val;
548  }
549 
554  {
555  increase(_tail);
556  if (_tail == 0)
557  ++_round;
558 
559  if (_tail == _head && !_empty)
560  increase(_head);
561 
562  _empty = false;
563  }
564 
569  void advance_tail(uint32_t len)
570  {
571  for (auto idx = 0; idx < len; idx++)
572  advance_tail();
573  }
574 
576  bool empty() const { return _empty; }
577 
583  bool full() const
584  {
585  return !_empty &&
586  (_tail + 1 == _head || (_tail + 1 == _capacity && _head == 0));
587  }
588 
591  {
592  if (_empty)
593  return end();
594  else if (_head > _tail)
595  return iterator(this, _head, _round - 1);
596  else
597  return iterator(this, _head, _round);
598  }
599 
600  /* TODO: This should return a const_iterator. */
601  iterator begin() const
602  {
603  if (_empty)
604  return end();
605  else if (_head > _tail)
606  return iterator(const_cast<CircularQueue*>(this), _head,
607  _round - 1);
608  else
609  return iterator(const_cast<CircularQueue*>(this), _head,
610  _round);
611  }
612 
614  {
615  auto poi = moduloAdd(_tail, 1);
616  auto round = _round;
617  if (poi == 0)
618  ++round;
619  return iterator(this, poi, round);
620  }
621 
622  iterator end() const
623  {
624  auto poi = moduloAdd(_tail, 1);
625  auto round = _round;
626  if (poi == 0)
627  ++round;
628  return iterator(const_cast<CircularQueue*>(this), poi, round);
629  }
630 
638  {
639  assert(isValidIdx(idx) || moduloAdd(_tail, 1) == idx);
640  if (_empty)
641  return end();
642 
643  uint32_t round = _round;
644  if (idx > _tail) {
645  if (idx >= _head && _head > _tail) {
646  round -= 1;
647  }
648  } else if (idx < _head && _tail + 1 == _capacity) {
649  round += 1;
650  }
651  return iterator(this, idx, round);
652  }
653 };
654 
655 #endif /* __BASE_CIRCULARQUEUE_HH__ */
Bitfield< 30, 0 > index
T value_type
Iterator Traits.
Bitfield< 28 > v
Iterator to the circular queue.
iterator begin()
Iterators.
iterator & operator=(const iterator &it)
iterator & operator++()
Pre-increment operator.
uint32_t head() const
static uint32_t moduloAdd(uint32_t op1, uint32_t op2, uint32_t size)
General modular addition.
uint32_t tail() const
std::random_access_iterator_tag iterator_category
iterator begin() const
static int32_t sub(uint32_t op1, uint32_t op2, uint32_t size)
bool full() const
Is the queue full? A queue is full if the head is the 0^{th} element and the tail is the (size-1)^{th...
reference back()
const value_type * const_pointer
const_pointer operator->() const
uint32_t moduloAdd(uint32_t s1, uint32_t s2) const
bool operator!=(const iterator &that)
Inequality operator.
void decrease(uint32_t &v)
bool operator==(const iterator &that) const
InputIterator.
iterator getIterator(size_t idx)
Return an iterator to an index in the vector.
uint32_t size() const
friend iterator operator+(const difference_type &t, iterator &it)
size_t capacity() const
size_t idx() const
OutputIterator has no extra requirements.
STL vector class.
Definition: stl.hh:37
void push_back(typename Base::value_type val)
Pushes an element at the end of the queue.
uint32_t _round
Counter for how many times the tail wraps around.
Bitfield< 63 > val
Definition: misc.hh:769
iterator end() const
static uint32_t moduloSub(uint32_t op1, uint32_t op2, uint32_t size)
General modular subtraction.
iterator & operator--()
Pre-decrement operator.
uint8_t type
Definition: inet.hh:328
void advance_tail()
Increases the tail by one.
iterator end()
void pop_back()
Circularly decrease the tail pointer.
pointer operator->()
Dereference operator.
iterator & operator-=(const difference_type &t)
uint32_t moduloSub(uint32_t s1, uint32_t s2) const
Bitfield< 18, 16 > len
std::ptrdiff_t difference_type
CircularQueue(uint32_t size=0)
std::enable_if< std::is_integral< Idx >::value, reference >::type operator[](const Idx &index)
Index operator.
const value_type & const_reference
difference_type operator-(const iterator &that)
Difference operator.
bool decrementable() const
ForwardIterator The multipass guarantee is provided by the reliance on _idx.
bool empty() const
Is the queue empty?
void pop_front(size_t num_elem=1)
Circularly increase the head pointer.
iterator operator++(int)
Post-increment operator.
iterator operator+(const difference_type &t)
Addition operator.
bool operator>=(const iterator &that) const
bool isValidIdx(size_t idx, uint32_t round) const
Test if the index is in the range of valid elements.
void flush()
Remove all the elements in the queue.
Circular queue.
iterator()
Trait reference type iterator satisfies OutputIterator, therefore reference must be T&...
void increase(uint32_t &v, size_t delta=1)
bool dereferenceable() const
Test dereferenceability.
iterator & operator+=(const difference_type &t)
RandomAccessIterator requirements.
iterator operator--(int)
Post-decrement operator.
bool operator<=(const iterator &that) const
iterator operator-(const difference_type &t)
Substraction operator.
bool operator>(const iterator &that) const
iterator(const iterator &it)
void advance_tail(uint32_t len)
Increases the tail by a specified number of steps.
reference operator*()
Dereference operator.
bool isValidIdx(size_t idx) const
Test if the index is in the range of valid elements.
bool operator<(const iterator &that) const
Comparisons.
const uint32_t _capacity
Bitfield< 5 > t
friend iterator operator-(const difference_type &t, iterator &it)
const_reference operator*() const
iterator(CircularQueue *cq, uint32_t idx, uint32_t round)
reference front()

Generated on Fri Jul 3 2020 15:52:58 for gem5 by doxygen 1.8.13