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