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