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