Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qarraydataops.h
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#ifndef QARRAYDATAOPS_H
7#define QARRAYDATAOPS_H
8
9#include <QtCore/qarraydata.h>
10#include <QtCore/qcontainertools_impl.h>
11#include <QtCore/qnamespace.h>
12
13#include <QtCore/q20functional.h>
14#include <QtCore/q20memory.h>
15#include <new>
16#include <string.h>
17#include <utility>
18#include <iterator>
19#include <type_traits>
20
21QT_BEGIN_NAMESPACE
22
23template <class T> struct QArrayDataPointer;
24
25namespace QtPrivate {
26
27template <class T>
29{
30 static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
31
32protected:
35
36private:
37 DataPointer *m_ptr;
38
39public:
40 explicit QPodArrayOps(DataPointer &dp) : m_ptr{&dp} {}
41
43 { return m_ptr; }
44 const DataPointer *that() const
45 { return m_ptr; }
46
48
49 void copyAppend(const T *b, const T *e) noexcept
50 {
51 Q_ASSERT(that()->isMutable() || b == e);
52 Q_ASSERT(!that()->isShared() || b == e);
53 Q_ASSERT(b <= e);
54 Q_ASSERT((e - b) <= that()->freeSpaceAtEnd());
55
56 if (b == e)
57 return;
58
59 ::memcpy(static_cast<void *>(that()->end()), static_cast<const void *>(b), (e - b) * sizeof(T));
60 that()->size += (e - b);
61 }
62
63 void copyAppend(qsizetype n, parameter_type t) noexcept
64 {
65 Q_ASSERT(!that()->isShared() || n == 0);
66 Q_ASSERT(that()->freeSpaceAtEnd() >= n);
67 if (!n)
68 return;
69
70 T *where = that()->end();
71 that()->size += qsizetype(n);
72 while (n--)
73 *where++ = t;
74 }
75
76 void moveAppend(T *b, T *e) noexcept
77 {
79 }
80
81 void truncate(size_t newSize) noexcept
82 {
83 Q_ASSERT(that()->isMutable());
84 Q_ASSERT(!that()->isShared());
85 Q_ASSERT(newSize <= size_t(that()->size));
86
87 that()->size = qsizetype(newSize);
88 }
89
90 void destroyAll() noexcept // Call from destructors, ONLY!
91 {
92 Q_ASSERT(that()->d);
93 Q_ASSERT(that()->d->ref_.loadRelaxed() == 0);
94
95 // As this is to be called only from destructor, it doesn't need to be
96 // exception safe; size not updated.
97 }
98
99 T *createHole(QArrayData::GrowthPosition pos, qsizetype where, qsizetype n)
100 {
101 Q_ASSERT((pos == QArrayData::GrowsAtBeginning && n <= that()->freeSpaceAtBegin()) ||
102 (pos == QArrayData::GrowsAtEnd && n <= that()->freeSpaceAtEnd()));
103
104 T *insertionPoint = that()->ptr + where;
105 if (pos == QArrayData::GrowsAtEnd) {
106 if (where < that()->size)
107 ::memmove(static_cast<void *>(insertionPoint + n), static_cast<void *>(insertionPoint), (that()->size - where) * sizeof(T));
108 } else {
109 Q_ASSERT(where == 0);
110 that()->ptr -= n;
111 insertionPoint -= n;
112 }
113 that()->size += n;
114 return insertionPoint;
115 }
116
117 void insert(qsizetype i, const T *data, qsizetype n)
118 {
119 typename Data::GrowthPosition pos = Data::GrowsAtEnd;
120 if (that()->size != 0 && i == 0)
121 pos = Data::GrowsAtBeginning;
122
123 DataPointer oldData;
124 that()->detachAndGrow(pos, n, &data, &oldData);
125 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
126 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
127
128 T *where = createHole(pos, i, n);
129 ::memcpy(static_cast<void *>(where), static_cast<const void *>(data), n * sizeof(T));
130 }
131
132 void insert(qsizetype i, qsizetype n, parameter_type t)
133 {
134 T copy(t);
135
136 typename Data::GrowthPosition pos = Data::GrowsAtEnd;
137 if (that()->size != 0 && i == 0)
138 pos = Data::GrowsAtBeginning;
139
140 that()->detachAndGrow(pos, n, nullptr, nullptr);
141 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
142 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
143
144 T *where = createHole(pos, i, n);
145 while (n--)
146 *where++ = copy;
147 }
148
149 template<typename... Args>
150 void emplace(qsizetype i, Args &&... args)
151 {
152 bool detach = that()->needsDetach();
153 if (!detach) {
154 if (i == that()->size && that()->freeSpaceAtEnd()) {
155 new (that()->end()) T(std::forward<Args>(args)...);
156 ++that()->size;
157 return;
158 }
159 if (i == 0 && that()->freeSpaceAtBegin()) {
160 new (that()->begin() - 1) T(std::forward<Args>(args)...);
161 --that()->ptr;
162 ++that()->size;
163 return;
164 }
165 }
166 T tmp(std::forward<Args>(args)...);
167 typename QArrayData::GrowthPosition pos = QArrayData::GrowsAtEnd;
168 if (that()->size != 0 && i == 0)
169 pos = QArrayData::GrowsAtBeginning;
170
171 that()->detachAndGrow(pos, 1, nullptr, nullptr);
172
173 T *where = createHole(pos, i, 1);
174 new (where) T(std::move(tmp));
175 }
176
177 void erase(T *b, qsizetype n)
178 {
179 T *e = b + n;
180 Q_ASSERT(that()->isMutable());
181 Q_ASSERT(b < e);
182 Q_ASSERT(b >= that()->begin() && b < that()->end());
183 Q_ASSERT(e > that()->begin() && e <= that()->end());
184
185 // Comply with std::vector::erase(): erased elements and all after them
186 // are invalidated. However, erasing from the beginning effectively
187 // means that all iterators are invalidated. We can use this freedom to
188 // erase by moving towards the end.
189 if (b == that()->begin() && e != that()->end()) {
190 that()->ptr = e;
191 } else if (e != that()->end()) {
192 ::memmove(static_cast<void *>(b), static_cast<void *>(e),
193 (static_cast<T *>(that()->end()) - e) * sizeof(T));
194 }
195 that()->size -= n;
196 }
197
198 void eraseFirst() noexcept
199 {
200 Q_ASSERT(that()->isMutable());
201 Q_ASSERT(that()->size);
202 ++that()->ptr;
203 --that()->size;
204 }
205
206 void eraseLast() noexcept
207 {
208 Q_ASSERT(that()->isMutable());
209 Q_ASSERT(that()->size);
210 --that()->size;
211 }
212
213 template <typename Predicate>
214 qsizetype eraseIf(Predicate pred)
215 {
216 qsizetype result = 0;
217 if (that()->size == 0)
218 return result;
219
220 if (!that()->needsDetach()) {
221 auto end = that()->end();
222 auto it = std::remove_if(that()->begin(), end, pred);
223 if (it != end) {
224 result = std::distance(it, end);
225 erase(it, result);
226 }
227 } else {
228 const auto begin = that()->begin();
229 const auto end = that()->end();
230 auto it = std::find_if(begin, end, pred);
231 if (it == end)
232 return result;
233
234 QArrayDataPointer<T> other(that()->size);
235 Q_CHECK_PTR(other.data());
236 auto dest = other.begin();
237 // std::uninitialized_copy will fallback to ::memcpy/memmove()
238 dest = std::uninitialized_copy(begin, it, dest);
239 dest = q_uninitialized_remove_copy_if(std::next(it), end, dest, pred);
240 other.size = std::distance(other.data(), dest);
241 result = that()->size - other.size;
242 that()->swap(other);
243 }
244 return result;
245 }
246
247 struct Span { T *begin; T *end; };
248
249 void copyRanges(std::initializer_list<Span> ranges)
250 {
251 auto it = that()->begin();
252 std::for_each(ranges.begin(), ranges.end(), [&it](const auto &span) {
253 it = std::copy(span.begin, span.end, it);
254 });
255 that()->size = std::distance(that()->begin(), it);
256 }
257
258 void assign(T *b, T *e, parameter_type t) noexcept
259 {
260 Q_ASSERT(b <= e);
261 Q_ASSERT(b >= that()->begin() && e <= that()->end());
262
263 while (b != e)
264 ::memcpy(static_cast<void *>(b++), static_cast<const void *>(&t), sizeof(T));
265 }
266
267 void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
268 {
269 auto pair = Data::reallocateUnaligned(that()->d, that()->ptr, alloc, option);
270 Q_CHECK_PTR(pair.ptr);
271 Q_ASSERT(pair.header != nullptr);
272 that()->d = pair.header;
273 that()->ptr = pair.ptr;
274 }
275};
276
277template <class T>
279{
280 static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
281
282protected:
285
286private:
287 DataPointer *m_ptr;
288
289public:
290 explicit QGenericArrayOps(DataPointer &dp) : m_ptr{&dp} {}
291
293 { return m_ptr; }
294 const DataPointer *that() const
295 { return m_ptr; }
296
298
299 void copyAppend(const T *b, const T *e)
300 {
301 Q_ASSERT(that()->isMutable() || b == e);
302 Q_ASSERT(!that()->isShared() || b == e);
303 Q_ASSERT(b <= e);
304 Q_ASSERT((e - b) <= that()->freeSpaceAtEnd());
305
306 if (b == e) // short-cut and handling the case b and e == nullptr
307 return;
308
309 T *data = that()->begin();
310 while (b < e) {
311 new (data + that()->size) T(*b);
312 ++b;
313 ++that()->size;
314 }
315 }
316
317 void copyAppend(qsizetype n, parameter_type t)
318 {
319 Q_ASSERT(!that()->isShared() || n == 0);
320 Q_ASSERT(that()->freeSpaceAtEnd() >= n);
321 if (!n)
322 return;
323
324 T *data = that()->begin();
325 while (n--) {
326 new (data + that()->size) T(t);
327 ++that()->size;
328 }
329 }
330
331 void moveAppend(T *b, T *e)
332 {
333 Q_ASSERT(that()->isMutable() || b == e);
334 Q_ASSERT(!that()->isShared() || b == e);
335 Q_ASSERT(b <= e);
336 Q_ASSERT((e - b) <= that()->freeSpaceAtEnd());
337
338 if (b == e)
339 return;
340
341 T *data = that()->begin();
342 while (b < e) {
343 new (data + that()->size) T(std::move(*b));
344 ++b;
345 ++that()->size;
346 }
347 }
348
349 void truncate(size_t newSize)
350 {
351 Q_ASSERT(that()->isMutable());
352 Q_ASSERT(!that()->isShared());
353 Q_ASSERT(newSize <= size_t(that()->size));
354
355 std::destroy(that()->begin() + newSize, that()->end());
356 that()->size = newSize;
357 }
358
359 void destroyAll() // Call from destructors, ONLY
360 {
361 Q_ASSERT(that()->d);
362 // As this is to be called only from destructor, it doesn't need to be
363 // exception safe; size not updated.
364
365 Q_ASSERT(that()->d->ref_.loadRelaxed() == 0);
366
367 std::destroy(that()->begin(), that()->end());
368 }
369
370 struct Inserter
371 {
375
377 T *end = nullptr, *last = nullptr, *where = nullptr;
378
379 Inserter(QArrayDataPointer<T> *d) : data(d)
380 {
381 begin = d->ptr;
382 size = d->size;
383 }
385 data->ptr = begin;
386 data->size = size;
387 }
389
391 {
392 end = begin + size;
393 last = end - 1;
394 where = begin + pos;
397 nSource = n;
398 move = n - dist; // smaller 0
400 if (n > dist) {
402 move = 0;
404 }
405 }
406
408 {
411
412 setup(pos, n);
413
414 // first create new elements at the end, by copying from elements
415 // to be inserted (if they extend past the current end of the array)
416 for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
417 new (end + i) T(source[nSource - sourceCopyConstruct + i]);
418 ++size;
419 }
420 Q_ASSERT(size <= oldSize + n);
421
422 // now move construct new elements at the end from existing elements inside
423 // the array.
424 for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
425 new (end + i) T(std::move(*(end + i - nSource)));
426 ++size;
427 }
428 // array has the new size now!
429 Q_ASSERT(size == oldSize + n);
430
431 // now move assign existing elements towards the end
432 for (qsizetype i = 0; i != move; --i)
433 last[i] = std::move(last[i - nSource]);
434
435 // finally copy the remaining elements from source over
436 for (qsizetype i = 0; i != sourceCopyAssign; ++i)
437 where[i] = source[i];
438 }
439
441 {
442 const qsizetype oldSize = size;
444
445 setup(pos, n);
446
447 // first create new elements at the end, by copying from elements
448 // to be inserted (if they extend past the current end of the array)
449 for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
450 new (end + i) T(t);
451 ++size;
452 }
453 Q_ASSERT(size <= oldSize + n);
454
455 // now move construct new elements at the end from existing elements inside
456 // the array.
457 for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
458 new (end + i) T(std::move(*(end + i - nSource)));
459 ++size;
460 }
461 // array has the new size now!
462 Q_ASSERT(size == oldSize + n);
463
464 // now move assign existing elements towards the end
465 for (qsizetype i = 0; i != move; --i)
466 last[i] = std::move(last[i - nSource]);
467
468 // finally copy the remaining elements from source over
469 for (qsizetype i = 0; i != sourceCopyAssign; ++i)
470 where[i] = t;
471 }
472
474 {
475 setup(pos, 1);
476
479 new (end) T(std::move(t));
480 ++size;
481 } else {
482 // create a new element at the end by move constructing one existing element
483 // inside the array.
484 new (end) T(std::move(*(end - 1)));
485 ++size;
486
487 // now move assign existing elements towards the end
488 for (qsizetype i = 0; i != move; --i)
489 last[i] = std::move(last[i - 1]);
490
491 // and move the new item into place
492 *where = std::move(t);
493 }
494 }
495 };
496
497 void insert(qsizetype i, const T *data, qsizetype n)
498 {
499 const bool growsAtBegin = that()->size != 0 && i == 0;
500 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
501
502 DataPointer oldData;
503 that()->detachAndGrow(pos, n, &data, &oldData);
504 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
505 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
506
507 if (growsAtBegin) {
508 // copy construct items in reverse order at the begin
509 Q_ASSERT(that()->freeSpaceAtBegin() >= n);
510 while (n) {
511 --n;
512 new (that()->begin() - 1) T(data[n]);
513 --that()->ptr;
514 ++that()->size;
515 }
516 } else {
517 Inserter{that()}.insert(i, data, n);
518 }
519 }
520
521 void insert(qsizetype i, qsizetype n, parameter_type t)
522 {
523 T copy(t);
524
525 const bool growsAtBegin = that()->size != 0 && i == 0;
526 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
527
528 that()->detachAndGrow(pos, n, nullptr, nullptr);
529 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
530 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
531
532 if (growsAtBegin) {
533 // copy construct items in reverse order at the begin
534 Q_ASSERT(that()->freeSpaceAtBegin() >= n);
535 while (n--) {
536 new (that()->begin() - 1) T(copy);
537 --that()->ptr;
538 ++that()->size;
539 }
540 } else {
541 Inserter{that()}.insert(i, copy, n);
542 }
543 }
544
545 template<typename... Args>
546 void emplace(qsizetype i, Args &&... args)
547 {
548 bool detach = that()->needsDetach();
549 if (!detach) {
550 if (i == that()->size && that()->freeSpaceAtEnd()) {
551 new (that()->end()) T(std::forward<Args>(args)...);
552 ++that()->size;
553 return;
554 }
555 if (i == 0 && that()->freeSpaceAtBegin()) {
556 new (that()->begin() - 1) T(std::forward<Args>(args)...);
557 --that()->ptr;
558 ++that()->size;
559 return;
560 }
561 }
562 T tmp(std::forward<Args>(args)...);
563 const bool growsAtBegin = that()->size != 0 && i == 0;
564 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
565
566 that()->detachAndGrow(pos, 1, nullptr, nullptr);
567
568 if (growsAtBegin) {
569 Q_ASSERT(that()->freeSpaceAtBegin());
570 new (that()->begin() - 1) T(std::move(tmp));
571 --that()->ptr;
572 ++that()->size;
573 } else {
574 Inserter{that()}.insertOne(i, std::move(tmp));
575 }
576 }
577
578 void erase(T *b, qsizetype n)
579 {
580 T *e = b + n;
581 Q_ASSERT(that()->isMutable());
582 Q_ASSERT(b < e);
583 Q_ASSERT(b >= that()->begin() && b < that()->end());
584 Q_ASSERT(e > that()->begin() && e <= that()->end());
585
586 // Comply with std::vector::erase(): erased elements and all after them
587 // are invalidated. However, erasing from the beginning effectively
588 // means that all iterators are invalidated. We can use this freedom to
589 // erase by moving towards the end.
590 if (b == that()->begin() && e != that()->end()) {
591 that()->ptr = e;
592 } else {
593 const T *const end = that()->end();
594
595 // move (by assignment) the elements from e to end
596 // onto b to the new end
597 while (e != end) {
598 *b = std::move(*e);
599 ++b;
600 ++e;
601 }
602 }
603 that()->size -= n;
604 std::destroy(b, e);
605 }
606
607 void eraseFirst() noexcept
608 {
609 Q_ASSERT(that()->isMutable());
610 Q_ASSERT(that()->size);
611 that()->begin()->~T();
612 ++that()->ptr;
613 --that()->size;
614 }
615
616 void eraseLast() noexcept
617 {
618 Q_ASSERT(that()->isMutable());
619 Q_ASSERT(that()->size);
620 (that()->end() - 1)->~T();
621 --that()->size;
622 }
623
624
625 void assign(T *b, T *e, parameter_type t)
626 {
627 Q_ASSERT(b <= e);
628 Q_ASSERT(b >= that()->begin() && e <= that()->end());
629
630 while (b != e)
631 *b++ = t;
632 }
633};
634
635template <class T>
638{
640 static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
641
642protected:
645
646public:
647 explicit QMovableArrayOps(DataPointer &dp) : Base(dp) {}
648
650 { return Base::that(); }
651 const DataPointer *that() const
652 { return Base::that(); }
653
654 // using QGenericArrayOps<T>::copyAppend;
655 // using QGenericArrayOps<T>::moveAppend;
656 // using QGenericArrayOps<T>::truncate;
657 // using QGenericArrayOps<T>::destroyAll;
659
660 struct Inserter
661 {
664 T * const displaceTo;
667
669 { Q_ASSERT(displaceFrom == displaceTo); }
670
671 explicit Inserter(QArrayDataPointer<T> *d, qsizetype pos, qsizetype n)
672 : data{d},
673 displaceFrom{d->ptr + pos},
675 nInserts{n},
676 bytes{(data->size - pos) * sizeof(T)}
677 {
678 ::memmove(static_cast<void *>(displaceTo), static_cast<void *>(displaceFrom), bytes);
679 }
681 auto inserts = nInserts;
682 if constexpr (!std::is_nothrow_copy_constructible_v<T>) {
683 if (displaceFrom != displaceTo) {
684 ::memmove(static_cast<void *>(displaceFrom), static_cast<void *>(displaceTo), bytes);
686 }
687 }
688 data->size += inserts;
689 }
691
693 {
694 while (n--) {
695 new (displaceFrom) T(*source);
696 ++source;
697 ++displaceFrom;
698 }
699 verifyPost();
700 }
701
702 void insertFill(const T &t, qsizetype n)
703 {
704 while (n--) {
705 new (displaceFrom) T(t);
706 ++displaceFrom;
707 }
708 verifyPost();
709 }
710
711 void insertOne(T &&t)
712 {
713 new (displaceFrom) T(std::move(t));
714 ++displaceFrom;
715 verifyPost();
716 }
717
718 };
719
720
721 void insert(qsizetype i, const T *data, qsizetype n)
722 {
723 const bool growsAtBegin = that()->size != 0 && i == 0;
724 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
725
726 DataPointer oldData;
727 that()->detachAndGrow(pos, n, &data, &oldData);
728 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
729 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
730
731 if (growsAtBegin) {
732 // copy construct items in reverse order at the begin
733 Q_ASSERT(that()->freeSpaceAtBegin() >= n);
734 while (n) {
735 --n;
736 new (that()->begin() - 1) T(data[n]);
737 --that()->ptr;
738 ++that()->size;
739 }
740 } else {
741 Inserter{that(), i, n}.insertRange(data, n);
742 }
743 }
744
745 void insert(qsizetype i, qsizetype n, parameter_type t)
746 {
747 T copy(t);
748
749 const bool growsAtBegin = that()->size != 0 && i == 0;
750 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
751
752 that()->detachAndGrow(pos, n, nullptr, nullptr);
753 Q_ASSERT((pos == Data::GrowsAtBeginning && that()->freeSpaceAtBegin() >= n) ||
754 (pos == Data::GrowsAtEnd && that()->freeSpaceAtEnd() >= n));
755
756 if (growsAtBegin) {
757 // copy construct items in reverse order at the begin
758 Q_ASSERT(that()->freeSpaceAtBegin() >= n);
759 while (n--) {
760 new (that()->begin() - 1) T(copy);
761 --that()->ptr;
762 ++that()->size;
763 }
764 } else {
765 Inserter{that(), i, n}.insertFill(copy, n);
766 }
767 }
768
769 template<typename... Args>
770 void emplace(qsizetype i, Args &&... args)
771 {
772 bool detach = that()->needsDetach();
773 if (!detach) {
774 if (i == that()->size && that()->freeSpaceAtEnd()) {
775 new (that()->end()) T(std::forward<Args>(args)...);
776 ++that()->size;
777 return;
778 }
779 if (i == 0 && that()->freeSpaceAtBegin()) {
780 new (that()->begin() - 1) T(std::forward<Args>(args)...);
781 --that()->ptr;
782 ++that()->size;
783 return;
784 }
785 }
786 T tmp(std::forward<Args>(args)...);
787 const bool growsAtBegin = that()->size != 0 && i == 0;
788 const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
789
790 that()->detachAndGrow(pos, 1, nullptr, nullptr);
791 if (growsAtBegin) {
792 Q_ASSERT(that()->freeSpaceAtBegin());
793 new (that()->begin() - 1) T(std::move(tmp));
794 --that()->ptr;
795 ++that()->size;
796 } else {
797 Inserter{that(), i, 1}.insertOne(std::move(tmp));
798 }
799 }
800
801 void erase(T *b, qsizetype n)
802 {
803 T *e = b + n;
804
805 Q_ASSERT(that()->isMutable());
806 Q_ASSERT(b < e);
807 Q_ASSERT(b >= that()->begin() && b < that()->end());
808 Q_ASSERT(e > that()->begin() && e <= that()->end());
809
810 // Comply with std::vector::erase(): erased elements and all after them
811 // are invalidated. However, erasing from the beginning effectively
812 // means that all iterators are invalidated. We can use this freedom to
813 // erase by moving towards the end.
814
815 std::destroy(b, e);
816 if (b == that()->begin() && e != that()->end()) {
817 that()->ptr = e;
818 } else if (e != that()->end()) {
819 memmove(static_cast<void *>(b), static_cast<const void *>(e), (static_cast<const T *>(that()->end()) - e)*sizeof(T));
820 }
821 that()->size -= n;
822 }
823
824 void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
825 {
826 auto pair = Data::reallocateUnaligned(that()->d, that()->ptr, alloc, option);
827 Q_CHECK_PTR(pair.ptr);
828 Q_ASSERT(pair.header != nullptr);
829 that()->d = pair.header;
830 that()->ptr = pair.ptr;
831 }
832};
833
834template <class T, class = void>
836{
838};
839
840template <class T>
842 typename std::enable_if<
844 >::type>
845{
847};
848
849template <class T>
851 typename std::enable_if<
853 >::type>
854{
856};
857
858template <class T>
860{
861 using Base = typename QArrayOpsSelector<T>::Type;
865
866protected:
868
869public:
870 using Base::Base;
871
873 { return Base::that(); }
874 const DataPointer *that() const
875 { return Base::that(); }
876
877 // using Base::truncate;
878 // using Base::destroyAll;
879
880 template<typename It>
881 void appendIteratorRange(It b, It e, QtPrivate::IfIsForwardIterator<It> = true)
882 {
883 Q_ASSERT(that()->isMutable() || b == e);
884 Q_ASSERT(!that()->isShared() || b == e);
885 const qsizetype distance = std::distance(b, e);
886 Q_ASSERT(distance >= 0 && distance <= that()->allocatedCapacity() - that()->size);
887 Q_UNUSED(distance);
888
889#if __cplusplus >= 202002L && defined(__cpp_concepts) && defined(__cpp_lib_concepts)
890 constexpr bool canUseCopyAppend =
891 std::contiguous_iterator<It> &&
892 std::is_same_v<
893 std::remove_cv_t<typename std::iterator_traits<It>::value_type>,
894 T
895 >;
896 if constexpr (canUseCopyAppend) {
897 Base::copyAppend(std::to_address(b), std::to_address(e));
898 } else
899#endif
900 {
901 T *iter = that()->end();
902 for (; b != e; ++iter, ++b) {
903 new (iter) T(*b);
904 ++that()->size;
905 }
906 }
907 }
908
909 // slightly higher level API than copyAppend() that also preallocates space
910 void growAppend(const T *b, const T *e)
911 {
912 if (b == e)
913 return;
914 Q_ASSERT(b < e);
915 const qsizetype n = e - b;
916 DataPointer old;
917
918 // points into range:
919 if (QtPrivate::q_points_into_range(b, *that()))
920 that()->detachAndGrow(QArrayData::GrowsAtEnd, n, &b, &old);
921 else
922 that()->detachAndGrow(QArrayData::GrowsAtEnd, n, nullptr, nullptr);
923 Q_ASSERT(that()->freeSpaceAtEnd() >= n);
924 // b might be updated so use [b, n)
925 Base::copyAppend(b, b + n);
926 }
927
928 void appendUninitialized(qsizetype newSize)
929 {
930 Q_ASSERT(that()->isMutable());
931 Q_ASSERT(!that()->isShared());
932 Q_ASSERT(newSize > that()->size);
933 Q_ASSERT(newSize - that()->size <= that()->freeSpaceAtEnd());
934
935
936 T *const b = that()->begin() + that()->size;
937 T *const e = that()->begin() + newSize;
938 if constexpr (std::is_constructible_v<T, Qt::Initialization>)
939 std::uninitialized_fill(b, e, Qt::Uninitialized);
940 else
941 std::uninitialized_default_construct(b, e);
942 that()->size = newSize;
943 }
944
945 using Base::assign;
946
947 template <typename InputIterator, typename Projection = q20::identity>
948 void assign(InputIterator first, InputIterator last, Projection proj = {})
949 {
950 // This function only provides the basic exception guarantee.
951 using Category = typename std::iterator_traits<InputIterator>::iterator_category;
952 constexpr bool IsFwdIt = std::is_convertible_v<Category, std::forward_iterator_tag>;
953
954 const qsizetype n = IsFwdIt ? std::distance(first, last) : 0;
955 bool undoPrependOptimization = true;
956 bool needCapacity = n > that()->constAllocatedCapacity();
957 if (needCapacity || that()->needsDetach()) {
958 qsizetype newCapacity = that()->detachCapacity(n);
959 bool wasLastRef = !that()->deref();
960 if (wasLastRef && needCapacity) {
961 // free memory we can't reuse
962 Base::destroyAll();
963 Data::deallocate(that()->d);
964 }
965 if (!needCapacity && wasLastRef) {
966 // we were the last reference and can reuse the storage
967 that()->d->ref_.storeRelaxed(1);
968 } else {
969 // we must allocate new memory
970 auto [hdr, p] = Data::allocate(newCapacity);
971 that()->d = hdr;
972 that()->ptr = p;
973 that()->size = 0;
974 undoPrependOptimization = false;
975 }
976 }
977
978 if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>
979 || !std::is_nothrow_invocable_v<Projection, decltype(*first)>)
980 {
981 // If construction can throw, and we have freeSpaceAtBegin(),
982 // it's easiest to just clear the container and start fresh.
983 // The alternative would be to keep track of two active, disjoint ranges.
984 if (undoPrependOptimization) {
985 Base::truncate(0);
986 that()->setBegin(Data::dataStart(that()->d, alignof(typename Data::AlignmentDummy)));
987 undoPrependOptimization = false;
988 }
989 }
990
991 const auto dend = that()->end();
992 T *dst = that()->begin();
993 T *capacityBegin = dst;
994 if (undoPrependOptimization) {
995 capacityBegin = Data::dataStart(that()->d, alignof(typename Data::AlignmentDummy));
996 that()->setBegin(capacityBegin); // undo prepend optimization
997 }
998
999 assign_impl(first, last, capacityBegin, dst, dend, proj, Category{});
1000 }
1001
1002 template <typename InputIterator, typename Projection>
1003 void assign_impl(InputIterator first, InputIterator last, T *capacityBegin, T *dst, T *dend,
1004 Projection proj, std::input_iterator_tag)
1005 {
1006 if (qsizetype offset = dst - capacityBegin) {
1007 T *prependBufferEnd = dst;
1008 dst = capacityBegin;
1009
1010 // By construction, the following loop is nothrow!
1011 // (otherwise, we can't reach here)
1012 // Assumes InputIterator operations don't throw.
1013 // (but we can't statically assert that, as these operations
1014 // have preconditons, so typically aren't noexcept)
1015 while (true) {
1016 if (dst == prependBufferEnd) { // ran out of prepend buffer space
1017 that()->size += offset;
1018 // we now have a contiguous buffer, continue with the main loop:
1019 break;
1020 }
1021 if (first == last) { // ran out of elements to assign
1022 std::destroy(prependBufferEnd, dend);
1023 that()->size = dst - that()->begin();
1024 return;
1025 }
1026 // construct element in prepend buffer
1027 q20::construct_at(dst, std::invoke(proj, *first));
1028 ++dst;
1029 ++first;
1030 }
1031 }
1032 while (true) {
1033 if (first == last) { // ran out of elements to assign
1034 std::destroy(dst, dend);
1035 break;
1036 }
1037 if (dst == dend) { // ran out of existing elements to overwrite
1038 do {
1039 Base::emplace(that()->size, std::invoke(proj, *first));
1040 } while (++first != last);
1041 return; // size() is already correct (and dst invalidated)!
1042 }
1043 *dst = std::invoke(proj, *first); // overwrite existing element
1044 ++dst;
1045 ++first;
1046 }
1047 that()->size = dst - that()->begin();
1048 }
1049
1050 template <typename InputIterator, typename Projection>
1051 void assign_impl(InputIterator first, InputIterator last, T *capacityBegin, T *dst, T *dend,
1052 Projection proj, std::forward_iterator_tag)
1053 {
1054 constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>;
1055 const qsizetype n = std::distance(first, last);
1056 if constexpr (IsIdentity && !QTypeInfo<T>::isComplex) {
1057 // For non-complex types, we prefer a single std::copy() -> memcpy()
1058 // call. We can do that because either the default constructor is
1059 // trivial (so the lifetime has started) or the copy constructor is
1060 // (and won't care what the stored value is).
1061 std::copy(first, last, capacityBegin);
1062 } else {
1063 // There are two possibilities:
1064 // 1) fewer elements than the current allocated space
1065 // | prepend buffer | array | destroy |
1066 // 2) more elements than the current allocated space
1067 // | prepend buffer | array | construct |
1068 //
1069 // Both the prepend buffer and the current array may be empty.
1070
1071 // construct elements in the prepend buffer
1072 while (first != last && capacityBegin != dst) {
1073 q20::construct_at(capacityBegin, std::invoke(proj, *first));
1074 ++first;
1075 ++capacityBegin;
1076 }
1077
1078 // overwrite elements in the existing array
1079 while (first != last && dst != dend) {
1080 *dst = std::invoke(proj, *first); // overwrite existing element
1081 ++first;
1082 ++dst;
1083 }
1084
1085 // construct new elements in the append buffer
1086 while (first != last) {
1087 q20::construct_at(dst, std::invoke(proj, *first));
1088 ++first;
1089 ++dst;
1090 }
1091 // or destroy elements from the existing array
1092 if (dst < dend)
1093 std::destroy(dst, dend);
1094 }
1095 that()->size = n;
1096 }
1097};
1098
1099} // namespace QtPrivate
1100
1101template <class T>
1104{
1105private:
1106 using Base = QtPrivate::QCommonArrayOps<T>;
1107public:
1108 using Base::Base;
1109
1110 QArrayDataOps *operator->() noexcept { return this; }
1111 const QArrayDataOps *operator->() const noexcept { return this; }
1112};
1113
1114QT_END_NAMESPACE
1115
1116#endif // include guard
const QArrayDataOps * operator->() const noexcept
QArrayDataOps * operator->() noexcept
QGenericArrayOps< T > Type
const DataPointer * that() const
void appendUninitialized(qsizetype newSize)
void assign_impl(InputIterator first, InputIterator last, T *capacityBegin, T *dst, T *dend, Projection proj, std::input_iterator_tag)
QCommonArrayOps< T > Self
void assign_impl(InputIterator first, InputIterator last, T *capacityBegin, T *dst, T *dend, Projection proj, std::forward_iterator_tag)
void assign(InputIterator first, InputIterator last, Projection proj={})
void appendIteratorRange(It b, It e, QtPrivate::IfIsForwardIterator< It >=true)
void growAppend(const T *b, const T *e)
void truncate(size_t newSize)
void insert(qsizetype i, qsizetype n, parameter_type t)
QArrayDataPointer< T >::parameter_type parameter_type
QTypedArrayData< T > Data
void emplace(qsizetype i, Args &&... args)
void copyAppend(const T *b, const T *e)
const DataPointer * that() const
void assign(T *b, T *e, parameter_type t)
void erase(T *b, qsizetype n)
void insert(qsizetype i, const T *data, qsizetype n)
void copyAppend(qsizetype n, parameter_type t)
QGenericArrayOps(DataPointer &dp)
QArrayDataPointer< T > *const data
QTypedArrayData< T > Data
void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
QGenericArrayOps< T > Base
void insert(qsizetype i, const T *data, qsizetype n)
void emplace(qsizetype i, Args &&... args)
QGenericArrayOps< T >::parameter_type parameter_type
const DataPointer * that() const
void erase(T *b, qsizetype n)
void insert(qsizetype i, qsizetype n, parameter_type t)
QMovableArrayOps(DataPointer &dp)
void moveAppend(T *b, T *e) noexcept
void eraseFirst() noexcept
QArrayDataPointer< T >::parameter_type parameter_type
void copyRanges(std::initializer_list< Span > ranges)
void erase(T *b, qsizetype n)
void eraseLast() noexcept
T * createHole(QArrayData::GrowthPosition pos, qsizetype where, qsizetype n)
void insert(qsizetype i, const T *data, qsizetype n)
void truncate(size_t newSize) noexcept
void emplace(qsizetype i, Args &&... args)
qsizetype eraseIf(Predicate pred)
void assign(T *b, T *e, parameter_type t) noexcept
const DataPointer * that() const
void copyAppend(const T *b, const T *e) noexcept
void copyAppend(qsizetype n, parameter_type t) noexcept
void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
void insert(qsizetype i, qsizetype n, parameter_type t)
QPodArrayOps(DataPointer &dp)
QTypedArrayData< T > Data
void destroyAll() noexcept