Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
generic_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 <initializer_list>
19#include <iterator>
20#include <limits>
21#include <memory>
22#include <new>
23#include <optional>
24#include <type_traits>
25#include <utility>
26
27#include "pw_assert/assert.h"
28#include "pw_containers/internal/deque_iterator.h"
29#include "pw_containers/internal/traits.h"
30#include "pw_numeric/checked_arithmetic.h"
31#include "pw_span/span.h"
32
33namespace pw::containers::internal {
34
35template <typename T>
36using EnableIfIterable =
37 std::enable_if_t<true, decltype(T().begin(), T().end())>;
38
48template <typename CountAndCapacityType>
50 public:
51 using size_type = typename CountAndCapacityType::size_type;
52
53 // Size
54
55 [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
56
58 constexpr size_type size() const noexcept {
59 return count_and_capacity_.count();
60 }
61
63 constexpr size_type capacity() const noexcept {
64 return count_and_capacity_.capacity();
65 }
66
67 protected:
68 // Functions used by deque implementations
69
70 CountAndCapacityType& count_and_capacity() noexcept {
71 return count_and_capacity_;
72 }
73
74 constexpr void MoveAssignIndices(GenericDequeBase& other) noexcept {
75 count_and_capacity_ = std::move(other.count_and_capacity_);
76 head_ = std::exchange(other.head_, 0);
77 tail_ = std::exchange(other.tail_, 0);
78 }
79
80 void SwapIndices(GenericDequeBase& other) noexcept {
81 std::swap(count_and_capacity_, other.count_and_capacity_);
82 std::swap(head_, other.head_);
83 std::swap(tail_, other.tail_);
84 }
85
86 // Returns if buffer can be resized larger without moving any items. Can
87 // extend if not wrapped or if tail_ wrapped but no elements were added.
88 bool CanExtendBuffer() const { return tail_ > head_ || tail_ == 0; }
89
90 // Can only shrink if there are no empty slots at the start of the buffer.
91 bool CanShrinkBuffer() const { return head_ == 0; }
92
93 void HandleNewBuffer(size_type new_capacity) {
94 count_and_capacity_.SetCapacity(new_capacity);
95 head_ = 0;
96 tail_ = size() == new_capacity
97 ? 0
98 : count_and_capacity_.count(); // handle full buffers
99 }
101 void HandleExtendedBuffer(size_type new_capacity) {
102 count_and_capacity_.SetCapacity(new_capacity);
103 if (tail_ == 0) { // "unwrap" the tail if needed
104 tail_ = head_ + count_and_capacity_.count();
105 }
106 }
107
108 void HandleShrunkBuffer(size_type new_capacity) {
109 count_and_capacity_.SetCapacity(new_capacity);
110 if (tail_ == new_capacity) { // wrap the tail if needed
111 tail_ = 0;
112 }
113 }
114
115 private:
116 // Functions needed by GenericDeque only
117 template <typename Derived, typename ValueType, typename S>
118 friend class GenericDeque;
119
120 explicit constexpr GenericDequeBase(size_type initial_capacity) noexcept
121 : count_and_capacity_(initial_capacity), head_(0), tail_(0) {}
122
123 constexpr void ClearIndices() {
124 count_and_capacity_.SetCount(0);
125 head_ = tail_ = 0;
126 }
127
128 // Returns the absolute index based on the relative index beyond the
129 // head offset.
130 //
131 // Precondition: The relative index must be valid, i.e. < size().
132 constexpr size_type AbsoluteIndex(const size_type relative_index) const {
133 const size_type absolute_index = head_ + relative_index;
134 if (absolute_index < capacity()) {
135 return absolute_index;
136 }
137 // Offset wrapped across the end of the circular buffer.
138 return absolute_index - capacity();
139 }
140
141 constexpr size_type AbsoluteIndexChecked(
142 const size_type relative_index) const {
143 PW_ASSERT(relative_index < size());
144 return AbsoluteIndex(relative_index);
145 }
146
147 constexpr void PushBack(size_type count = 1) {
148 IncrementWithWrap(tail_, count);
149 count_and_capacity_.IncrementCount(count);
150 }
151 constexpr void PushFront(size_type count = 1) {
152 DecrementWithWrap(head_, count);
153 count_and_capacity_.IncrementCount(count);
154 }
155 constexpr void PopFront() {
156 IncrementWithWrap(head_, 1);
157 count_and_capacity_.DecrementCount();
158 }
159 constexpr void PopBack() {
160 DecrementWithWrap(tail_, 1);
161 count_and_capacity_.DecrementCount();
162 }
163
164 constexpr void IncrementWithWrap(size_type& index, size_type count) const {
165 index += count;
166 // Note: branch is faster than mod (%) on common embedded architectures.
167 if (index >= capacity()) {
168 index -= capacity();
169 }
170 }
171
172 constexpr void DecrementWithWrap(size_type& index, size_type count) const {
173 if (index < count) {
174 index += capacity();
175 }
176 index -= count;
177 }
178
179 CountAndCapacityType count_and_capacity_;
180 size_type head_; // Inclusive offset for the front.
181 size_type tail_; // Non-inclusive offset for the back.
182};
183
188template <typename Derived, typename ValueType, typename CountAndCapacityType>
189class GenericDeque : public GenericDequeBase<CountAndCapacityType> {
190 private:
192
193 public:
194 using value_type = ValueType;
195 using size_type = typename CountAndCapacityType::size_type;
196 using difference_type = ptrdiff_t;
197 using reference = value_type&;
198 using const_reference = const value_type&;
199 using pointer = value_type*;
200 using const_pointer = const value_type*;
201 using iterator = containers::internal::DequeIterator<Derived>;
202 using const_iterator = containers::internal::DequeIterator<const Derived>;
203
204 // Copy/assign are implemented in derived classes.
205 GenericDeque(const GenericDeque&) = delete;
206 GenericDeque(GenericDeque&&) = delete;
207
208 GenericDeque& operator=(const GenericDeque&) = delete;
209 GenericDeque&& operator=(GenericDeque&&) = delete;
210
211 // Size
212
214 using Base::empty;
216
217 // Infallible assign
218
220 void assign(size_type count, const value_type& value) {
221 PW_ASSERT(try_assign(count, value));
222 }
223
226 template <typename It,
227 int&...,
228 typename = containers::internal::EnableIfInputIterator<It>>
229 void assign(It start, It finish);
230
232 void assign(const std::initializer_list<value_type>& list) {
233 assign(list.begin(), list.end());
234 }
235
236 // Access
237
238 constexpr reference at(size_type index) {
239 return data()[Base::AbsoluteIndexChecked(index)];
240 }
241 constexpr const_reference at(size_type index) const {
242 return data()[Base::AbsoluteIndexChecked(index)];
243 }
244
245 constexpr reference operator[](size_type index) {
246 PW_DASSERT(index < size());
247 return data()[Base::AbsoluteIndex(index)];
248 }
249 constexpr const_reference operator[](size_type index) const {
250 PW_DASSERT(index < size());
251 return data()[Base::AbsoluteIndex(index)];
252 }
253
254 constexpr reference front() {
255 PW_DASSERT(!empty());
256 return data()[head()];
257 }
258 constexpr const_reference front() const {
259 PW_DASSERT(!empty());
260 return data()[head()];
261 }
262
263 constexpr reference back() {
264 PW_DASSERT(!empty());
265 return data()[Base::AbsoluteIndex(size() - 1)];
266 }
267 constexpr const_reference back() const {
268 PW_DASSERT(!empty());
269 return data()[Base::AbsoluteIndex(size() - 1)];
270 }
271
273 constexpr std::pair<span<const value_type>, span<const value_type>>
275 constexpr std::pair<span<value_type>, span<value_type>> contiguous_data() {
276 auto [first, second] = std::as_const(*this).contiguous_data();
277 return {{const_cast<pointer>(first.data()), first.size()},
278 {const_cast<pointer>(second.data()), second.size()}};
279 }
280
281 // Iterate
282
283 constexpr iterator begin() noexcept {
284 if (empty()) {
285 return end();
286 }
287
288 return iterator(derived(), 0);
289 }
290 constexpr const_iterator begin() const noexcept { return cbegin(); }
291 constexpr const_iterator cbegin() const noexcept {
292 if (empty()) {
293 return cend();
294 }
295 return const_iterator(derived(), 0);
296 }
297
298 constexpr iterator end() noexcept { return iterator(derived(), size()); }
299 constexpr const_iterator end() const noexcept { return cend(); }
300 constexpr const_iterator cend() const noexcept {
301 return const_iterator(derived(), size());
302 }
303
304 // Infallible modify
305
306 void clear() {
307 if constexpr (!std::is_trivially_destructible_v<value_type>) {
308 std::destroy(begin(), end());
309 }
310 Base::ClearIndices();
311 }
312
314 iterator erase(const_iterator pos) {
315 PW_DASSERT(pos.pos_ < size());
316 return erase(pos, pos + 1);
317 }
318
321 iterator erase(const_iterator first, const_iterator last);
322
323 void push_back(const value_type& value) { PW_ASSERT(try_push_back(value)); }
324
325 void push_back(value_type&& value) {
326 PW_ASSERT(try_push_back(std::move(value)));
327 }
328
329 template <typename... Args>
330 void emplace_back(Args&&... args) {
331 PW_ASSERT(try_emplace_back(std::forward<Args>(args)...));
332 }
333
334 void pop_back();
335
336 void push_front(const value_type& value) { PW_ASSERT(try_push_front(value)); }
337
338 void push_front(value_type&& value) {
339 PW_ASSERT(try_push_front(std::move(value)));
340 }
341
342 template <typename... Args>
343 void emplace_front(Args&&... args) {
344 PW_ASSERT(try_emplace_front(std::forward<Args>(args)...));
345 }
346
347 void pop_front();
348
353 template <typename... Args>
354 iterator emplace(const_iterator pos, Args&&... args) {
355 std::optional<iterator> result =
356 try_emplace(pos, std::forward<Args>(args)...);
357 PW_ASSERT(result.has_value());
358 return *result;
359 }
360
364 iterator insert(const_iterator pos, const value_type& value) {
365 std::optional<iterator> result = try_insert(pos, value);
366 PW_ASSERT(result.has_value());
367 return *result;
368 }
369
374 iterator insert(const_iterator pos, value_type&& value) {
375 std::optional<iterator> result = try_insert(pos, std::move(value));
376 PW_ASSERT(result.has_value());
377 return *result;
378 }
379
384 iterator insert(const_iterator pos,
385 size_type count,
386 const value_type& value) {
387 std::optional<iterator> result = try_insert(pos, count, value);
388 PW_ASSERT(result.has_value());
389 return *result;
390 }
391
396 template <typename InputIt,
397 typename = containers::internal::EnableIfInputIterator<InputIt>>
398 iterator insert(const_iterator pos, InputIt first, InputIt last);
399
403 iterator insert(const_iterator pos, std::initializer_list<value_type> ilist) {
404 std::optional<iterator> result = try_insert(pos, ilist);
405 PW_ASSERT(result.has_value());
406 return *result;
407 }
408
409 void resize(size_type new_size) { resize(new_size, value_type()); }
410
411 void resize(size_type new_size, const value_type& value) {
412 PW_ASSERT(try_resize(new_size, value));
413 }
414
415 protected:
416 explicit constexpr GenericDeque(size_type initial_capacity) noexcept
417 : GenericDequeBase<CountAndCapacityType>(initial_capacity) {}
418
419 // Infallible assignment operators
420
421 // NOLINTBEGIN(misc-unconventional-assign-operator);
422 Derived& operator=(const std::initializer_list<value_type>& list) {
423 assign(list);
424 return derived();
425 }
426
427 template <typename T, typename = containers::internal::EnableIfIterable<T>>
428 Derived& operator=(const T& other) {
429 assign(other.begin(), other.end());
430 return derived();
431 }
432 // NOLINTEND(misc-unconventional-assign-operator);
433
434 // Fallible assign
435
439 [[nodiscard]] bool try_assign(size_type count, const value_type& value);
440
447 template <typename It,
448 int&...,
449 typename = containers::internal::EnableIfForwardIterator<It>>
450 [[nodiscard]] bool try_assign(It start, It finish);
451
454 [[nodiscard]] bool try_assign(const std::initializer_list<value_type>& list) {
455 return try_assign(list.begin(), list.end());
456 }
457
458 // Fallible modify
459
465 template <typename... Args>
466 [[nodiscard]] std::optional<iterator> try_emplace(const_iterator pos,
467 Args&&... args);
468
474 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
475 const value_type& value) {
476 return try_emplace(pos, value);
477 }
478
484 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
485 value_type&& value) {
486 return try_emplace(pos, std::move(value));
487 }
488
494 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
495 size_type count,
496 const value_type& value);
497
503 template <typename ForwardIt,
504 typename = containers::internal::EnableIfForwardIterator<ForwardIt>>
505 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
506 ForwardIt first,
507 ForwardIt last);
508
514 [[nodiscard]] std::optional<iterator> try_insert(
515 const_iterator pos, std::initializer_list<value_type> ilist) {
516 return try_insert(pos, ilist.begin(), ilist.end());
517 }
518
519 [[nodiscard]] bool try_push_back(const value_type& value) {
520 return try_emplace_back(value);
521 }
522
523 [[nodiscard]] bool try_push_back(value_type&& value) {
524 return try_emplace_back(std::move(value));
525 }
526
527 template <typename... Args>
528 [[nodiscard]] bool try_emplace_back(Args&&... args);
529
530 [[nodiscard]] bool try_push_front(const value_type& value) {
531 return try_emplace_front(value);
532 }
533
534 [[nodiscard]] bool try_push_front(value_type&& value) {
535 return try_emplace_front(std::move(value));
536 }
537
538 template <typename... Args>
539 [[nodiscard]] bool try_emplace_front(Args&&... args);
540
541 [[nodiscard]] bool try_resize(size_type new_size) {
542 return try_resize(new_size, value_type());
543 }
544
545 [[nodiscard]] bool try_resize(size_type new_size, const value_type& value);
546
547 private:
548 constexpr Derived& derived() { return static_cast<Derived&>(*this); }
549 constexpr const Derived& derived() const {
550 return static_cast<const Derived&>(*this);
551 }
552
553 constexpr size_type head() const { return Base::head_; }
554 constexpr size_type tail() const { return Base::tail_; }
555
556 // Accessed the underlying array in the derived class.
557 constexpr pointer data() { return derived().data(); }
558 constexpr const_pointer data() const { return derived().data(); }
559
560 // Creates an uninitialized slot of `new_items` at `insert_index`.
561 bool ShiftForInsert(size_type insert_index, size_type new_items);
562
563 // Called by ShiftForInsert depending on where the insert happens.
564 void ShiftLeft(size_type insert_index, size_type new_items);
565 void ShiftRight(size_type insert_index, size_type new_items);
566
567 // Make sure the container can hold count additional items.
568 bool CheckCapacityAdd(size_type count) {
569 size_type new_size;
570 return CheckedAdd(size(), count, new_size) && CheckCapacity(new_size);
571 }
572
573 // Ensures that the container can hold at least this many elements.
574 constexpr bool CheckCapacity(size_type new_size) {
575 if constexpr (Derived::kFixedCapacity) {
576 return new_size <= capacity();
577 } else {
578 return derived().try_reserve(new_size);
579 }
580 }
581
582 // Appends items without checking the capacity. Capacity MUST be large enough.
583 template <typename... Args>
584 void EmplaceBackUnchecked(Args&&... args) {
585 new (&data()[tail()]) value_type(std::forward<Args>(args)...);
586 Base::PushBack();
587 }
588};
589
590// Function implementations
591
592template <typename Derived, typename ValueType, typename CountAndCapacityType>
593template <typename It, int&..., typename>
595 It finish) {
596 // Can't safely check std::distance for InputIterator, so use push_back().
597 if constexpr (Derived::kFixedCapacity ||
598 std::is_same_v<
599 typename std::iterator_traits<It>::iterator_category,
600 std::input_iterator_tag>) {
601 clear();
602 while (start != finish) {
603 push_back(*start);
604 ++start;
605 }
606 } else {
607 PW_ASSERT(try_assign(start, finish));
608 }
609}
610
611template <typename Derived, typename ValueType, typename CountAndCapacityType>
613 size_type count, const value_type& value) {
614 if (!CheckCapacity(count)) {
615 return false;
616 }
617 clear();
618 Base::PushBack(count);
619 std::uninitialized_fill_n(data(), count, value);
620 return true;
621}
622
623template <typename Derived, typename ValueType, typename CountAndCapacityType>
624template <typename It, int&..., typename>
626 It start, It finish) {
627 // Requires at least a forward iterator to safely check std::distance.
628 static_assert(std::is_convertible_v<
629 typename std::iterator_traits<It>::iterator_category,
630 std::forward_iterator_tag>);
631 const auto items = std::distance(start, finish);
632 PW_DASSERT(items >= 0);
633 if (static_cast<std::make_unsigned_t<decltype(items)>>(items) >
634 std::numeric_limits<size_type>::max()) {
635 return false;
636 }
637 const size_type count = static_cast<size_type>(items);
638 if (!CheckCapacity(count)) {
639 return false;
640 }
641
642 clear();
643 Base::PushBack(count);
644 std::uninitialized_move(start, finish, data());
645 return true;
646}
647
648template <typename Derived, typename ValueType, typename CountAndCapacityType>
649constexpr std::pair<span<const ValueType>, span<const ValueType>>
651 const {
652 if (empty()) {
653 return {span<const value_type>(), span<const value_type>()};
654 }
655 if (tail() > head()) {
656 // If the newest entry is after the oldest entry, we have not wrapped:
657 // [ |head()|...more_entries...|tail()| ]
658 return {span<const value_type>(&data()[head()], size()),
659 span<const value_type>()};
660 } else {
661 // If the newest entry is before or at the oldest entry and we know we are
662 // not empty, ergo we have wrapped:
663 // [..more_entries...|tail()| |head()|...more_entries...]
664 return {span<const value_type>(&data()[head()], capacity() - head()),
665 span<const value_type>(&data()[0], tail())};
666 }
667}
668
669template <typename Derived, typename ValueType, typename CountAndCapacityType>
670template <typename... Args>
672 Args&&... args) {
673 if (!CheckCapacityAdd(1)) {
674 return false;
675 }
676 EmplaceBackUnchecked(std::forward<Args>(args)...);
677 return true;
678}
679
680template <typename Derived, typename ValueType, typename CountAndCapacityType>
681void GenericDeque<Derived, ValueType, CountAndCapacityType>::pop_back() {
682 PW_ASSERT(!empty());
683 if constexpr (!std::is_trivially_destructible_v<value_type>) {
684 std::destroy_at(&back());
685 }
686 Base::PopBack();
687}
688
689template <typename Derived, typename ValueType, typename CountAndCapacityType>
690template <typename... Args>
691bool GenericDeque<Derived, ValueType, CountAndCapacityType>::try_emplace_front(
692 Args&&... args) {
693 if (!CheckCapacityAdd(1)) {
694 return false;
695 }
696 Base::PushFront();
697 new (&data()[head()]) value_type(std::forward<Args>(args)...);
698 return true;
699}
700
701template <typename Derived, typename ValueType, typename CountAndCapacityType>
702void GenericDeque<Derived, ValueType, CountAndCapacityType>::pop_front() {
703 PW_ASSERT(!empty());
704 if constexpr (!std::is_trivially_destructible_v<value_type>) {
705 std::destroy_at(&front());
706 }
707 Base::PopFront();
708}
709
710template <typename Derived, typename ValueType, typename CountAndCapacityType>
711bool GenericDeque<Derived, ValueType, CountAndCapacityType>::try_resize(
712 size_type new_size, const value_type& value) {
713 if (size() < new_size) {
714 if (!CheckCapacity(new_size)) {
715 return false;
716 }
717 const size_type new_items = new_size - size();
718 for (size_type i = 0; i < new_items; ++i) {
719 EmplaceBackUnchecked(value);
720 }
721 } else {
722 while (size() > new_size) {
723 pop_back();
724 }
725 }
726 return true;
727}
728
729template <typename Derived, typename ValueType, typename CountAndCapacityType>
730typename GenericDeque<Derived, ValueType, CountAndCapacityType>::iterator
732 const_iterator first, const_iterator last) {
733 PW_DASSERT(first <= last);
734 const iterator first_it(derived(), first.pos_);
735 const iterator last_it(derived(), last.pos_);
736
737 const size_type items_to_erase = static_cast<size_type>(last - first);
738 if (items_to_erase == 0) {
739 return first_it;
740 }
741
742 const size_type items_after = static_cast<size_type>(size() - last.pos_);
743 // Shift head forward or tail backwards -- whichever involves fewer moves.
744 if (first.pos_ < items_after) { // fewer before than after
745 std::move_backward(begin(), first_it, last_it);
746 if constexpr (!std::is_trivially_destructible_v<value_type>) {
747 std::destroy(begin(), begin() + items_to_erase);
748 }
749 Base::head_ = Base::AbsoluteIndex(items_to_erase);
750 } else { // fewer after than before
751 std::move(last_it, end(), first_it);
752 if constexpr (!std::is_trivially_destructible_v<value_type>) {
753 std::destroy(first_it + items_after, end());
754 }
755 Base::tail_ = Base::AbsoluteIndex(first.pos_ + items_after);
756 }
757 Base::count_and_capacity().SetCount(size() - items_to_erase);
758 return first_it;
759}
760
761template <typename Derived, typename ValueType, typename CountAndCapacityType>
762template <typename... Args>
763std::optional<
764 typename GenericDeque<Derived, ValueType, CountAndCapacityType>::iterator>
766 const_iterator pos, Args&&... args) {
767 // Insert in the middle of the deque, shifting other items out of the way.
768 if (!ShiftForInsert(pos.pos_, 1)) {
769 return std::nullopt;
770 }
771 iterator it(derived(), pos.pos_);
772 new (std::addressof(*it)) value_type(std::forward<Args>(args)...);
773 return it;
774}
775
776template <typename Derived, typename ValueType, typename CountAndCapacityType>
777bool GenericDeque<Derived, ValueType, CountAndCapacityType>::ShiftForInsert(
778 const size_type insert_index, size_type new_items) {
779 if (!CheckCapacityAdd(new_items)) {
780 return false;
781 }
782
783 if (insert_index < size() / 2) { // Fewer items before than after.
784 ShiftLeft(insert_index, new_items);
785 } else {
786 ShiftRight(insert_index, new_items);
787 }
788 return true;
789}
790
791template <typename Derived, typename ValueType, typename CountAndCapacityType>
792void GenericDeque<Derived, ValueType, CountAndCapacityType>::ShiftLeft(
793 const size_type insert_index, size_type new_items) {
794 Base::PushFront(new_items);
795 iterator original_begin(derived(), new_items);
796
797 const size_type move_to_new_slots = std::min(new_items, insert_index);
798 auto [next_src, next_dst] =
799 std::uninitialized_move_n(original_begin, move_to_new_slots, begin());
800
801 const size_type move_to_existing_slots = insert_index - move_to_new_slots;
802 std::move(next_src, next_src + move_to_existing_slots, next_dst);
803
804 // For consistency, ensure the whole opening is uninitialized.
805 // TODO: b/429231491 - Consider having callers handle moved vs uninitialized
806 // slots or doing the insert in this function.
807 if constexpr (!std::is_trivially_destructible_v<value_type>) {
808 std::destroy(
809 iterator(derived(), std::max(insert_index, original_begin.pos_)),
810 iterator(derived(), insert_index + new_items));
811 }
812}
813
814template <typename Derived, typename ValueType, typename CountAndCapacityType>
815void GenericDeque<Derived, ValueType, CountAndCapacityType>::ShiftRight(
816 const size_type insert_index, size_type new_items) {
817 const size_type items_after = size() - insert_index;
818 iterator original_end = end();
819 Base::PushBack(new_items);
820
821 const size_type move_to_new_slots = std::min(new_items, items_after);
822 std::uninitialized_move(original_end - move_to_new_slots,
823 original_end,
824 end() - move_to_new_slots);
825
826 const size_type move_to_existing_slots = items_after - move_to_new_slots;
827 iterator pos(derived(), insert_index);
828 std::move_backward(pos, pos + move_to_existing_slots, original_end);
829
830 // For consistency, ensure the whole opening is uninitialized.
831 // TODO: b/429231491 - Consider having callers handle moved vs uninitialized
832 // slots or doing the insert in this function.
833 if constexpr (!std::is_trivially_destructible_v<value_type>) {
834 std::destroy(
835 iterator(derived(), insert_index),
836 iterator(derived(),
837 std::min(static_cast<size_type>(insert_index + new_items),
838 original_end.pos_)));
839 }
840}
841
842template <typename Derived, typename ValueType, typename CountAndCapacityType>
843std::optional<
844 typename GenericDeque<Derived, ValueType, CountAndCapacityType>::iterator>
846 const_iterator pos, size_type count, const value_type& value) {
847 if (count == 0) {
848 return iterator(derived(), pos.pos_);
849 }
850 if (!ShiftForInsert(pos.pos_, count)) {
851 return std::nullopt;
852 }
853
854 iterator it(derived(), pos.pos_);
855 std::uninitialized_fill_n(it, count, value);
856 return it;
857}
858
859template <typename Derived, typename ValueType, typename CountAndCapacityType>
860template <typename InputIt, typename>
861typename GenericDeque<Derived, ValueType, CountAndCapacityType>::iterator
863 const_iterator pos, InputIt first, InputIt last) {
864 // Can't safely check std::distance for InputIterator, so repeatedly emplace()
865 if constexpr (std::is_same_v<
866 typename std::iterator_traits<InputIt>::iterator_category,
867 std::input_iterator_tag>) {
868 // Inserting M items into an N-item deque is O(N*M) due to repeated
869 // emplace() calls. DynamicDeque reads into a temporary deque instead.
870 iterator insert_pos = iterator(derived(), pos.pos_);
871 while (first != last) {
872 insert_pos = emplace(insert_pos, *first);
873 ++first;
874 ++insert_pos;
875 }
876 return iterator(derived(), pos.pos_);
877 } else {
878 std::optional<iterator> result = try_insert(pos, first, last);
879 PW_ASSERT(result.has_value());
880 return *result;
881 }
882}
883
884template <typename Derived, typename ValueType, typename CountAndCapacityType>
885template <typename ForwardIt, typename>
886std::optional<
887 typename GenericDeque<Derived, ValueType, CountAndCapacityType>::iterator>
889 const_iterator pos, ForwardIt first, ForwardIt last) {
890 static_assert(std::is_convertible_v<
891 typename std::iterator_traits<ForwardIt>::iterator_category,
892 std::forward_iterator_tag>);
893 const auto distance = std::distance(first, last);
894 PW_DASSERT(distance >= 0);
895 const size_type count = static_cast<size_type>(distance);
896
897 const iterator it(derived(), pos.pos_);
898 if (count == 0) {
899 return it;
900 }
901 if (!ShiftForInsert(pos.pos_, count)) {
902 return std::nullopt;
903 }
904 std::uninitialized_move(first, last, it);
905 return it;
906}
907
908} // namespace pw::containers::internal
Definition: generic_deque.h:49
constexpr size_type size() const noexcept
Returns the number of elements in the deque.
Definition: generic_deque.h:58
constexpr size_type capacity() const noexcept
Returns the maximum number of elements in the deque.
Definition: generic_deque.h:63
Definition: generic_deque.h:189
constexpr size_type size() const noexcept
Returns the number of elements in the deque.
Definition: generic_deque.h:58
iterator insert(const_iterator pos, size_type count, const value_type &value)
Definition: generic_deque.h:384
constexpr std::pair< span< const value_type >, span< const value_type > > contiguous_data() const
Provides access to the valid data in a contiguous form.
Definition: generic_deque.h:650
std::optional< iterator > try_insert(const_iterator pos, std::initializer_list< value_type > ilist)
Definition: generic_deque.h:514
bool try_assign(size_type count, const value_type &value)
Definition: generic_deque.h:612
void assign(const std::initializer_list< value_type > &list)
Sets contents to copies of the items from the list. Crashes if cannot fit.
Definition: generic_deque.h:232
iterator insert(const_iterator pos, const value_type &value)
Definition: generic_deque.h:364
void assign(It start, It finish)
Definition: generic_deque.h:594
iterator erase(const_iterator first, const_iterator last)
Definition: generic_deque.h:731
std::optional< iterator > try_emplace(const_iterator pos, Args &&... args)
bool try_assign(const std::initializer_list< value_type > &list)
Definition: generic_deque.h:454
iterator emplace(const_iterator pos, Args &&... args)
Definition: generic_deque.h:354
iterator erase(const_iterator pos)
Erases the item at pos, which must be a dereferenceable iterator.
Definition: generic_deque.h:314
iterator insert(const_iterator pos, InputIt first, InputIt last)
Definition: generic_deque.h:862
constexpr size_type capacity() const noexcept
Returns the maximum number of elements in the deque.
Definition: generic_deque.h:63
std::optional< iterator > try_insert(const_iterator pos, const value_type &value)
Definition: generic_deque.h:474
std::optional< iterator > try_insert(const_iterator pos, value_type &&value)
Definition: generic_deque.h:484
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:220
iterator insert(const_iterator pos, value_type &&value)
Definition: generic_deque.h:374
std::optional< iterator > try_insert(const_iterator pos, ForwardIt first, ForwardIt last)
iterator insert(const_iterator pos, std::initializer_list< value_type > ilist)
Definition: generic_deque.h:403
constexpr bool CheckedAdd(A a, B b, T &result)
Definition: checked_arithmetic.h:42