xref: /aosp_15_r20/external/pigweed/pw_containers/public/pw_containers/inline_queue.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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