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