1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <initializer_list> 17 #include <utility> 18 19 #include "pw_containers/inline_deque.h" 20 21 namespace pw { 22 23 template <typename T, typename SizeType, size_t kCapacity> 24 class BasicInlineQueue; 25 26 /// The `InlineQueue` class is similar to `std::queue<T, std::deque>`, except 27 /// it is backed by a fixed-size buffer. `InlineQueue`'s must be declared with 28 /// an explicit maximum size (e.g. `InlineQueue<int, 10>>`) but deques can be 29 /// used and referred to without the max size template parameter (e.g. 30 /// `InlineQueue<int>`). 31 /// 32 /// `pw::InlineQueue` is wrapper around `pw::InlineDeque` with a simplified 33 /// API and `push_overwrite()` & `emplace_overwrite()` helpers. 34 template <typename T, size_t kCapacity = containers::internal::kGenericSized> 35 using InlineQueue = BasicInlineQueue<T, uint16_t, kCapacity>; 36 37 template <typename ValueType, 38 typename SizeType, 39 size_t kCapacity = containers::internal::kGenericSized> 40 class BasicInlineQueue 41 : public BasicInlineQueue<ValueType, 42 SizeType, 43 containers::internal::kGenericSized> { 44 private: 45 using QueueBase = BasicInlineQueue<ValueType, 46 SizeType, 47 containers::internal::kGenericSized>; 48 49 public: 50 using typename QueueBase::const_iterator; 51 using typename QueueBase::const_pointer; 52 using typename QueueBase::const_reference; 53 using typename QueueBase::difference_type; 54 using typename QueueBase::iterator; 55 using typename QueueBase::pointer; 56 using typename QueueBase::reference; 57 using typename QueueBase::size_type; 58 using typename QueueBase::value_type; 59 60 // Constructors 61 BasicInlineQueue()62 constexpr BasicInlineQueue() noexcept : deque_() {} 63 BasicInlineQueue(size_type count,const_reference value)64 BasicInlineQueue(size_type count, const_reference value) 65 : deque_(count, value) {} 66 BasicInlineQueue(size_type count)67 explicit BasicInlineQueue(size_type count) : deque_(count) {} 68 69 template < 70 typename InputIterator, 71 typename = containers::internal::EnableIfInputIterator<InputIterator>> BasicInlineQueue(InputIterator start,InputIterator finish)72 BasicInlineQueue(InputIterator start, InputIterator finish) 73 : deque_(start, finish) {} 74 BasicInlineQueue(const std::initializer_list<value_type> & list)75 BasicInlineQueue(const std::initializer_list<value_type>& list) { 76 *this = list; 77 } 78 79 /// Copy constructs for matching capacity. BasicInlineQueue(const BasicInlineQueue & other)80 BasicInlineQueue(const BasicInlineQueue& other) { *this = other; } 81 82 /// Copy constructs for mismatched capacity. 83 /// 84 /// Note that this will result in a crash if `kOtherCapacity < size()`. 85 template <size_t kOtherCapacity> BasicInlineQueue(const BasicInlineQueue<ValueType,SizeType,kOtherCapacity> & other)86 BasicInlineQueue( 87 const BasicInlineQueue<ValueType, SizeType, kOtherCapacity>& other) { 88 *this = other; 89 } 90 91 /// Move constructs for matching capacity. BasicInlineQueue(BasicInlineQueue && other)92 BasicInlineQueue(BasicInlineQueue&& other) { *this = std::move(other); } 93 94 /// Move constructs for mismatched capacity. 95 /// 96 /// Note that this will result in a crash if `kOtherCapacity < size()`. 97 template <size_t kOtherCapacity> BasicInlineQueue(BasicInlineQueue<ValueType,SizeType,kOtherCapacity> && other)98 BasicInlineQueue( 99 BasicInlineQueue<ValueType, SizeType, kOtherCapacity>&& other) { 100 *this = std::move(other); 101 } 102 103 template <typename T, typename = containers::internal::EnableIfIterable<T>> BasicInlineQueue(const T & other)104 BasicInlineQueue(const T& other) { 105 *this = other; 106 } 107 108 // 109 BasicInlineQueue& operator=(const std::initializer_list<value_type>& list) { 110 deque_ = std::move(list); 111 return *this; 112 } 113 114 /// Copy assigns from matching capacity. 115 BasicInlineQueue& operator=(const BasicInlineQueue& other) { 116 deque_ = other.deque_; 117 return *this; 118 } 119 120 /// Copy assigns from mismatched capacity. 121 /// 122 /// Note that this will result in a crash if `kOtherCapacity < size()`. 123 template <size_t kOtherCapacity> 124 BasicInlineQueue& operator=( 125 const BasicInlineQueue<ValueType, SizeType, kOtherCapacity>& other) { 126 deque_ = other.deque_; 127 return *this; 128 } 129 130 /// Move assigns from matching capacity. 131 BasicInlineQueue& operator=(BasicInlineQueue&& other) { 132 deque_ = std::move(other.deque_); 133 return *this; 134 } 135 136 /// Move assigns from mismatched capacity. 137 /// 138 /// Note that this will result in a crash if `kOtherCapacity < size()`. 139 template <size_t kOtherCapacity> 140 BasicInlineQueue& operator=( 141 BasicInlineQueue<ValueType, SizeType, kOtherCapacity>&& other) { 142 deque_ = std::move(other.deque_); 143 return *this; 144 } 145 146 template <typename T, typename = containers::internal::EnableIfIterable<T>> 147 BasicInlineQueue& operator=(const T& other) { 148 deque_ = BasicInlineDeque<ValueType, SizeType, kCapacity>(other.begin(), 149 other.end()); 150 return *this; 151 } 152 153 // Size 154 max_size()155 static constexpr size_type max_size() { return capacity(); } capacity()156 static constexpr size_type capacity() { return kCapacity; } 157 158 private: 159 template <typename OtherValueType, typename OtherSizeType, size_t kOtherSized> 160 friend class BasicInlineQueue; 161 162 // The deque() function is defined differently for the generic-sized and 163 // known-sized specializations. This data() implementation simply returns the 164 // generic-sized deque_. The generic-sized deque() function casts *this to a 165 // known zero-sized specialzation to access this exact function. deque()166 BasicInlineDeque<ValueType, SizeType>& deque() { return deque_; } deque()167 const BasicInlineDeque<ValueType, SizeType>& deque() const { return deque_; } 168 169 BasicInlineDeque<ValueType, SizeType, kCapacity> deque_; 170 }; 171 172 // Defines the generic-sized BasicInlineDeque<T> specialization, which 173 // serves as the base class for BasicInlineDeque<T> of any capacity. 174 // 175 // Except for constructors, all other methods should be implemented on this 176 // generic-sized specialization. 177 template <typename ValueType, typename SizeType> 178 class BasicInlineQueue<ValueType, 179 SizeType, 180 containers::internal::kGenericSized> { 181 private: 182 using Deque = BasicInlineDeque<ValueType, SizeType>; 183 184 public: 185 using const_iterator = typename Deque::const_iterator; 186 using const_pointer = typename Deque::const_pointer; 187 using const_reference = typename Deque::const_reference; 188 using difference_type = typename Deque::difference_type; 189 using iterator = typename Deque::iterator; 190 using pointer = typename Deque::pointer; 191 using reference = typename Deque::reference; 192 using size_type = typename Deque::size_type; 193 using value_type = typename Deque::value_type; 194 195 // Access 196 at(size_type index)197 reference at(size_type index) { return deque().at(index); } at(size_type index)198 const_reference at(size_type index) const { return deque().at(index); } 199 200 reference operator[](size_type index) { return deque()[index]; } 201 const_reference operator[](size_type index) const { return deque()[index]; } 202 front()203 reference front() { return deque().front(); } front()204 const_reference front() const { return deque().front(); } 205 back()206 reference back() { return deque().back(); } back()207 const_reference back() const { return deque().back(); } 208 contiguous_data()209 std::pair<span<const value_type>, span<const value_type>> contiguous_data() 210 const { 211 return deque().contiguous_data(); 212 } contiguous_data()213 std::pair<span<value_type>, span<value_type>> contiguous_data() { 214 return deque().contiguous_data(); 215 } 216 217 // Iterate 218 begin()219 iterator begin() noexcept { return deque().begin(); } begin()220 const_iterator begin() const noexcept { return cbegin(); } cbegin()221 const_iterator cbegin() const noexcept { return deque().cbegin(); } 222 end()223 iterator end() noexcept { return deque().end(); } end()224 const_iterator end() const noexcept { return cend(); } cend()225 const_iterator cend() const noexcept { return deque().cend(); } 226 227 // Size 228 empty()229 [[nodiscard]] bool empty() const noexcept { return deque().empty(); } 230 full()231 [[nodiscard]] bool full() const noexcept { return deque().full(); } 232 size()233 size_type size() const noexcept { return deque().size(); } 234 max_size()235 size_type max_size() const noexcept { return capacity(); } 236 capacity()237 size_type capacity() const noexcept { return deque().capacity(); } 238 239 // Modify 240 clear()241 void clear() noexcept { deque().clear(); } 242 push(const value_type & value)243 void push(const value_type& value) { emplace(value); } 244 push(value_type && value)245 void push(value_type&& value) { emplace(std::move(value)); } 246 247 template <typename... Args> emplace(Args &&...args)248 void emplace(Args&&... args) { 249 deque().emplace_back(std::forward<Args>(args)...); 250 } 251 push_overwrite(const value_type & value)252 void push_overwrite(const value_type& value) { emplace_overwrite(value); } 253 push_overwrite(value_type && value)254 void push_overwrite(value_type&& value) { 255 emplace_overwrite(std::move(value)); 256 } 257 258 template <typename... Args> emplace_overwrite(Args &&...args)259 void emplace_overwrite(Args&&... args) { 260 if (full()) { 261 pop(); 262 } 263 emplace(std::forward<Args>(args)...); 264 } 265 pop()266 void pop() { deque().pop_front(); } 267 268 protected: 269 constexpr BasicInlineQueue() noexcept = default; 270 271 // Polymorphic-sized `pw::InlineQueue<T>` may not be used with `unique_ptr` 272 // or `delete`. `delete` could be supported using C++20's destroying delete. 273 ~BasicInlineQueue() = default; 274 275 private: 276 // The underlying BasicInlineDeque is not part of the generic-sized class. It 277 // is provided in the derived class from which this instance was constructed. 278 // To access the data, down-cast this to a known max size specialization, and 279 // return a reference to a generic-sized BasicInlineDeque, which is the same 280 // reference for all sizes. deque()281 BasicInlineDeque<ValueType, SizeType>& deque() { 282 return static_cast<BasicInlineQueue<value_type, size_type, 0>*>(this) 283 ->deque(); 284 } deque()285 const BasicInlineDeque<ValueType, SizeType>& deque() const { 286 return static_cast<const BasicInlineQueue<value_type, size_type, 0>*>(this) 287 ->deque(); 288 } 289 }; 290 291 } // namespace pw 292