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;
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 constexpr void PushBackOverwrite() {
167 IncrementWithWrap(tail_, size_type(1), capacity());
168 if (count_ < capacity()) {
169 count_++;
170 } else {
171 head_ = tail_;
172 }
173 }
174
175 constexpr void PushFrontOverwrite() {
176 DecrementWithWrap(head_, size_type(1), capacity());
177 if (count_ < capacity()) {
178 count_++;
179 } else {
180 tail_ = head_;
181 }
182 }
183
184 size_type capacity_;
185 size_type count_;
186 size_type head_; // Inclusive offset for the front.
187 size_type tail_; // Non-inclusive offset for the back.
188};
189
194template <typename Derived, typename ValueType, typename SizeType>
195class GenericDeque : public GenericDequeBase<SizeType> {
196 private:
198
199 public:
200 using value_type = ValueType;
201 using size_type = SizeType;
202 using difference_type = ptrdiff_t;
203 using reference = value_type&;
204 using const_reference = const value_type&;
205 using pointer = value_type*;
206 using const_pointer = const value_type*;
207 using iterator = containers::internal::DequeIterator<Derived>;
208 using const_iterator = containers::internal::DequeIterator<const Derived>;
209
210 // Copy/assign are implemented in derived classes.
211 GenericDeque(const GenericDeque&) = delete;
212 GenericDeque(GenericDeque&&) = delete;
213
214 GenericDeque& operator=(const GenericDeque&) = delete;
215 GenericDeque&& operator=(GenericDeque&&) = delete;
216
217 // Size
218
220 using Base::empty;
222
223 // Infallible assign
224
226 void assign(size_type count, const value_type& value) {
227 PW_ASSERT(try_assign(count, value));
228 }
229
232 template <typename It,
233 int&...,
234 typename = containers::internal::EnableIfInputIterator<It>>
235 void assign(It start, It finish);
236
238 void assign(const std::initializer_list<value_type>& list) {
239 assign(list.begin(), list.end());
240 }
241
242 // Access
243
244 constexpr reference at(size_type index) {
245 return data()[Base::AbsoluteIndexChecked(index)];
246 }
247 constexpr const_reference at(size_type index) const {
248 return data()[Base::AbsoluteIndexChecked(index)];
249 }
250
251 constexpr reference operator[](size_type index) {
252 PW_DASSERT(index < size());
253 return data()[Base::AbsoluteIndex(index)];
254 }
255 constexpr const_reference operator[](size_type index) const {
256 PW_DASSERT(index < size());
257 return data()[Base::AbsoluteIndex(index)];
258 }
259
260 constexpr reference front() {
261 PW_DASSERT(!empty());
262 return data()[head()];
263 }
264 constexpr const_reference front() const {
265 PW_DASSERT(!empty());
266 return data()[head()];
267 }
268
269 constexpr reference back() {
270 PW_DASSERT(!empty());
271 return data()[Base::AbsoluteIndex(size() - 1)];
272 }
273 constexpr const_reference back() const {
274 PW_DASSERT(!empty());
275 return data()[Base::AbsoluteIndex(size() - 1)];
276 }
277
279 constexpr std::pair<span<const value_type>, span<const value_type>>
281 constexpr std::pair<span<value_type>, span<value_type>> contiguous_data() {
282 auto [first, second] = std::as_const(*this).contiguous_data();
283 return {{const_cast<pointer>(first.data()), first.size()},
284 {const_cast<pointer>(second.data()), second.size()}};
285 }
286
287 // Iterate
288
289 constexpr iterator begin() noexcept {
290 if (empty()) {
291 return end();
292 }
293
294 return iterator(derived(), 0);
295 }
296 constexpr const_iterator begin() const noexcept { return cbegin(); }
297 constexpr const_iterator cbegin() const noexcept {
298 if (empty()) {
299 return cend();
300 }
301 return const_iterator(derived(), 0);
302 }
303
304 constexpr iterator end() noexcept { return iterator(derived(), size()); }
305 constexpr const_iterator end() const noexcept { return cend(); }
306 constexpr const_iterator cend() const noexcept {
307 return const_iterator(derived(), size());
308 }
309
310 // Infallible modify
311
312 constexpr void clear() {
313 DestroyAll();
314 Base::ClearIndices();
315 }
316
318 iterator erase(const_iterator pos) {
319 PW_DASSERT(pos.pos_ < size());
320 return erase(pos, pos + 1);
321 }
322
325 iterator erase(const_iterator first, const_iterator last);
326
327 void push_back(const value_type& value) { PW_ASSERT(try_push_back(value)); }
328
329 void push_back(value_type&& value) {
330 PW_ASSERT(try_push_back(std::move(value)));
331 }
332
333 template <typename... Args>
334 void emplace_back(Args&&... args) {
335 PW_ASSERT(try_emplace_back(std::forward<Args>(args)...));
336 }
337
340 void push_back_overwrite(const value_type& value) {
342 }
343
346 void push_back_overwrite(value_type&& value) {
347 emplace_back_overwrite(std::move(value));
348 }
349
352 template <typename... Args>
353 void emplace_back_overwrite(Args&&... args) {
354 PW_ASSERT(Base::capacity() > 0);
355
356 if constexpr (!std::is_trivially_destructible_v<value_type>) {
357 if (Base::full()) {
358 std::destroy_at(&front());
359 }
360 }
361
362 new (&data()[Base::tail_]) value_type(std::forward<Args>(args)...);
363 Base::PushBackOverwrite();
364 }
365
366 void pop_back();
367
368 void push_front(const value_type& value) { PW_ASSERT(try_push_front(value)); }
369
370 void push_front(value_type&& value) {
371 PW_ASSERT(try_push_front(std::move(value)));
372 }
373
374 template <typename... Args>
375 void emplace_front(Args&&... args) {
376 PW_ASSERT(try_emplace_front(std::forward<Args>(args)...));
377 }
378
379 void pop_front();
380
383 void push_front_overwrite(const value_type& value) {
385 }
386
389 void push_front_overwrite(value_type&& value) {
390 emplace_front_overwrite(std::move(value));
391 }
392
395 template <typename... Args>
396 void emplace_front_overwrite(Args&&... args) {
397 PW_ASSERT(Base::capacity() > 0);
398
399 if constexpr (!std::is_trivially_destructible_v<value_type>) {
400 if (Base::full()) {
401 std::destroy_at(&back());
402 }
403 }
404
405 Base::PushFrontOverwrite();
406 new (&data()[Base::head_]) value_type(std::forward<Args>(args)...);
407 }
408
413 template <typename... Args>
414 iterator emplace(const_iterator pos, Args&&... args) {
415 std::optional<iterator> result =
416 try_emplace(pos, std::forward<Args>(args)...);
417 PW_ASSERT(result.has_value());
418 return *result;
419 }
420
424 iterator insert(const_iterator pos, const value_type& value) {
425 std::optional<iterator> result = try_insert(pos, value);
426 PW_ASSERT(result.has_value());
427 return *result;
428 }
429
434 iterator insert(const_iterator pos, value_type&& value) {
435 std::optional<iterator> result = try_insert(pos, std::move(value));
436 PW_ASSERT(result.has_value());
437 return *result;
438 }
439
444 iterator insert(const_iterator pos,
445 size_type count,
446 const value_type& value) {
447 std::optional<iterator> result = try_insert(pos, count, value);
448 PW_ASSERT(result.has_value());
449 return *result;
450 }
451
456 template <typename InputIt,
457 typename = containers::internal::EnableIfInputIterator<InputIt>>
458 iterator insert(const_iterator pos, InputIt first, InputIt last);
459
463 iterator insert(const_iterator pos, std::initializer_list<value_type> ilist) {
464 std::optional<iterator> result = try_insert(pos, ilist);
465 PW_ASSERT(result.has_value());
466 return *result;
467 }
468
469 void resize(size_type new_size) { resize(new_size, value_type()); }
470
471 void resize(size_type new_size, const value_type& value) {
472 PW_ASSERT(try_resize(new_size, value));
473 }
474
475 protected:
476 explicit constexpr GenericDeque(size_type initial_capacity) noexcept
477 : GenericDequeBase<SizeType>(initial_capacity) {}
478
479 constexpr void DestroyAll() {
480 if constexpr (!std::is_trivially_destructible_v<value_type>) {
481 auto [first, second] = contiguous_data();
482 std::destroy(first.begin(), first.end());
483 std::destroy(second.begin(), second.end());
484 }
485 }
486
487 // Infallible assignment operators
488
489 // NOLINTBEGIN(misc-unconventional-assign-operator);
490 Derived& operator=(const std::initializer_list<value_type>& list) {
491 assign(list);
492 return derived();
493 }
494
495 template <typename T, typename = containers::internal::EnableIfIterable<T>>
496 Derived& operator=(const T& other) {
497 assign(other.begin(), other.end());
498 return derived();
499 }
500 // NOLINTEND(misc-unconventional-assign-operator);
501
502 // Fallible assign
503
507 [[nodiscard]] bool try_assign(size_type count, const value_type& value);
508
515 template <typename It,
516 int&...,
517 typename = containers::internal::EnableIfForwardIterator<It>>
518 [[nodiscard]] bool try_assign(It start, It finish);
519
522 [[nodiscard]] bool try_assign(const std::initializer_list<value_type>& list) {
523 return try_assign(list.begin(), list.end());
524 }
525
526 // Fallible modify
527
533 template <typename... Args>
534 [[nodiscard]] std::optional<iterator> try_emplace(const_iterator pos,
535 Args&&... args);
536
542 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
543 const value_type& value) {
544 return try_emplace(pos, value);
545 }
546
552 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
553 value_type&& value) {
554 return try_emplace(pos, std::move(value));
555 }
556
562 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
563 size_type count,
564 const value_type& value);
565
571 template <typename ForwardIt,
572 typename = containers::internal::EnableIfForwardIterator<ForwardIt>>
573 [[nodiscard]] std::optional<iterator> try_insert(const_iterator pos,
574 ForwardIt first,
575 ForwardIt last);
576
582 [[nodiscard]] std::optional<iterator> try_insert(
583 const_iterator pos, std::initializer_list<value_type> ilist) {
584 return try_insert(pos, ilist.begin(), ilist.end());
585 }
586
587 [[nodiscard]] bool try_push_back(const value_type& value) {
588 return try_emplace_back(value);
589 }
590
591 [[nodiscard]] bool try_push_back(value_type&& value) {
592 return try_emplace_back(std::move(value));
593 }
594
595 template <typename... Args>
596 [[nodiscard]] bool try_emplace_back(Args&&... args);
597
598 [[nodiscard]] bool try_push_front(const value_type& value) {
599 return try_emplace_front(value);
600 }
601
602 [[nodiscard]] bool try_push_front(value_type&& value) {
603 return try_emplace_front(std::move(value));
604 }
605
606 template <typename... Args>
607 [[nodiscard]] bool try_emplace_front(Args&&... args);
608
609 [[nodiscard]] bool try_resize(size_type new_size) {
610 return try_resize(new_size, value_type());
611 }
612
613 [[nodiscard]] bool try_resize(size_type new_size, const value_type& value);
614
615 template <typename... Args>
616 [[nodiscard]] std::optional<iterator> try_emplace_shift_right(
617 const_iterator pos, Args&&... args);
618
619 [[nodiscard]] std::optional<iterator> try_insert_shift_right(
620 const_iterator pos, size_type count, const value_type& value);
621
622 template <typename ForwardIt,
623 typename = containers::internal::EnableIfForwardIterator<ForwardIt>>
624 [[nodiscard]] std::optional<iterator> try_insert_shift_right(
625 const_iterator pos, ForwardIt first, ForwardIt last);
626
627 private:
628 constexpr Derived& derived() { return static_cast<Derived&>(*this); }
629 constexpr const Derived& derived() const {
630 return static_cast<const Derived&>(*this);
631 }
632
633 constexpr size_type head() const { return Base::head_; }
634 constexpr size_type tail() const { return Base::tail_; }
635
636 // Accessed the underlying array in the derived class.
637 constexpr pointer data() { return derived().data(); }
638 constexpr const_pointer data() const { return derived().data(); }
639
640 // Creates an uninitialized slot of `new_items` at `insert_index`.
641 bool ShiftForInsert(size_type insert_index, size_type new_items);
642
643 // Called by ShiftForInsert depending on where the insert happens.
644 void ShiftLeft(size_type insert_index, size_type new_items);
645 void ShiftRight(size_type insert_index, size_type new_items);
646
647 // Make sure the container can hold count additional items.
648 bool CheckCapacityAdd(size_type count) {
649 size_type new_size;
650 return CheckedAdd(size(), count, new_size) && CheckCapacity(new_size);
651 }
652
653 // Ensures that the container can hold at least this many elements.
654 constexpr bool CheckCapacity(size_type new_size) {
655 if constexpr (Derived::kFixedCapacity) {
656 return new_size <= capacity();
657 } else {
658 return derived().try_reserve(new_size);
659 }
660 }
661
662 // Appends items without checking the capacity. Capacity MUST be large enough.
663 template <typename... Args>
664 void EmplaceBackUnchecked(Args&&... args) {
665 new (&data()[tail()]) value_type(std::forward<Args>(args)...);
666 Base::PushBack();
667 }
668};
669
670// Function implementations
671
672template <typename Derived, typename ValueType, typename SizeType>
673template <typename It, int&..., typename>
675 // Can't safely check std::distance for InputIterator, so use push_back().
676 if constexpr (Derived::kFixedCapacity ||
677 std::is_same_v<
678 typename std::iterator_traits<It>::iterator_category,
679 std::input_iterator_tag>) {
680 clear();
681 while (start != finish) {
682 push_back(*start);
683 ++start;
684 }
685 } else {
686 PW_ASSERT(try_assign(start, finish));
687 }
688}
689
690template <typename Derived, typename ValueType, typename SizeType>
692 size_type count, const value_type& value) {
693 if (!CheckCapacity(count)) {
694 return false;
695 }
696 clear();
697 Base::PushBack(count);
698 std::uninitialized_fill_n(data(), count, value);
699 return true;
700}
701
702template <typename Derived, typename ValueType, typename SizeType>
703template <typename It, int&..., typename>
705 It finish) {
706 // Requires at least a forward iterator to safely check std::distance.
707 static_assert(std::is_convertible_v<
708 typename std::iterator_traits<It>::iterator_category,
709 std::forward_iterator_tag>);
710 const auto items = std::distance(start, finish);
711 PW_DASSERT(items >= 0);
712 if (static_cast<std::make_unsigned_t<decltype(items)>>(items) >
713 std::numeric_limits<size_type>::max()) {
714 return false;
715 }
716 const size_type count = static_cast<size_type>(items);
717 if (!CheckCapacity(count)) {
718 return false;
719 }
720
721 clear();
722 Base::PushBack(count);
723 std::uninitialized_move(start, finish, data());
724 return true;
725}
726
727template <typename Derived, typename ValueType, typename SizeType>
728constexpr std::pair<span<const ValueType>, span<const ValueType>>
730 if (empty()) {
732 }
733 if (tail() > head()) {
734 // If the newest entry is after the oldest entry, we have not wrapped:
735 // [ |head()|...more_entries...|tail()| ]
736 return {span<const value_type>(&data()[head()], size()),
738 } else {
739 // If the newest entry is before or at the oldest entry and we know we are
740 // not empty, ergo we have wrapped:
741 // [..more_entries...|tail()| |head()|...more_entries...]
742 return {span<const value_type>(&data()[head()], capacity() - head()),
743 span<const value_type>(&data()[0], tail())};
744 }
745}
746
747template <typename Derived, typename ValueType, typename SizeType>
748template <typename... Args>
750 Args&&... args) {
751 if (!CheckCapacityAdd(1)) {
752 return false;
753 }
754 EmplaceBackUnchecked(std::forward<Args>(args)...);
755 return true;
756}
757
758template <typename Derived, typename ValueType, typename SizeType>
759void GenericDeque<Derived, ValueType, SizeType>::pop_back() {
760 PW_ASSERT(!empty());
761 if constexpr (!std::is_trivially_destructible_v<value_type>) {
762 std::destroy_at(&back());
763 }
764 Base::PopBack();
765}
766
767template <typename Derived, typename ValueType, typename SizeType>
768template <typename... Args>
769bool GenericDeque<Derived, ValueType, SizeType>::try_emplace_front(
770 Args&&... args) {
771 if (!CheckCapacityAdd(1)) {
772 return false;
773 }
774 Base::PushFront();
775 new (&data()[head()]) value_type(std::forward<Args>(args)...);
776 return true;
777}
778
779template <typename Derived, typename ValueType, typename SizeType>
780void GenericDeque<Derived, ValueType, SizeType>::pop_front() {
781 PW_ASSERT(!empty());
782 if constexpr (!std::is_trivially_destructible_v<value_type>) {
783 std::destroy_at(&front());
784 }
785 Base::PopFront();
786}
787
788template <typename Derived, typename ValueType, typename SizeType>
789bool GenericDeque<Derived, ValueType, SizeType>::try_resize(
790 size_type new_size, const value_type& value) {
791 if (size() < new_size) {
792 if (!CheckCapacity(new_size)) {
793 return false;
794 }
795 const size_type new_items = new_size - size();
796 for (size_type i = 0; i < new_items; ++i) {
797 EmplaceBackUnchecked(value);
798 }
799 } else {
800 while (size() > new_size) {
801 pop_back();
802 }
803 }
804 return true;
805}
806
807template <typename Derived, typename ValueType, typename SizeType>
808typename GenericDeque<Derived, ValueType, SizeType>::iterator
810 const_iterator last) {
811 PW_DASSERT(first <= last);
812 const iterator first_it(derived(), first.pos_);
813 const iterator last_it(derived(), last.pos_);
814
815 const size_type items_to_erase = static_cast<size_type>(last - first);
816 if (items_to_erase == 0) {
817 return first_it;
818 }
819
820 const size_type items_after = static_cast<size_type>(size() - last.pos_);
821 // Shift head forward or tail backwards -- whichever involves fewer moves.
822 if (first.pos_ < items_after) { // fewer before than after
823 std::move_backward(begin(), first_it, last_it);
824 if constexpr (!std::is_trivially_destructible_v<value_type>) {
825 std::destroy(begin(), begin() + items_to_erase);
826 }
827 Base::head_ = Base::AbsoluteIndex(items_to_erase);
828 } else { // fewer after than before
829 std::move(last_it, end(), first_it);
830 if constexpr (!std::is_trivially_destructible_v<value_type>) {
831 std::destroy(first_it + items_after, end());
832 }
833 Base::tail_ = Base::AbsoluteIndex(first.pos_ + items_after);
834 }
835 Base::count_ = size() - items_to_erase;
836 return first_it;
837}
838
839template <typename Derived, typename ValueType, typename SizeType>
840template <typename... Args>
841std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
843 Args&&... args) {
844 // Insert in the middle of the deque, shifting other items out of the way.
845 if (!ShiftForInsert(pos.pos_, 1)) {
846 return std::nullopt;
847 }
848 iterator it(derived(), pos.pos_);
849 new (std::addressof(*it)) value_type(std::forward<Args>(args)...);
850 return it;
851}
852
853template <typename Derived, typename ValueType, typename SizeType>
854bool GenericDeque<Derived, ValueType, SizeType>::ShiftForInsert(
855 const size_type insert_index, size_type new_items) {
856 if (!CheckCapacityAdd(new_items)) {
857 return false;
858 }
859
860 if (insert_index < size() / 2) { // Fewer items before than after.
861 ShiftLeft(insert_index, new_items);
862 } else {
863 ShiftRight(insert_index, new_items);
864 }
865 return true;
866}
867
868template <typename Derived, typename ValueType, typename SizeType>
869void GenericDeque<Derived, ValueType, SizeType>::ShiftLeft(
870 const size_type insert_index, size_type new_items) {
871 Base::PushFront(new_items);
872 iterator original_begin(derived(), new_items);
873
874 const size_type move_to_new_slots = std::min(new_items, insert_index);
875 auto [next_src, next_dst] =
876 std::uninitialized_move_n(original_begin, move_to_new_slots, begin());
877
878 const size_type move_to_existing_slots = insert_index - move_to_new_slots;
879 std::move(next_src, next_src + move_to_existing_slots, next_dst);
880
881 // For consistency, ensure the whole opening is uninitialized.
882 // TODO: b/429231491 - Consider having callers handle moved vs uninitialized
883 // slots or doing the insert in this function.
884 if constexpr (!std::is_trivially_destructible_v<value_type>) {
885 std::destroy(
886 iterator(derived(), std::max(insert_index, original_begin.pos_)),
887 iterator(derived(), insert_index + new_items));
888 }
889}
890
891template <typename Derived, typename ValueType, typename SizeType>
892void GenericDeque<Derived, ValueType, SizeType>::ShiftRight(
893 const size_type insert_index, size_type new_items) {
894 const size_type items_after = size() - insert_index;
895 iterator original_end = end();
896 Base::PushBack(new_items);
897
898 const size_type move_to_new_slots = std::min(new_items, items_after);
899 std::uninitialized_move(original_end - move_to_new_slots,
900 original_end,
901 end() - move_to_new_slots);
902
903 const size_type move_to_existing_slots = items_after - move_to_new_slots;
904 iterator pos(derived(), insert_index);
905 std::move_backward(pos, pos + move_to_existing_slots, original_end);
906
907 // For consistency, ensure the whole opening is uninitialized.
908 // TODO: b/429231491 - Consider having callers handle moved vs uninitialized
909 // slots or doing the insert in this function.
910 if constexpr (!std::is_trivially_destructible_v<value_type>) {
911 std::destroy(
912 iterator(derived(), insert_index),
913 iterator(derived(),
914 std::min(static_cast<size_type>(insert_index + new_items),
915 original_end.pos_)));
916 }
917}
918
919template <typename Derived, typename ValueType, typename SizeType>
920std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
922 const_iterator pos, size_type count, const value_type& value) {
923 if (count == 0) {
924 return iterator(derived(), pos.pos_);
925 }
926 if (!ShiftForInsert(pos.pos_, count)) {
927 return std::nullopt;
928 }
929
930 iterator it(derived(), pos.pos_);
931 std::uninitialized_fill_n(it, count, value);
932 return it;
933}
934
935template <typename Derived, typename ValueType, typename SizeType>
936template <typename InputIt, typename>
937typename GenericDeque<Derived, ValueType, SizeType>::iterator
939 InputIt first,
940 InputIt last) {
941 // Can't safely check std::distance for InputIterator, so repeatedly emplace()
942 if constexpr (std::is_same_v<
943 typename std::iterator_traits<InputIt>::iterator_category,
944 std::input_iterator_tag>) {
945 // Inserting M items into an N-item deque is O(N*M) due to repeated
946 // emplace() calls. DynamicDeque reads into a temporary deque instead.
947 iterator insert_pos = iterator(derived(), pos.pos_);
948 while (first != last) {
949 insert_pos = emplace(insert_pos, *first);
950 ++first;
951 ++insert_pos;
952 }
953 return iterator(derived(), pos.pos_);
954 } else {
955 std::optional<iterator> result = try_insert(pos, first, last);
956 PW_ASSERT(result.has_value());
957 return *result;
958 }
959}
960
961template <typename Derived, typename ValueType, typename SizeType>
962template <typename ForwardIt, typename>
963std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
965 ForwardIt first,
966 ForwardIt last) {
967 static_assert(std::is_convertible_v<
968 typename std::iterator_traits<ForwardIt>::iterator_category,
969 std::forward_iterator_tag>);
970 const auto distance = std::distance(first, last);
971 PW_DASSERT(distance >= 0);
972 const size_type count = static_cast<size_type>(distance);
973
974 const iterator it(derived(), pos.pos_);
975 if (count == 0) {
976 return it;
977 }
978 if (!ShiftForInsert(pos.pos_, count)) {
979 return std::nullopt;
980 }
981 std::uninitialized_move(first, last, it);
982 return it;
983}
984
985template <typename Derived, typename ValueType, typename SizeType>
986template <typename... Args>
987std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
988GenericDeque<Derived, ValueType, SizeType>::try_emplace_shift_right(
989 const_iterator pos, Args&&... args) {
990 if (!CheckCapacityAdd(1)) {
991 return std::nullopt;
992 }
993 ShiftRight(pos.pos_, 1);
994 iterator it(derived(), pos.pos_);
995 new (std::addressof(*it)) value_type(std::forward<Args>(args)...);
996 return it;
997}
998
999template <typename Derived, typename ValueType, typename SizeType>
1000std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
1001GenericDeque<Derived, ValueType, SizeType>::try_insert_shift_right(
1002 const_iterator pos, size_type count, const value_type& value) {
1003 if (count == 0) {
1004 return iterator(derived(), pos.pos_);
1005 }
1006 if (!CheckCapacityAdd(count)) {
1007 return std::nullopt;
1008 }
1009
1010 ShiftRight(pos.pos_, count);
1011 iterator it(derived(), pos.pos_);
1012 std::uninitialized_fill_n(it, count, value);
1013 return it;
1014}
1015
1016template <typename Derived, typename ValueType, typename SizeType>
1017template <typename ForwardIt, typename>
1018std::optional<typename GenericDeque<Derived, ValueType, SizeType>::iterator>
1019GenericDeque<Derived, ValueType, SizeType>::try_insert_shift_right(
1020 const_iterator pos, ForwardIt first, ForwardIt last) {
1021 static_assert(std::is_convertible_v<
1022 typename std::iterator_traits<ForwardIt>::iterator_category,
1023 std::forward_iterator_tag>);
1024 const auto distance = std::distance(first, last);
1025 PW_DASSERT(distance >= 0);
1026 const size_type count = static_cast<size_type>(distance);
1027
1028 const iterator it(derived(), pos.pos_);
1029 if (count == 0) {
1030 return it;
1031 }
1032 if (!CheckCapacityAdd(count)) {
1033 return std::nullopt;
1034 }
1035 ShiftRight(pos.pos_, count);
1036 std::uninitialized_move(first, last, it);
1037 return it;
1038}
1039
1040} // namespace pw::containers::internal
Definition: generic_deque.h:54
Definition: generic_deque.h:195
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:424
iterator insert(const_iterator pos, size_type count, const value_type &value)
Definition: generic_deque.h:444
iterator insert(const_iterator pos, InputIt first, InputIt last)
Definition: generic_deque.h:938
void push_back_overwrite(const value_type &value)
Definition: generic_deque.h:340
std::optional< iterator > try_insert(const_iterator pos, const value_type &value)
Definition: generic_deque.h:542
bool try_assign(size_type count, const value_type &value)
Definition: generic_deque.h:691
void emplace_front_overwrite(Args &&... args)
Definition: generic_deque.h:396
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:729
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:318
std::optional< iterator > try_insert(const_iterator pos, std::initializer_list< value_type > ilist)
Definition: generic_deque.h:582
iterator insert(const_iterator pos, value_type &&value)
Definition: generic_deque.h:434
void assign(It start, It finish)
Definition: generic_deque.h:674
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:226
iterator insert(const_iterator pos, std::initializer_list< value_type > ilist)
Definition: generic_deque.h:463
void push_back_overwrite(value_type &&value)
Definition: generic_deque.h:346
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:522
std::optional< iterator > try_insert(const_iterator pos, value_type &&value)
Definition: generic_deque.h:552
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:238
iterator emplace(const_iterator pos, Args &&... args)
Definition: generic_deque.h:414
std::optional< iterator > try_insert(const_iterator pos, ForwardIt first, ForwardIt last)
void emplace_back_overwrite(Args &&... args)
Definition: generic_deque.h:353
void push_front_overwrite(const value_type &value)
Definition: generic_deque.h:383
iterator erase(const_iterator first, const_iterator last)
Definition: generic_deque.h:809
void push_front_overwrite(value_type &&value)
Definition: generic_deque.h:389
constexpr bool CheckedAdd(A a, B b, T &result)
Definition: checked_arithmetic.h:70