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
52template <typename T, typename SizeType = uint16_t>
54 Deque<T, SizeType>,
55 T,
56 containers::internal::CountAndCapacity<SizeType>> {
57 private:
60 T,
61 containers::internal::CountAndCapacity<SizeType>>;
62
63 public:
64 using typename Base::const_iterator;
65 using typename Base::const_pointer;
66 using typename Base::const_reference;
67 using typename Base::difference_type;
68 using typename Base::iterator;
69 using typename Base::pointer;
70 using typename Base::reference;
71 using typename Base::size_type;
72 using typename Base::value_type;
73
80 explicit constexpr Deque(span<std::byte> buffer) noexcept
81 : Deque(Aligned::Align(buffer)) {}
82
85 template <size_t kAlignment, size_t kSizeBytes>
86 explicit constexpr Deque(
88 : Deque(Aligned(buffer.data(), buffer.size())) {
89 static_assert(kAlignment >= alignof(value_type));
90 }
91
93 Deque(const Deque&) = delete;
94 Deque& operator=(const Deque&) = delete;
95
97 Deque(Deque&&) = delete;
98 Deque& operator=(Deque&&) = delete;
99
100 PW_CONSTEXPR_CPP20 ~Deque() { Base::DestroyAll(); }
101
102 constexpr size_type max_size() const noexcept { return Base::capacity(); }
103
104 private:
105 friend Base;
106
107 template <typename, typename>
108 friend class Queue;
109
110 template <typename, size_t, typename>
111 friend class FixedDeque;
112
113 static constexpr bool kFixedCapacity = true; // uses a fixed buffer
114
115 // Span that is known to be aligned as T.
116 class Aligned {
117 public:
118 // Construct from an UNALIGNED span. Checks alignment if necessary.
119 static constexpr Aligned Align(span<std::byte> unaligned_buffer) {
120 Aligned buffer(unaligned_buffer.data(), unaligned_buffer.size());
121
122 if constexpr (alignof(value_type) > alignof(std::byte)) {
123 void* data_void = buffer.data_;
124 buffer.data_ = static_cast<std::byte*>(std::align(
125 alignof(value_type), sizeof(value_type), data_void, buffer.size_));
126 if (buffer.data_ == nullptr) {
127 buffer.size_ = 0; // cannot fit any items
128 }
129 }
130
131 return buffer;
132 }
133
134 constexpr Aligned(std::byte* aligned_data, size_t size_bytes)
135 : data_(aligned_data), size_(size_bytes) {}
136
137 constexpr std::byte* data() const { return data_; }
138 constexpr size_t capacity() const { return size_ / sizeof(value_type); }
139
140 private:
141 std::byte* data_;
142 size_t size_;
143 };
144
145 explicit constexpr Deque(Aligned buffer) noexcept
146 : Base(static_cast<size_type>(buffer.capacity())),
147 buffer_(buffer.data()) {}
148
149 constexpr void MoveBufferFrom(Deque& other) noexcept {
150 Base::DestroyAll();
151 buffer_ = cpp20::exchange(other.buffer_, nullptr);
152 Base::MoveAssignIndices(other);
153 }
154
155 void MoveItemsFrom(Deque& other) {
156 this->assign(std::make_move_iterator(other.begin()),
157 std::make_move_iterator(other.end()));
158 other.clear();
159 }
160
161 void SwapBufferWith(Deque& other) noexcept {
162 Base::SwapIndices(other);
163 std::swap(buffer_, other.buffer_);
164 }
165
166 void SwapValuesWith(Deque& other);
167
168 pointer data() { return std::launder(reinterpret_cast<pointer>(buffer_)); }
169 const_pointer data() const {
170 return std::launder(reinterpret_cast<const_pointer>(buffer_));
171 }
172
173 // Pointer to the buffer used for the deque. This may differ from the pointer
174 // passed into the constructor if it had to be shifted for alignment.
175 std::byte* buffer_;
176};
177
188template <typename T,
189 size_t kInlineCapacity = containers::kAllocatedStorage,
190 typename SizeType = uint16_t>
191class FixedDeque final
192 : private containers::internal::ArrayStorage<T, kInlineCapacity>,
193 public Deque<T, SizeType> {
194 public:
197 constexpr FixedDeque()
198 : containers::internal::ArrayStorage<T, kInlineCapacity>{},
199 Deque<T, SizeType>(this->storage_array) {}
200
201 FixedDeque(FixedDeque&& other) noexcept : FixedDeque() {
202 this->MoveItemsFrom(other);
203 }
204
205 FixedDeque& operator=(FixedDeque&& other) noexcept {
206 this->clear();
207 this->MoveItemsFrom(other);
208 return *this;
209 }
210
213 template <size_t kOtherCapacity,
214 typename = std::enable_if_t<kOtherCapacity <= kInlineCapacity>>
215 FixedDeque(FixedDeque<T, kOtherCapacity>&& other) noexcept : FixedDeque() {
216 this->MoveItemsFrom(other);
217 }
218
219 template <size_t kOtherCapacity,
220 typename = std::enable_if_t<kOtherCapacity <= kInlineCapacity>>
221 FixedDeque& operator=(FixedDeque<T, kOtherCapacity>&& other) noexcept {
222 this->clear();
223 this->MoveItemsFrom(other);
224 return *this;
225 }
226
229 template <size_t kOtherCapacity>
231 this->SwapValuesWith(other);
232 }
233
234 private:
235 static_assert(kInlineCapacity <= std::numeric_limits<SizeType>::max(),
236 "The capacity is too large for the size; use a large SizeType");
237};
238
246template <typename T, typename SizeType>
247class FixedDeque<T, containers::kAllocatedStorage, SizeType> final
249 public Deque<T, SizeType> {
250 public:
254 static FixedDeque Allocate(Allocator& allocator, const SizeType capacity) {
255 FixedDeque deque = TryAllocate(allocator, capacity);
256 PW_ASSERT(deque.capacity() == capacity);
257 return deque;
258 }
259
263 static FixedDeque TryAllocate(Allocator& allocator, SizeType capacity) {
264 const allocator::Layout layout = allocator::Layout::Of<T[]>(capacity);
265 std::byte* array = static_cast<std::byte*>(allocator.Allocate(layout));
266 return FixedDeque(allocator, array, array != nullptr ? layout.size() : 0u);
267 }
268
272 explicit FixedDeque(UniquePtr<std::byte[]>&& storage) noexcept
273 : containers::internal::DynamicStorage{std::move(storage)},
275 span(storage_unique_ptr.get(), storage_unique_ptr.size())) {}
276
281 FixedDeque(FixedDeque&& other) noexcept
283 other.storage_unique_ptr)},
285 this->MoveBufferFrom(other);
286 }
287
288 FixedDeque& operator=(FixedDeque&& other) noexcept {
289 this->MoveBufferFrom(other);
290 return *this;
291 }
292
295 void swap(FixedDeque& other) noexcept {
296 this->SwapBufferWith(other);
297 std::swap(storage_unique_ptr, other.storage_unique_ptr);
298 }
299
303 template <size_t kInlineCapacity>
305 other.swap(*this); // Use the swap impl for statically allocated deques.
306 }
307
308 private:
309 FixedDeque(Allocator& allocator, std::byte* aligned_buffer, size_t size_bytes)
310 : containers::internal::DynamicStorage{UniquePtr<std::byte[]>(
311 aligned_buffer, size_bytes, allocator)},
312 Deque<T, SizeType>(typename Deque<T, SizeType>::Aligned(
313 storage_unique_ptr.get(), size_bytes)) {}
314};
315
316template <typename T, typename SizeType>
317void Deque<T, SizeType>::SwapValuesWith(Deque& other) {
318 Deque* smaller;
319 Deque* larger;
320
321 if (this->size() < other.size()) {
322 smaller = this;
323 larger = &other;
324 } else {
325 smaller = &other;
326 larger = this;
327 }
328
329 const SizeType smaller_size = smaller->size();
330 for (auto it =
331 std::swap_ranges(smaller->begin(), smaller->end(), larger->begin());
332 it != larger->end();
333 ++it) {
334 smaller->push_back(std::move(*it));
335 }
336
337 while (larger->size() > smaller_size) {
338 larger->pop_back();
339 }
340}
341
342} // namespace pw
Definition: allocator.h:36
void * Allocate(Layout layout)
Definition: allocator.h:44
Definition: deque.h:56
Definition: deque.h:193
Definition: unique_ptr.h:43
Definition: layout.h:58
Definition: storage.h:37
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
FixedDeque(UniquePtr< std::byte[]> &&storage) noexcept
Definition: deque.h:272
void swap(FixedDeque< T, kInlineCapacity, SizeType > &other)
Definition: deque.h:304
Deque(const Deque &)=delete
Copying is not supported since it can fail.
void swap(FixedDeque< T, kOtherCapacity, SizeType > &other)
Definition: deque.h:230
constexpr Deque(span< std::byte > buffer) noexcept
Definition: deque.h:80
constexpr Deque(containers::Storage< kAlignment, kSizeBytes > &buffer) noexcept
Definition: deque.h:86
MoveItemsFrom(other)
FixedDeque(FixedDeque &&other) noexcept
Definition: deque.h:281
static FixedDeque Allocate(Allocator &allocator, const SizeType capacity)
Definition: deque.h:254
constexpr FixedDeque()
Definition: deque.h:197
void swap(FixedDeque &other) noexcept
Definition: deque.h:295
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
static FixedDeque TryAllocate(Allocator &allocator, SizeType capacity)
Definition: deque.h:263
constexpr size_t kAllocatedStorage
Definition: storage.h:78
#define PW_CONSTEXPR_CPP20
Definition: language_feature_macros.h:27
The Pigweed namespace.
Definition: alignment.h:27