C/C++ API Reference
Loading...
Searching...
No Matches
deque.h
1// Copyright 2025 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 <algorithm>
17#include <cstddef>
18#include <cstdint>
19#include <iterator>
20#include <limits>
21#include <type_traits>
22#include <utility>
23
24#include "lib/stdcompat/utility.h"
25#include "pw_allocator/allocator.h"
26#include "pw_allocator/unique_ptr.h"
27#include "pw_assert/assert.h"
28#include "pw_containers/internal/count_and_capacity.h"
29#include "pw_containers/internal/generic_deque.h"
30#include "pw_containers/storage.h"
31#include "pw_polyfill/language_feature_macros.h"
32#include "pw_span/span.h"
33
34namespace pw {
35
37
56template <typename T, typename SizeType = uint16_t>
58 Deque<T, SizeType>,
59 T,
60 containers::internal::CountAndCapacity<SizeType>> {
61 private:
64 T,
65 containers::internal::CountAndCapacity<SizeType>>;
66
67 public:
68 using typename Base::const_iterator;
69 using typename Base::const_pointer;
70 using typename Base::const_reference;
71 using typename Base::difference_type;
72 using typename Base::iterator;
73 using typename Base::pointer;
74 using typename Base::reference;
75 using typename Base::size_type;
76 using typename Base::value_type;
77
84 explicit constexpr Deque(span<std::byte> unaligned_buffer) noexcept
85 : Deque(Aligned::Align(unaligned_buffer)) {}
86
89 template <size_t kAlignment, size_t kSizeBytes>
90 explicit constexpr Deque(
92 : Deque(Aligned(buffer.data(), buffer.size())) {
93 static_assert(kAlignment >= alignof(value_type));
94 }
95
97 Deque(const Deque&) = delete;
98 Deque& operator=(const Deque&) = delete;
99
101 Deque(Deque&&) = delete;
102 Deque& operator=(Deque&&) = delete;
103
104 PW_CONSTEXPR_CPP20 ~Deque() { Base::DestroyAll(); }
105
106 constexpr size_type max_size() const noexcept { return Base::capacity(); }
107
108 private:
109 friend Base;
110
111 template <typename, typename>
112 friend class Queue;
113
114 template <typename, size_t, typename>
115 friend class FixedDeque;
116
117 static constexpr bool kFixedCapacity = true; // uses a fixed buffer
118
119 // Span that is known to be aligned as T.
120 class Aligned {
121 public:
122 // Construct from an UNALIGNED span. Adjusts alignment if necessary.
123 static constexpr Aligned Align(span<std::byte> unaligned_buffer) {
124 Aligned buffer(unaligned_buffer.data(), unaligned_buffer.size());
125
126 if constexpr (alignof(value_type) > alignof(std::byte)) {
127 void* data_void = buffer.data_;
128 buffer.data_ = static_cast<std::byte*>(std::align(
129 alignof(value_type), sizeof(value_type), data_void, buffer.size_));
130 if (buffer.data_ == nullptr) {
131 buffer.size_ = 0; // cannot fit any items
132 }
133 }
134
135 return buffer;
136 }
137
138 // Asserts that a buffer is aligned at least as T.
139 static constexpr Aligned Assert(std::byte* data, size_t size) {
140 void* buffer_start = data;
141 PW_ASSERT(reinterpret_cast<uintptr_t>(buffer_start) % alignof(T) == 0);
142 return Aligned(data, size);
143 }
144
145 constexpr Aligned(std::byte* aligned_data, size_t size_bytes)
146 : data_(aligned_data), size_(size_bytes) {}
147
148 constexpr std::byte* data() const { return data_; }
149 constexpr size_t capacity() const { return size_ / sizeof(value_type); }
150
151 private:
152 std::byte* data_;
153 size_t size_;
154 };
155
156 explicit constexpr Deque(Aligned buffer) noexcept
157 : Base(static_cast<size_type>(buffer.capacity())),
158 buffer_(buffer.data()) {}
159
160 constexpr void MoveBufferFrom(Deque& other) noexcept {
161 Base::DestroyAll();
162 buffer_ = cpp20::exchange(other.buffer_, nullptr);
163 Base::MoveAssignIndices(other);
164 }
165
166 void MoveItemsFrom(Deque& other) {
167 this->assign(std::make_move_iterator(other.begin()),
168 std::make_move_iterator(other.end()));
169 other.clear();
170 }
171
172 void SwapBufferWith(Deque& other) noexcept {
173 Base::SwapIndices(other);
174 std::swap(buffer_, other.buffer_);
175 }
176
177 void SwapValuesWith(Deque& other);
178
179 pointer data() { return std::launder(reinterpret_cast<pointer>(buffer_)); }
180 const_pointer data() const {
181 return std::launder(reinterpret_cast<const_pointer>(buffer_));
182 }
183
184 // Pointer to the buffer used for the deque. This may differ from the pointer
185 // passed into the constructor if it had to be shifted for alignment.
186 std::byte* buffer_;
187};
188
203template <typename T,
204 size_t kInlineCapacity = containers::kExternalStorage,
205 typename S = typename Deque<T>::size_type>
206class FixedDeque final : private containers::StorageBaseFor<T, kInlineCapacity>,
207 public Deque<T, S> {
208 public:
211 constexpr FixedDeque()
212 : containers::StorageBaseFor<T, kInlineCapacity>{},
213 Deque<T, S>(this->storage()) {}
214
215 FixedDeque(const FixedDeque&) = delete;
216 FixedDeque& operator=(const FixedDeque&) = delete;
217
218 constexpr FixedDeque(FixedDeque&& other) noexcept : FixedDeque() {
219 this->MoveItemsFrom(other);
220 }
221
222 constexpr FixedDeque& operator=(FixedDeque&& other) noexcept {
223 this->clear();
224 this->MoveItemsFrom(other);
225 return *this;
226 }
227
230 template <size_t kOtherCapacity,
231 typename = std::enable_if_t<kOtherCapacity <= kInlineCapacity>>
232 FixedDeque(FixedDeque<T, kOtherCapacity>&& other) noexcept : FixedDeque() {
233 this->MoveItemsFrom(other);
234 }
235
236 template <size_t kOtherCapacity,
237 typename = std::enable_if_t<kOtherCapacity <= kInlineCapacity>>
238 FixedDeque& operator=(FixedDeque<T, kOtherCapacity>&& other) noexcept {
239 this->clear();
240 this->MoveItemsFrom(other);
241 return *this;
242 }
243
246 template <size_t kOtherCapacity>
248 this->SwapValuesWith(other);
249 }
250
252 constexpr Deallocator* deallocator() const { return nullptr; }
253
254 private:
255 static_assert(
256 kInlineCapacity <= std::numeric_limits<S>::max(),
257 "The capacity is too large for the size_type; use a larger size_type");
258};
259
268template <typename T, typename S>
269class FixedDeque<T, containers::kExternalStorage, S> final
270 : public Deque<T, S> {
271 public:
277 static FixedDeque Allocate(Allocator& allocator, const S capacity) {
278 FixedDeque deque = TryAllocate(allocator, capacity);
279 PW_ASSERT(deque.capacity() == capacity);
280 return deque;
281 }
282
289 static FixedDeque TryAllocate(Allocator& allocator, S capacity) {
290 const allocator::Layout layout = allocator::Layout::Of<T[]>(capacity);
291 std::byte* array = static_cast<std::byte*>(allocator.Allocate(layout));
292 if (array == nullptr) {
293 return FixedDeque(Aligned(array, 0u), nullptr);
294 }
295 return FixedDeque(Aligned(array, layout.size()), &allocator);
296 }
297
302 static FixedDeque WithStorage(UniquePtr<std::byte[]>&& storage) {
303 const size_t size = storage.size();
304 Deallocator* deallocator = storage.deallocator();
305 return FixedDeque(Aligned::Assert(storage.Release(), size), deallocator);
306 }
307
315 explicit constexpr FixedDeque(span<std::byte> unaligned_buffer) noexcept
316 : Deque<T, S>(unaligned_buffer), deallocator_(nullptr) {}
317
322 template <size_t kAlignment, size_t kSizeBytes>
323 explicit constexpr FixedDeque(
325 : Deque<T, S>(buffer), deallocator_(nullptr) {}
326
328 FixedDeque(const FixedDeque&) = delete;
329 FixedDeque& operator=(const FixedDeque&) = delete;
330
335 constexpr FixedDeque(FixedDeque&& other) noexcept
336 : Deque<T, S>({}),
337 deallocator_(cpp20::exchange(other.deallocator_, nullptr)) {
338 this->MoveBufferFrom(other);
339 }
340
341 constexpr FixedDeque& operator=(FixedDeque&& other) noexcept {
342 if (this == &other) {
343 return *this;
344 }
345 T* const data = this->data();
346 this->MoveBufferFrom(other);
347
348 if (deallocator_ != nullptr) {
349 deallocator_->Deallocate(data);
350 }
351 deallocator_ = cpp20::exchange(other.deallocator_, nullptr);
352 return *this;
353 }
354
355 ~FixedDeque() {
356 // Clear the deque so that the base `Deque` destructor will not need to
357 // access its buffer, which will be freed.
358 this->clear();
359 if (deallocator_ != nullptr) {
360 deallocator_->Deallocate(this->data());
361 }
362 }
363
366 void swap(FixedDeque& other) noexcept {
367 this->SwapBufferWith(other);
368 std::swap(deallocator_, other.deallocator_);
369 }
370
375 template <size_t kInlineCapacity>
377 other.swap(*this); // Use the swap impl for statically allocated deques.
378 }
379
382 constexpr Deallocator* deallocator() const { return deallocator_; }
383
384 private:
385 using typename Deque<T, S>::Aligned;
386
387 constexpr FixedDeque(Aligned buffer, Deallocator* deallocator)
388 : Deque<T, S>(buffer), deallocator_(deallocator) {}
389
390 Deallocator* deallocator_;
391};
392
393template <typename T, typename SizeType>
394void Deque<T, SizeType>::SwapValuesWith(Deque& other) {
395 Deque* smaller;
396 Deque* larger;
397
398 if (this->size() < other.size()) {
399 smaller = this;
400 larger = &other;
401 } else {
402 smaller = &other;
403 larger = this;
404 }
405
406 const SizeType smaller_size = smaller->size();
407 for (auto it =
408 std::swap_ranges(smaller->begin(), smaller->end(), larger->begin());
409 it != larger->end();
410 ++it) {
411 smaller->push_back(std::move(*it));
412 }
413
414 while (larger->size() > smaller_size) {
415 larger->pop_back();
416 }
417}
418
419} // namespace pw
Definition: allocator.h:36
void * Allocate(Layout layout)
Definition: allocator.h:44
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: deque.h:60
Definition: deque.h:207
Definition: unique_ptr.h:43
Definition: layout.h:58
Definition: storage.h:86
Definition: storage.h:39
Definition: generic_deque.h:185
constexpr size_type size() const noexcept
Returns the number of elements in the deque.
Definition: generic_deque.h:69
constexpr size_type capacity() const noexcept
Returns the maximum number of elements in the deque.
Definition: generic_deque.h:74
Definition: span_impl.h:235
constexpr FixedDeque()
Definition: deque.h:211
FixedDeque(const FixedDeque &)=delete
Copying is not supported since it can fail.
void swap(FixedDeque< T, kOtherCapacity, S > &other)
Definition: deque.h:247
constexpr Deallocator * deallocator() const
Definition: deque.h:382
Deque(const Deque &)=delete
Copying is not supported since it can fail.
constexpr Deallocator * deallocator() const
Returns nullptr; a FixedDeque with static storage never allocates.
Definition: deque.h:252
constexpr Deque(span< std::byte > unaligned_buffer) noexcept
Definition: deque.h:84
static FixedDeque Allocate(Allocator &allocator, const S capacity)
Definition: deque.h:277
constexpr FixedDeque(FixedDeque &&other) noexcept
Definition: deque.h:335
MoveItemsFrom(other)
constexpr Deque(containers::Storage< kAlignment, kSizeBytes > &buffer) noexcept
Definition: deque.h:90
static FixedDeque WithStorage(UniquePtr< std::byte[]> &&storage)
Definition: deque.h:302
constexpr FixedDeque(containers::Storage< kAlignment, kSizeBytes > &buffer) noexcept
Definition: deque.h:323
Deque(Deque &&)=delete
Move is not supported to avoid confusion about deque/buffer pairings.
void assign(size_type count, const value_type &value)
Sets the contents to count copies of value. Crashes if cannot fit.
Definition: generic_deque.h:216
void swap(FixedDeque< T, kInlineCapacity, S > &other)
Definition: deque.h:376
static FixedDeque TryAllocate(Allocator &allocator, S capacity)
Definition: deque.h:289
void swap(FixedDeque &other) noexcept
Definition: deque.h:366
constexpr FixedDeque(span< std::byte > unaligned_buffer) noexcept
Definition: deque.h:315
constexpr size_t kExternalStorage
Reserved capacity value for container specializations with external storage.
Definition: storage.h:79
#define PW_CONSTEXPR_CPP20
Definition: language_feature_macros.h:27
The Pigweed namespace.
Definition: alignment.h:27