• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef CHRE_UTIL_ARRAY_QUEUE_H_
18 #define CHRE_UTIL_ARRAY_QUEUE_H_
19 
20 #include <cstddef>
21 #include <iterator>
22 #include <type_traits>
23 
24 #include "chre/util/non_copyable.h"
25 
26 /**
27  * @file
28  * ArrayQueue is a templated fixed-size FIFO queue implemented around a
29  * contiguous array. Two variations on how the
30  *
31  *  1) ArrayQueue<ElementType, kCapacity> allocates the underlying array within
32  *  the ArrayQueue object itself.
33  *  2) ArrayQueueExt<ElementType> accepts a pointer to the storage at
34  *  construction time. Since this variation maintains the capacity of the array
35  *  as a member variable rather than template parameter, it can be useful in
36  *  situations where it'd be inconvenient to include the array capacity in the
37  *  type specification, for example when processing multiple array queues with
38  *  different capacities in a loop or similar construct.
39  *
40  * This variability is accomplished through a base class which provides the
41  * underlying storage, which is attached to the array queue implementation in
42  * ArrayQueueCore via a template parameter, then the two storage options are
43  * composed into public APIs as ArrayQueue and ArrayQueueExt. Users of this
44  * container are not expected to reference ArrayQueueCore or ArrayQueue*Storage
45  * directly, but developers should refer to ArrayQueueCore for API
46  * documentation.
47  */
48 
49 namespace chre {
50 
51 // Forward declaration to support declarations in ArrayQueueCore
52 template <typename ValueType>
53 class ArrayQueueIterator;
54 
55 namespace internal {
56 
57 /**
58  * The core implementation of an array queue, from which the public interfaces
59  * (ArrayQueue and ArrayQueueExt) are derived.
60  *
61  * The StorageType template parameter must be a class supplying data() and
62  * capacity() methods used to access the underlying array storage.
63  */
64 template <typename ElementType, class StorageType>
65 class ArrayQueueCore : public StorageType {
66  public:
67   // Inherit constructors from StorageType
68   using StorageType::StorageType;
69 
70   /**
71    * Calls the destructor of all the elements in the array queue.
72    */
73   ~ArrayQueueCore();
74 
75   // data() and capacity() functions are inherited from StorageType
76 
77   /**
78    * @return true if the array queue is empty.
79    */
80   bool empty() const;
81 
82   /**
83    * @return true if the array queue is full.
84    */
85   bool full() const;
86 
87   /**
88    * @return The number of elements currently stored in the array queue.
89    */
90   size_t size() const;
91 
92   /**
93    * Obtains the front element of the array queue. It is illegal to access the
94    * front element when the array queue is empty. The user of the API must check
95    * the size() or empty() function prior to accessing the front element to
96    * ensure that they will not read out of bounds.
97    *
98    * @return The front element.
99    */
100   ElementType &front();
101   const ElementType &front() const;
102 
103   /**
104    * Obtains the last element in the queue. Illegal to call when empty() is
105    * true.
106    *
107    * @return The last element in the queue.
108    */
109   ElementType &back();
110   const ElementType &back() const;
111 
112   /**
113    * Obtains an element of the array queue given an index. It is illegal to
114    * index this array queue out of bounds and the user of the API must check the
115    * size() function prior to indexing this array queue to ensure that they will
116    * not read out of bounds.
117    *
118    * @param index Requested index in range [0,size()-1]
119    * @return The element.
120    */
121   ElementType &operator[](size_t index);
122 
123   /**
124    * Obtains an element of the array queue given an index. It is illegal to
125    * index this array queue out of bounds and the user of the API must check the
126    * size() function prior to indexing this array queue to ensure that they will
127    * not read out of bounds.
128    *
129    * @param index Requested index in range [0,size()-1]
130    * @return The element.
131    */
132   const ElementType &operator[](size_t index) const;
133 
134   /**
135    * Pushes an element onto the back of the array queue via copy or move
136    * construction. It returns false if the array queue is full already and there
137    * is no room for the elements. All iterators and references are unaffected.
138    *
139    * @param element The element to push onto the array queue.
140    * @return true if the element is pushed successfully.
141    */
142   bool push(const ElementType &element);
143   bool push(ElementType &&element);
144 
145   /**
146    * Pushes an element onto the back of the array queue via copy or move
147    * construction. If the array queue is full the front element is removed
148    * to make room for the new element.
149    *
150    * @param element The element to push onto the array queue.
151    */
152   void kick_push(const ElementType &element);
153   void kick_push(ElementType &&element);
154 
155   /**
156    * Removes the front element from the array queue if the array queue is not
157    * empty. Only iterators and references to the front of the queue are
158    * invalidated.
159    */
160   void pop();
161 
162   /**
163    * Removes the back element from the array queue if the array queue is not
164    * empty. Only iterators and references to the back of the queue are
165    * invalidated.
166    */
167   void pop_back();
168 
169   /**
170    * Removes an element from the array queue given an index. It returns false if
171    * the array queue contains fewer items than the index. All iterators and
172    * references to elements before the removed one are unaffected. Iterators
173    * and references to the removed element or any elements after it are
174    * invalidated.
175    *
176    * @param index Requested index in range [0,size()-1]
177    * @return true if the indexed element has been removed successfully.
178    */
179   bool remove(size_t index);
180 
181   /**
182    * Constructs an element onto the back of the array queue. All iterators and
183    * references are unaffected.
184    *
185    * @param The arguments to the constructor
186    * @return true if the element is constructed successfully.
187    */
188   template <typename... Args>
189   bool emplace(Args &&... args);
190 
191   /**
192    * Removes all the elements of the queue.
193    */
194   void clear();
195 
196   /**
197    * Forward iterator that points to some element in the container.
198    */
199   typedef ArrayQueueIterator<ElementType> iterator;
200   typedef ArrayQueueIterator<const ElementType> const_iterator;
201 
202   /**
203    * @return A forward iterator to the beginning.
204    */
205   iterator begin();
206   const_iterator begin() const;
207   const_iterator cbegin() const;
208 
209   /**
210    * @return A forward iterator to the end.
211    */
212   iterator end();
213   const_iterator end() const;
214   const_iterator cend() const;
215 
216  private:
217   /*
218    * Initialize mTail to be (capacity-1). When an element is pushed in,
219    * mHead and mTail will align. Also, this is consistent with
220    * mSize = (mTail - mHead)%capacity + 1 for mSize > 0.
221    */
222   //! Index of the front element
223   size_t mHead = 0;
224 
225   //! Index of the back element
226   size_t mTail = StorageType::capacity() - 1;
227 
228   //! Number of elements in the array queue
229   size_t mSize = 0;
230 
231   /**
232    * Converts relative index with respect to mHead to absolute index in the
233    * storage array.
234    *
235    * @param index Relative index in range [0,size()-1]
236    * @return The index of the storage array in range [0,kCapacity-1]
237    */
238   size_t relativeIndexToAbsolute(size_t index) const;
239 
240   /*
241    * Pulls mHead to the next element in the array queue and decrements mSize
242    * accordingly. It is illegal to call this function on an empty array queue.
243    */
244   void pullHead();
245 
246   /*
247    * Pulls mTail to the previous element in the array queue and decrements mSize
248    * accordingly. It is illegal to call this function on an empty array queue.
249    */
250   void pullTail();
251 
252   /*
253    * Pushes mTail to the next available storage space and increments mSize
254    * accordingly.
255    *
256    * @return true if the array queue is not full.
257    */
258   bool pushTail();
259 };
260 
261 /**
262  * Storage for ArrayQueue based on an array allocated inside this object.
263  */
264 template <typename ElementType, size_t kCapacity>
265 class ArrayQueueInternalStorage : public NonCopyable {
266  public:
data()267   ElementType *data() {
268     return reinterpret_cast<ElementType *>(mData);
269   }
270 
data()271   const ElementType *data() const {
272     return reinterpret_cast<const ElementType *>(mData);
273   }
274 
capacity()275   size_t capacity() const {
276     return kCapacity;
277   }
278 
279  private:
280   /**
281    * Storage for array queue elements. To avoid static initialization of
282    * members, std::aligned_storage is used.
283    */
284   typename std::aligned_storage<sizeof(ElementType), alignof(ElementType)>::type
285       mData[kCapacity];
286 };
287 
288 /**
289  * Storage for ArrayQueue based on a pointer to an array allocated elsewhere.
290  */
291 template <typename ElementType>
292 class ArrayQueueExternalStorage : public NonCopyable {
293  public:
ArrayQueueExternalStorage(ElementType * storage,size_t capacity)294   ArrayQueueExternalStorage(ElementType *storage, size_t capacity)
295       : mData(storage), kCapacity(capacity) {}
296 
data()297   ElementType *data() {
298     return mData;
299   }
300 
data()301   const ElementType *data() const {
302     return mData;
303   }
304 
capacity()305   size_t capacity() const {
306     return kCapacity;
307   }
308 
309  private:
310   ElementType *mData;
311   const size_t kCapacity;
312 };
313 
314 }  // namespace internal
315 
316 /**
317  * Alias to the array queue implementation with storage allocated inside the
318  * object. This is the interface that most code is expected to use.
319  */
320 template <typename ElementType, size_t kCapacity>
321 class ArrayQueue
322     : public internal::ArrayQueueCore<
323           ElementType,
324           internal::ArrayQueueInternalStorage<ElementType, kCapacity>> {
325  public:
326   typedef ElementType value_type;
327 };
328 
329 /**
330  * Wrapper for the array queue implementation with storage allocated elsewhere.
331  * This is useful in instances where it's inconvenient to have the array's
332  * capacity form part of the type specification.
333  */
334 template <typename ElementType>
335 class ArrayQueueExt
336     : public internal::ArrayQueueCore<
337           ElementType, internal::ArrayQueueExternalStorage<ElementType>> {
338  public:
ArrayQueueExt(ElementType * storage,size_t capacity)339   ArrayQueueExt(ElementType *storage, size_t capacity)
340       : internal::ArrayQueueCore<
341             ElementType, internal::ArrayQueueExternalStorage<ElementType>>(
342             storage, capacity) {}
343 };
344 
345 /**
346  * A template class that implements a forward iterator for the array queue.
347  */
348 template <typename ValueType>
349 class ArrayQueueIterator {
350  public:
351   typedef ValueType value_type;
352   typedef ValueType &reference;
353   typedef ValueType *pointer;
354   typedef std::ptrdiff_t difference_type;
355   typedef std::forward_iterator_tag iterator_category;
356 
357   ArrayQueueIterator() = default;
ArrayQueueIterator(ValueType * pointer,ValueType * base,size_t tail,size_t capacity)358   ArrayQueueIterator(ValueType *pointer, ValueType *base, size_t tail,
359                      size_t capacity)
360       : mPointer(pointer), mBase(base), mTail(tail), mCapacity(capacity) {}
361 
362   bool operator==(const ArrayQueueIterator &right) const {
363     return (mPointer == right.mPointer);
364   }
365 
366   bool operator!=(const ArrayQueueIterator &right) const {
367     return (mPointer != right.mPointer);
368   }
369 
370   ValueType &operator*() {
371     return *mPointer;
372   }
373 
374   ValueType *operator->() {
375     return mPointer;
376   }
377 
378   ArrayQueueIterator &operator++() {
379     if (mPointer == (mBase + mTail)) {
380       // Jump to end() if at tail
381       mPointer = mBase + mCapacity;
382     } else if (mPointer == (mBase + mCapacity - 1)) {
383       // Wrap around in the memory
384       mPointer = mBase;
385     } else {
386       mPointer++;
387     }
388     return *this;
389   }
390 
391   ArrayQueueIterator operator++(int) {
392     ArrayQueueIterator it(*this);
393     operator++();
394     return it;
395   }
396 
397  private:
398   //! Pointer of the iterator.
399   ValueType *mPointer;
400 
401   //! The memory base address of this container.
402   ValueType *mBase;
403 
404   //! The tail offset relative to the memory base address.
405   size_t mTail;
406 
407   //! Number of elements the underlying ArrayQueue can hold
408   size_t mCapacity;
409 };
410 
411 }  // namespace chre
412 
413 #include "chre/util/array_queue_impl.h"
414 
415 #endif  // CHRE_UTIL_ARRAY_QUEUE_H_
416