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