2 * Copyright (c) 2017-2018 ARM Limited
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.
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.
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.
38 #ifndef __BASE_CIRCULAR_QUEUE_HH__
39 #define __BASE_CIRCULAR_QUEUE_HH__
45 #include <type_traits>
49 * Circular queue implemented in a standard vector. All indices are
50 * monotonically increasing, and modulo is used at access time to alias them
51 * down to the actual storage.
53 * The queue keeps track of two pieces of state, a head index, which is the
54 * index of the next element to come out of the queue, and a size which is how
55 * many valid elements are currently in the queue. Size can increase to, but
56 * never exceed, the capacity of the queue.
58 * In theory the index may overflow at some point, but since it's a 64 bit
59 * value that would take a very long time.
61 * @tparam T Type of the elements in the queue
63 * @ingroup api_base_utils
71 using reference = typename std::vector<T>::reference;
72 using const_reference = typename std::vector<T>::const_reference;
73 const size_t _capacity;
77 /** Iterator to the circular queue.
78 * iterator implementation to provide wrap around indexing which the
79 * vector iterator does not.
85 CircularQueue* _cq = nullptr;
89 * @ingroup api_base_utils
91 iterator(CircularQueue* cq, size_t idx) : _cq(cq), _idx(idx) {}
97 * @ingroup api_base_utils
100 using value_type = T;
101 using difference_type = std::ptrdiff_t;
102 using reference = value_type&;
103 using const_reference = const value_type&;
104 using pointer = value_type*;
105 using const_pointer = const value_type*;
106 using iterator_category = std::random_access_iterator_tag;
107 /** @} */ // end of api_base_utils
109 /** Trait reference type
110 * iterator satisfies OutputIterator, therefore reference
112 static_assert(std::is_same<reference, T&>::value,
113 "reference type is not assignable as required");
116 * @ingroup api_base_utils
118 iterator(const iterator& it) : _cq(it._cq), _idx(it._idx) {}
121 * @ingroup api_base_utils
124 operator=(const iterator& it)
132 * Test dereferenceability.
133 * An iterator is dereferenceable if it is pointing to a non-null
134 * circular queue, and the index is within the current range of the
137 * @ingroup api_base_utils
140 dereferenceable() const
142 return _cq != nullptr && _cq->isValidIdx(_idx);
145 /** InputIterator. */
149 * Two iterators must point to the same, possibly null, circular
150 * queue and the same element on it to be equal.
152 * @ingroup api_base_utils
155 operator==(const iterator& that) const
157 return _cq == that._cq && _idx == that._idx;
161 * Inequality operator.
162 * Conversely, two iterators are different if they both point to
163 * different circular queues or they point to different elements.
165 * @ingroup api_base_utils
167 bool operator!=(const iterator& that) { return !(*this == that); }
170 * Dereference operator.
172 * @ingroup api_base_utils
177 // This has to be dereferenceable.
182 * @ingroup api_base_utils
187 // This has to be dereferenceable.
192 * Dereference operator.
193 * Rely on operator* to check for dereferenceability.
195 * @ingroup api_base_utils
197 pointer operator->() { return &**this; }
200 * @ingroup api_base_utils
202 const_pointer operator->() const { return &**this; }
205 * Pre-increment operator.
207 * @ingroup api_base_utils
217 * Post-increment operator.
219 * @ingroup api_base_utils
230 * The multipass guarantee is provided by the reliance on _idx.
233 /** BidirectionalIterator requirements. */
236 * Pre-decrement operator.
238 * @ingroup api_base_utils
243 /* this has to be decrementable. */
244 assert(_cq && _idx > _cq->head());
250 * Post-decrement operator.
252 * @ingroup api_base_utils
263 * RandomAccessIterator requirements.
265 * @ingroup api_base_utils
268 operator+=(const difference_type& t)
275 * @ingroup api_base_utils
278 operator-=(const difference_type& t)
280 assert(_cq && _idx >= _cq->head() + t);
288 * @ingroup api_base_utils
291 operator+(const difference_type& t)
298 * @ingroup api_base_utils
301 operator+(const difference_type& t, iterator& it)
308 * Substraction operator.
310 * @ingroup api_base_utils
313 operator-(const difference_type& t)
320 * @ingroup api_base_utils
323 operator-(const difference_type& t, iterator& it)
330 * Difference operator.
333 * @ingroup api_base_utils
336 operator-(const iterator& that)
338 return (ssize_t)_idx - (ssize_t)that._idx;
343 * The use of * tests for dereferenceability.
345 * @ingroup api_base_utils
347 template<typename Idx>
348 typename std::enable_if_t<std::is_integral<Idx>::value, reference>
349 operator[](const Idx& index)
351 return *(*this + index);
357 * @ingroup api_base_utils
360 operator<(const iterator& that) const
362 return _idx < that._idx;
366 * @ingroup api_base_utils
368 bool operator>(const iterator& that) const { return !(*this <= that); }
371 * @ingroup api_base_utils
373 bool operator>=(const iterator& that) const { return !(*this < that); }
376 * @ingroup api_base_utils
378 bool operator<=(const iterator& that) const { return !(that < *this); }
381 * OutputIterator has no extra requirements.
383 size_t idx() const { return _idx; }
388 * @ingroup api_base_utils
390 template <typename Idx>
391 typename std::enable_if_t<std::is_integral<Idx>::value, reference>
392 operator[](const Idx& index)
395 return data[index % _capacity];
398 template <typename Idx>
399 typename std::enable_if_t<std::is_integral<Idx>::value, const_reference>
400 operator[](const Idx& index) const
403 return data[index % _capacity];
407 * @ingroup api_base_utils
409 explicit CircularQueue(size_t size=0) : data(size), _capacity(size) {}
412 * Remove all the elements in the queue.
414 * Note: This does not actually remove elements from the backing
417 * @ingroup api_base_utils
427 * Test if the index is in the range of valid elements.
430 isValidIdx(size_t idx) const
432 return _head <= idx && idx < (_head + _size);
436 * @ingroup api_base_utils
438 reference front() { return (*this)[head()]; }
441 * @ingroup api_base_utils
443 reference back() { return (*this)[tail()]; }
446 * @ingroup api_base_utils
448 size_t head() const { return _head; }
451 * @ingroup api_base_utils
453 size_t tail() const { return _head + _size - 1; }
456 * @ingroup api_base_utils
458 size_t capacity() const { return _capacity; }
461 * @ingroup api_base_utils
463 size_t size() const { return _size; }
465 /** Circularly increase the head pointer.
466 * By increasing the head pointer we are removing elements from
467 * the begin of the circular queue.
469 * @params num_elem number of elements to remove
471 * @ingroup api_base_utils
474 pop_front(size_t num_elem=1)
476 assert(num_elem <= size());
482 * Circularly decrease the tail pointer.
484 * @ingroup api_base_utils
494 * Pushes an element at the end of the queue.
496 * @ingroup api_base_utils
499 push_back(typename std::vector<T>::value_type val)
506 * Increases the tail by one. This may overwrite the head if the queue is
509 * @ingroup api_base_utils
521 * Increases the tail by a specified number of steps. This may overwrite
522 * one or more entries starting at the head if the queue is full.
524 * @param len Number of steps
526 * @ingroup api_base_utils
529 advance_tail(size_t len)
531 size_t remaining = _capacity - _size;
532 if (len > remaining) {
533 size_t overflow = len - remaining;
541 * Is the queue empty?
543 * @ingroup api_base_utils
545 bool empty() const { return _size == 0; }
549 * A queue is full if the head is the 0^{th} element and the tail is
550 * the (size-1)^{th} element, or if the head is the n^{th} element and
551 * the tail the (n-1)^{th} element.
553 * @ingroup api_base_utils
555 bool full() const { return _size == _capacity; }
560 * @ingroup api_base_utils
562 iterator begin() { return iterator(this, _head); }
564 /* TODO: This should return a const_iterator. */
566 * @ingroup api_base_utils
571 return iterator(const_cast<CircularQueue*>(this), _head);
575 * @ingroup api_base_utils
577 iterator end() { return iterator(this, tail() + 1); }
580 * @ingroup api_base_utils
585 return iterator(const_cast<CircularQueue*>(this), tail() + 1);
588 /** Return an iterator to an index in the queue. */
589 iterator getIterator(size_t idx) { return iterator(this, idx); }
592 #endif /* __BASE_CIRCULARQUEUE_HH__ */