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