Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-31 09:15:22

0001 // Copyright (C) 2020 The Qt Company Ltd.
0002 // Copyright (C) 2016 Intel Corporation.
0003 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0004 // Qt-Security score:significant reason:default
0005 
0006 #ifndef QARRAYDATAOPS_H
0007 #define QARRAYDATAOPS_H
0008 
0009 #include <QtCore/qarraydata.h>
0010 #include <QtCore/qcontainertools_impl.h>
0011 #include <QtCore/qnamespace.h>
0012 
0013 #include <QtCore/q20functional.h>
0014 #include <QtCore/q20memory.h>
0015 #include <new>
0016 #include <string.h>
0017 #include <utility>
0018 #include <iterator>
0019 #include <type_traits>
0020 
0021 QT_BEGIN_NAMESPACE
0022 
0023 template <class T> struct QArrayDataPointer;
0024 
0025 namespace QtPrivate {
0026 
0027 template <class T>
0028 struct QPodArrayOps
0029         : public QArrayDataPointer<T>
0030 {
0031     static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0032 
0033 protected:
0034     typedef QTypedArrayData<T> Data;
0035     using DataPointer = QArrayDataPointer<T>;
0036 
0037 public:
0038     typedef typename QArrayDataPointer<T>::parameter_type parameter_type;
0039 
0040     using QArrayDataPointer<T>::QArrayDataPointer;
0041 
0042     void copyAppend(const T *b, const T *e) noexcept
0043     {
0044         Q_ASSERT(this->isMutable() || b == e);
0045         Q_ASSERT(!this->isShared() || b == e);
0046         Q_ASSERT(b <= e);
0047         Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
0048 
0049         if (b == e)
0050             return;
0051 
0052         ::memcpy(static_cast<void *>(this->end()), static_cast<const void *>(b), (e - b) * sizeof(T));
0053         this->size += (e - b);
0054     }
0055 
0056     void copyAppend(qsizetype n, parameter_type t) noexcept
0057     {
0058         Q_ASSERT(!this->isShared() || n == 0);
0059         Q_ASSERT(this->freeSpaceAtEnd() >= n);
0060         if (!n)
0061             return;
0062 
0063         T *where = this->end();
0064         this->size += qsizetype(n);
0065         while (n--)
0066             *where++ = t;
0067     }
0068 
0069     void moveAppend(T *b, T *e) noexcept
0070     {
0071         copyAppend(b, e);
0072     }
0073 
0074     void truncate(size_t newSize) noexcept
0075     {
0076         Q_ASSERT(this->isMutable());
0077         Q_ASSERT(!this->isShared());
0078         Q_ASSERT(newSize <= size_t(this->size));
0079 
0080         this->size = qsizetype(newSize);
0081     }
0082 
0083     void destroyAll() noexcept // Call from destructors, ONLY!
0084     {
0085         Q_ASSERT(this->d);
0086         Q_ASSERT(this->d->ref_.loadRelaxed() == 0);
0087 
0088         // As this is to be called only from destructor, it doesn't need to be
0089         // exception safe; size not updated.
0090     }
0091 
0092     T *createHole(QArrayData::GrowthPosition pos, qsizetype where, qsizetype n)
0093     {
0094         Q_ASSERT((pos == QArrayData::GrowsAtBeginning && n <= this->freeSpaceAtBegin()) ||
0095                  (pos == QArrayData::GrowsAtEnd && n <= this->freeSpaceAtEnd()));
0096 
0097         T *insertionPoint = this->ptr + where;
0098         if (pos == QArrayData::GrowsAtEnd) {
0099             if (where < this->size)
0100                 ::memmove(static_cast<void *>(insertionPoint + n), static_cast<void *>(insertionPoint), (this->size - where) * sizeof(T));
0101         } else {
0102             Q_ASSERT(where == 0);
0103             this->ptr -= n;
0104             insertionPoint -= n;
0105         }
0106         this->size += n;
0107         return insertionPoint;
0108     }
0109 
0110     void insert(qsizetype i, const T *data, qsizetype n)
0111     {
0112         typename Data::GrowthPosition pos = Data::GrowsAtEnd;
0113         if (this->size != 0 && i == 0)
0114             pos = Data::GrowsAtBeginning;
0115 
0116         DataPointer oldData;
0117         this->detachAndGrow(pos, n, &data, &oldData);
0118         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0119                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0120 
0121         T *where = createHole(pos, i, n);
0122         ::memcpy(static_cast<void *>(where), static_cast<const void *>(data), n * sizeof(T));
0123     }
0124 
0125     void insert(qsizetype i, qsizetype n, parameter_type t)
0126     {
0127         T copy(t);
0128 
0129         typename Data::GrowthPosition pos = Data::GrowsAtEnd;
0130         if (this->size != 0 && i == 0)
0131             pos = Data::GrowsAtBeginning;
0132 
0133         this->detachAndGrow(pos, n, nullptr, nullptr);
0134         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0135                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0136 
0137         T *where = createHole(pos, i, n);
0138         while (n--)
0139             *where++ = copy;
0140     }
0141 
0142     template<typename... Args>
0143     void emplace(qsizetype i, Args &&... args)
0144     {
0145         bool detach = this->needsDetach();
0146         if (!detach) {
0147             if (i == this->size && this->freeSpaceAtEnd()) {
0148                 new (this->end()) T(std::forward<Args>(args)...);
0149                 ++this->size;
0150                 return;
0151             }
0152             if (i == 0 && this->freeSpaceAtBegin()) {
0153                 new (this->begin() - 1) T(std::forward<Args>(args)...);
0154                 --this->ptr;
0155                 ++this->size;
0156                 return;
0157             }
0158         }
0159         T tmp(std::forward<Args>(args)...);
0160         typename QArrayData::GrowthPosition pos = QArrayData::GrowsAtEnd;
0161         if (this->size != 0 && i == 0)
0162             pos = QArrayData::GrowsAtBeginning;
0163 
0164         this->detachAndGrow(pos, 1, nullptr, nullptr);
0165 
0166         T *where = createHole(pos, i, 1);
0167         new (where) T(std::move(tmp));
0168     }
0169 
0170     void erase(T *b, qsizetype n)
0171     {
0172         T *e = b + n;
0173         Q_ASSERT(this->isMutable());
0174         Q_ASSERT(b < e);
0175         Q_ASSERT(b >= this->begin() && b < this->end());
0176         Q_ASSERT(e > this->begin() && e <= this->end());
0177 
0178         // Comply with std::vector::erase(): erased elements and all after them
0179         // are invalidated. However, erasing from the beginning effectively
0180         // means that all iterators are invalidated. We can use this freedom to
0181         // erase by moving towards the end.
0182         if (b == this->begin() && e != this->end()) {
0183             this->ptr = e;
0184         } else if (e != this->end()) {
0185             ::memmove(static_cast<void *>(b), static_cast<void *>(e),
0186                       (static_cast<T *>(this->end()) - e) * sizeof(T));
0187         }
0188         this->size -= n;
0189     }
0190 
0191     void eraseFirst() noexcept
0192     {
0193         Q_ASSERT(this->isMutable());
0194         Q_ASSERT(this->size);
0195         ++this->ptr;
0196         --this->size;
0197     }
0198 
0199     void eraseLast() noexcept
0200     {
0201         Q_ASSERT(this->isMutable());
0202         Q_ASSERT(this->size);
0203         --this->size;
0204     }
0205 
0206     template <typename Predicate>
0207     qsizetype eraseIf(Predicate pred)
0208     {
0209         qsizetype result = 0;
0210         if (this->size == 0)
0211             return result;
0212 
0213         if (!this->needsDetach()) {
0214             auto end = this->end();
0215             auto it = std::remove_if(this->begin(), end, pred);
0216             if (it != end) {
0217                 result = std::distance(it, end);
0218                 erase(it, result);
0219             }
0220         } else {
0221             const auto begin = this->begin();
0222             const auto end = this->end();
0223             auto it = std::find_if(begin, end, pred);
0224             if (it == end)
0225                 return result;
0226 
0227             QPodArrayOps<T> other(this->size);
0228             Q_CHECK_PTR(other.data());
0229             auto dest = other.begin();
0230             // std::uninitialized_copy will fallback to ::memcpy/memmove()
0231             dest = std::uninitialized_copy(begin, it, dest);
0232             dest = q_uninitialized_remove_copy_if(std::next(it), end, dest, pred);
0233             other.size = std::distance(other.data(), dest);
0234             result = this->size - other.size;
0235             this->swap(other);
0236         }
0237         return result;
0238     }
0239 
0240     struct Span { T *begin; T *end; };
0241 
0242     void copyRanges(std::initializer_list<Span> ranges)
0243     {
0244         auto it = this->begin();
0245         std::for_each(ranges.begin(), ranges.end(), [&it](const auto &span) {
0246             it = std::copy(span.begin, span.end, it);
0247         });
0248         this->size = std::distance(this->begin(), it);
0249     }
0250 
0251     void assign(T *b, T *e, parameter_type t) noexcept
0252     {
0253         Q_ASSERT(b <= e);
0254         Q_ASSERT(b >= this->begin() && e <= this->end());
0255 
0256         while (b != e)
0257             ::memcpy(static_cast<void *>(b++), static_cast<const void *>(&t), sizeof(T));
0258     }
0259 
0260     void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
0261     {
0262         auto pair = Data::reallocateUnaligned(this->d, this->ptr, alloc, option);
0263         Q_CHECK_PTR(pair.second);
0264         Q_ASSERT(pair.first != nullptr);
0265         this->d = pair.first;
0266         this->ptr = pair.second;
0267     }
0268 };
0269 
0270 template <class T>
0271 struct QGenericArrayOps
0272         : public QArrayDataPointer<T>
0273 {
0274     static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0275 
0276 protected:
0277     typedef QTypedArrayData<T> Data;
0278     using DataPointer = QArrayDataPointer<T>;
0279 
0280 public:
0281     typedef typename QArrayDataPointer<T>::parameter_type parameter_type;
0282 
0283     void copyAppend(const T *b, const T *e)
0284     {
0285         Q_ASSERT(this->isMutable() || b == e);
0286         Q_ASSERT(!this->isShared() || b == e);
0287         Q_ASSERT(b <= e);
0288         Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
0289 
0290         if (b == e) // short-cut and handling the case b and e == nullptr
0291             return;
0292 
0293         T *data = this->begin();
0294         while (b < e) {
0295             new (data + this->size) T(*b);
0296             ++b;
0297             ++this->size;
0298         }
0299     }
0300 
0301     void copyAppend(qsizetype n, parameter_type t)
0302     {
0303         Q_ASSERT(!this->isShared() || n == 0);
0304         Q_ASSERT(this->freeSpaceAtEnd() >= n);
0305         if (!n)
0306             return;
0307 
0308         T *data = this->begin();
0309         while (n--) {
0310             new (data + this->size) T(t);
0311             ++this->size;
0312         }
0313     }
0314 
0315     void moveAppend(T *b, T *e)
0316     {
0317         Q_ASSERT(this->isMutable() || b == e);
0318         Q_ASSERT(!this->isShared() || b == e);
0319         Q_ASSERT(b <= e);
0320         Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
0321 
0322         if (b == e)
0323             return;
0324 
0325         T *data = this->begin();
0326         while (b < e) {
0327             new (data + this->size) T(std::move(*b));
0328             ++b;
0329             ++this->size;
0330         }
0331     }
0332 
0333     void truncate(size_t newSize)
0334     {
0335         Q_ASSERT(this->isMutable());
0336         Q_ASSERT(!this->isShared());
0337         Q_ASSERT(newSize <= size_t(this->size));
0338 
0339         std::destroy(this->begin() + newSize, this->end());
0340         this->size = newSize;
0341     }
0342 
0343     void destroyAll() // Call from destructors, ONLY
0344     {
0345         Q_ASSERT(this->d);
0346         // As this is to be called only from destructor, it doesn't need to be
0347         // exception safe; size not updated.
0348 
0349         Q_ASSERT(this->d->ref_.loadRelaxed() == 0);
0350 
0351         std::destroy(this->begin(), this->end());
0352     }
0353 
0354     struct Inserter
0355     {
0356         QArrayDataPointer<T> *data;
0357         T *begin;
0358         qsizetype size;
0359 
0360         qsizetype sourceCopyConstruct = 0, nSource = 0, move = 0, sourceCopyAssign = 0;
0361         T *end = nullptr, *last = nullptr, *where = nullptr;
0362 
0363         Inserter(QArrayDataPointer<T> *d) : data(d)
0364         {
0365             begin = d->ptr;
0366             size = d->size;
0367         }
0368         ~Inserter() {
0369             data->ptr = begin;
0370             data->size = size;
0371         }
0372         Q_DISABLE_COPY(Inserter)
0373 
0374         void setup(qsizetype pos, qsizetype n)
0375         {
0376             end = begin + size;
0377             last = end - 1;
0378             where = begin + pos;
0379             qsizetype dist = size - pos;
0380             sourceCopyConstruct = 0;
0381             nSource = n;
0382             move = n - dist; // smaller 0
0383             sourceCopyAssign = n;
0384             if (n > dist) {
0385                 sourceCopyConstruct = n - dist;
0386                 move = 0;
0387                 sourceCopyAssign -= sourceCopyConstruct;
0388             }
0389         }
0390 
0391         void insert(qsizetype pos, const T *source, qsizetype n)
0392         {
0393             qsizetype oldSize = size;
0394             Q_UNUSED(oldSize);
0395 
0396             setup(pos, n);
0397 
0398             // first create new elements at the end, by copying from elements
0399             // to be inserted (if they extend past the current end of the array)
0400             for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
0401                 new (end + i) T(source[nSource - sourceCopyConstruct + i]);
0402                 ++size;
0403             }
0404             Q_ASSERT(size <= oldSize + n);
0405 
0406             // now move construct new elements at the end from existing elements inside
0407             // the array.
0408             for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
0409                 new (end + i) T(std::move(*(end + i - nSource)));
0410                 ++size;
0411             }
0412             // array has the new size now!
0413             Q_ASSERT(size == oldSize + n);
0414 
0415             // now move assign existing elements towards the end
0416             for (qsizetype i = 0; i != move; --i)
0417                 last[i] = std::move(last[i - nSource]);
0418 
0419             // finally copy the remaining elements from source over
0420             for (qsizetype i = 0; i != sourceCopyAssign; ++i)
0421                 where[i] = source[i];
0422         }
0423 
0424         void insert(qsizetype pos, const T &t, qsizetype n)
0425         {
0426             const qsizetype oldSize = size;
0427             Q_UNUSED(oldSize);
0428 
0429             setup(pos, n);
0430 
0431             // first create new elements at the end, by copying from elements
0432             // to be inserted (if they extend past the current end of the array)
0433             for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
0434                 new (end + i) T(t);
0435                 ++size;
0436             }
0437             Q_ASSERT(size <= oldSize + n);
0438 
0439             // now move construct new elements at the end from existing elements inside
0440             // the array.
0441             for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
0442                 new (end + i) T(std::move(*(end + i - nSource)));
0443                 ++size;
0444             }
0445             // array has the new size now!
0446             Q_ASSERT(size == oldSize + n);
0447 
0448             // now move assign existing elements towards the end
0449             for (qsizetype i = 0; i != move; --i)
0450                 last[i] = std::move(last[i - nSource]);
0451 
0452             // finally copy the remaining elements from source over
0453             for (qsizetype i = 0; i != sourceCopyAssign; ++i)
0454                 where[i] = t;
0455         }
0456 
0457         void insertOne(qsizetype pos, T &&t)
0458         {
0459             setup(pos, 1);
0460 
0461             if (sourceCopyConstruct) {
0462                 Q_ASSERT(sourceCopyConstruct == 1);
0463                 new (end) T(std::move(t));
0464                 ++size;
0465             } else {
0466                 // create a new element at the end by move constructing one existing element
0467                 // inside the array.
0468                 new (end) T(std::move(*(end - 1)));
0469                 ++size;
0470 
0471                 // now move assign existing elements towards the end
0472                 for (qsizetype i = 0; i != move; --i)
0473                     last[i] = std::move(last[i - 1]);
0474 
0475                 // and move the new item into place
0476                 *where = std::move(t);
0477             }
0478         }
0479     };
0480 
0481     void insert(qsizetype i, const T *data, qsizetype n)
0482     {
0483         const bool growsAtBegin = this->size != 0 && i == 0;
0484         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0485 
0486         DataPointer oldData;
0487         this->detachAndGrow(pos, n, &data, &oldData);
0488         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0489                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0490 
0491         if (growsAtBegin) {
0492             // copy construct items in reverse order at the begin
0493             Q_ASSERT(this->freeSpaceAtBegin() >= n);
0494             while (n) {
0495                 --n;
0496                 new (this->begin() - 1) T(data[n]);
0497                 --this->ptr;
0498                 ++this->size;
0499             }
0500         } else {
0501             Inserter(this).insert(i, data, n);
0502         }
0503     }
0504 
0505     void insert(qsizetype i, qsizetype n, parameter_type t)
0506     {
0507         T copy(t);
0508 
0509         const bool growsAtBegin = this->size != 0 && i == 0;
0510         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0511 
0512         this->detachAndGrow(pos, n, nullptr, nullptr);
0513         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0514                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0515 
0516         if (growsAtBegin) {
0517             // copy construct items in reverse order at the begin
0518             Q_ASSERT(this->freeSpaceAtBegin() >= n);
0519             while (n--) {
0520                 new (this->begin() - 1) T(copy);
0521                 --this->ptr;
0522                 ++this->size;
0523             }
0524         } else {
0525             Inserter(this).insert(i, copy, n);
0526         }
0527     }
0528 
0529     template<typename... Args>
0530     void emplace(qsizetype i, Args &&... args)
0531     {
0532         bool detach = this->needsDetach();
0533         if (!detach) {
0534             if (i == this->size && this->freeSpaceAtEnd()) {
0535                 new (this->end()) T(std::forward<Args>(args)...);
0536                 ++this->size;
0537                 return;
0538             }
0539             if (i == 0 && this->freeSpaceAtBegin()) {
0540                 new (this->begin() - 1) T(std::forward<Args>(args)...);
0541                 --this->ptr;
0542                 ++this->size;
0543                 return;
0544             }
0545         }
0546         T tmp(std::forward<Args>(args)...);
0547         const bool growsAtBegin = this->size != 0 && i == 0;
0548         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0549 
0550         this->detachAndGrow(pos, 1, nullptr, nullptr);
0551 
0552         if (growsAtBegin) {
0553             Q_ASSERT(this->freeSpaceAtBegin());
0554             new (this->begin() - 1) T(std::move(tmp));
0555             --this->ptr;
0556             ++this->size;
0557         } else {
0558             Inserter(this).insertOne(i, std::move(tmp));
0559         }
0560     }
0561 
0562     void erase(T *b, qsizetype n)
0563     {
0564         T *e = b + n;
0565         Q_ASSERT(this->isMutable());
0566         Q_ASSERT(b < e);
0567         Q_ASSERT(b >= this->begin() && b < this->end());
0568         Q_ASSERT(e > this->begin() && e <= this->end());
0569 
0570         // Comply with std::vector::erase(): erased elements and all after them
0571         // are invalidated. However, erasing from the beginning effectively
0572         // means that all iterators are invalidated. We can use this freedom to
0573         // erase by moving towards the end.
0574         if (b == this->begin() && e != this->end()) {
0575             this->ptr = e;
0576         } else {
0577             const T *const end = this->end();
0578 
0579             // move (by assignment) the elements from e to end
0580             // onto b to the new end
0581             while (e != end) {
0582                 *b = std::move(*e);
0583                 ++b;
0584                 ++e;
0585             }
0586         }
0587         this->size -= n;
0588         std::destroy(b, e);
0589     }
0590 
0591     void eraseFirst() noexcept
0592     {
0593         Q_ASSERT(this->isMutable());
0594         Q_ASSERT(this->size);
0595         this->begin()->~T();
0596         ++this->ptr;
0597         --this->size;
0598     }
0599 
0600     void eraseLast() noexcept
0601     {
0602         Q_ASSERT(this->isMutable());
0603         Q_ASSERT(this->size);
0604         (this->end() - 1)->~T();
0605         --this->size;
0606     }
0607 
0608 
0609     void assign(T *b, T *e, parameter_type t)
0610     {
0611         Q_ASSERT(b <= e);
0612         Q_ASSERT(b >= this->begin() && e <= this->end());
0613 
0614         while (b != e)
0615             *b++ = t;
0616     }
0617 };
0618 
0619 template <class T>
0620 struct QMovableArrayOps
0621     : QGenericArrayOps<T>
0622 {
0623     static_assert (std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0624 
0625 protected:
0626     typedef QTypedArrayData<T> Data;
0627     using DataPointer = QArrayDataPointer<T>;
0628 
0629 public:
0630     // using QGenericArrayOps<T>::copyAppend;
0631     // using QGenericArrayOps<T>::moveAppend;
0632     // using QGenericArrayOps<T>::truncate;
0633     // using QGenericArrayOps<T>::destroyAll;
0634     typedef typename QGenericArrayOps<T>::parameter_type parameter_type;
0635 
0636     struct Inserter
0637     {
0638         QArrayDataPointer<T> * const data;
0639         T *displaceFrom;
0640         T * const displaceTo;
0641         const qsizetype nInserts = 0;
0642         const size_t bytes;
0643 
0644         void verifyPost()
0645         { Q_ASSERT(displaceFrom == displaceTo); }
0646 
0647         explicit Inserter(QArrayDataPointer<T> *d, qsizetype pos, qsizetype n)
0648             : data{d},
0649               displaceFrom{d->ptr + pos},
0650               displaceTo{displaceFrom + n},
0651               nInserts{n},
0652               bytes{(data->size - pos) * sizeof(T)}
0653         {
0654             ::memmove(static_cast<void *>(displaceTo), static_cast<void *>(displaceFrom), bytes);
0655         }
0656         ~Inserter() {
0657             auto inserts = nInserts;
0658             if constexpr (!std::is_nothrow_copy_constructible_v<T>) {
0659                 if (displaceFrom != displaceTo) {
0660                     ::memmove(static_cast<void *>(displaceFrom), static_cast<void *>(displaceTo), bytes);
0661                     inserts -= qAbs(displaceFrom - displaceTo);
0662                 }
0663             }
0664             data->size += inserts;
0665         }
0666         Q_DISABLE_COPY(Inserter)
0667 
0668         void insertRange(const T *source, qsizetype n)
0669         {
0670             while (n--) {
0671                 new (displaceFrom) T(*source);
0672                 ++source;
0673                 ++displaceFrom;
0674             }
0675             verifyPost();
0676         }
0677 
0678         void insertFill(const T &t, qsizetype n)
0679         {
0680             while (n--) {
0681                 new (displaceFrom) T(t);
0682                 ++displaceFrom;
0683             }
0684             verifyPost();
0685         }
0686 
0687         void insertOne(T &&t)
0688         {
0689             new (displaceFrom) T(std::move(t));
0690             ++displaceFrom;
0691             verifyPost();
0692         }
0693 
0694     };
0695 
0696 
0697     void insert(qsizetype i, const T *data, qsizetype n)
0698     {
0699         const bool growsAtBegin = this->size != 0 && i == 0;
0700         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0701 
0702         DataPointer oldData;
0703         this->detachAndGrow(pos, n, &data, &oldData);
0704         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0705                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0706 
0707         if (growsAtBegin) {
0708             // copy construct items in reverse order at the begin
0709             Q_ASSERT(this->freeSpaceAtBegin() >= n);
0710             while (n) {
0711                 --n;
0712                 new (this->begin() - 1) T(data[n]);
0713                 --this->ptr;
0714                 ++this->size;
0715             }
0716         } else {
0717             Inserter(this, i, n).insertRange(data, n);
0718         }
0719     }
0720 
0721     void insert(qsizetype i, qsizetype n, parameter_type t)
0722     {
0723         T copy(t);
0724 
0725         const bool growsAtBegin = this->size != 0 && i == 0;
0726         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0727 
0728         this->detachAndGrow(pos, n, nullptr, nullptr);
0729         Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) ||
0730                  (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n));
0731 
0732         if (growsAtBegin) {
0733             // copy construct items in reverse order at the begin
0734             Q_ASSERT(this->freeSpaceAtBegin() >= n);
0735             while (n--) {
0736                 new (this->begin() - 1) T(copy);
0737                 --this->ptr;
0738                 ++this->size;
0739             }
0740         } else {
0741             Inserter(this, i, n).insertFill(copy, n);
0742         }
0743     }
0744 
0745     template<typename... Args>
0746     void emplace(qsizetype i, Args &&... args)
0747     {
0748         bool detach = this->needsDetach();
0749         if (!detach) {
0750             if (i == this->size && this->freeSpaceAtEnd()) {
0751                 new (this->end()) T(std::forward<Args>(args)...);
0752                 ++this->size;
0753                 return;
0754             }
0755             if (i == 0 && this->freeSpaceAtBegin()) {
0756                 new (this->begin() - 1) T(std::forward<Args>(args)...);
0757                 --this->ptr;
0758                 ++this->size;
0759                 return;
0760             }
0761         }
0762         T tmp(std::forward<Args>(args)...);
0763         const bool growsAtBegin = this->size != 0 && i == 0;
0764         const auto pos = growsAtBegin ? Data::GrowsAtBeginning : Data::GrowsAtEnd;
0765 
0766         this->detachAndGrow(pos, 1, nullptr, nullptr);
0767         if (growsAtBegin) {
0768             Q_ASSERT(this->freeSpaceAtBegin());
0769             new (this->begin() - 1) T(std::move(tmp));
0770             --this->ptr;
0771             ++this->size;
0772         } else {
0773             Inserter(this, i, 1).insertOne(std::move(tmp));
0774         }
0775     }
0776 
0777     void erase(T *b, qsizetype n)
0778     {
0779         T *e = b + n;
0780 
0781         Q_ASSERT(this->isMutable());
0782         Q_ASSERT(b < e);
0783         Q_ASSERT(b >= this->begin() && b < this->end());
0784         Q_ASSERT(e > this->begin() && e <= this->end());
0785 
0786         // Comply with std::vector::erase(): erased elements and all after them
0787         // are invalidated. However, erasing from the beginning effectively
0788         // means that all iterators are invalidated. We can use this freedom to
0789         // erase by moving towards the end.
0790 
0791         std::destroy(b, e);
0792         if (b == this->begin() && e != this->end()) {
0793             this->ptr = e;
0794         } else if (e != this->end()) {
0795             memmove(static_cast<void *>(b), static_cast<const void *>(e), (static_cast<const T *>(this->end()) - e)*sizeof(T));
0796         }
0797         this->size -= n;
0798     }
0799 
0800     void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
0801     {
0802         auto pair = Data::reallocateUnaligned(this->d, this->ptr, alloc, option);
0803         Q_CHECK_PTR(pair.second);
0804         Q_ASSERT(pair.first != nullptr);
0805         this->d = pair.first;
0806         this->ptr = pair.second;
0807     }
0808 };
0809 
0810 template <class T, class = void>
0811 struct QArrayOpsSelector
0812 {
0813     typedef QGenericArrayOps<T> Type;
0814 };
0815 
0816 template <class T>
0817 struct QArrayOpsSelector<T,
0818     typename std::enable_if<
0819         !QTypeInfo<T>::isComplex && QTypeInfo<T>::isRelocatable
0820     >::type>
0821 {
0822     typedef QPodArrayOps<T> Type;
0823 };
0824 
0825 template <class T>
0826 struct QArrayOpsSelector<T,
0827     typename std::enable_if<
0828         QTypeInfo<T>::isComplex && QTypeInfo<T>::isRelocatable
0829     >::type>
0830 {
0831     typedef QMovableArrayOps<T> Type;
0832 };
0833 
0834 template <class T>
0835 struct QCommonArrayOps : QArrayOpsSelector<T>::Type
0836 {
0837     using Base = typename QArrayOpsSelector<T>::Type;
0838     using Data = QTypedArrayData<T>;
0839     using DataPointer = QArrayDataPointer<T>;
0840     using parameter_type = typename Base::parameter_type;
0841 
0842 protected:
0843     using Self = QCommonArrayOps<T>;
0844 
0845 public:
0846     // using Base::truncate;
0847     // using Base::destroyAll;
0848 
0849     template<typename It>
0850     void appendIteratorRange(It b, It e, QtPrivate::IfIsForwardIterator<It> = true)
0851     {
0852         Q_ASSERT(this->isMutable() || b == e);
0853         Q_ASSERT(!this->isShared() || b == e);
0854         const qsizetype distance = std::distance(b, e);
0855         Q_ASSERT(distance >= 0 && distance <= this->allocatedCapacity() - this->size);
0856         Q_UNUSED(distance);
0857 
0858 #if __cplusplus >= 202002L && defined(__cpp_concepts) && defined(__cpp_lib_concepts)
0859         constexpr bool canUseCopyAppend =
0860                 std::contiguous_iterator<It> &&
0861                 std::is_same_v<
0862                     std::remove_cv_t<typename std::iterator_traits<It>::value_type>,
0863                     T
0864                 >;
0865         if constexpr (canUseCopyAppend) {
0866             this->copyAppend(std::to_address(b), std::to_address(e));
0867         } else
0868 #endif
0869         {
0870             T *iter = this->end();
0871             for (; b != e; ++iter, ++b) {
0872                 new (iter) T(*b);
0873                 ++this->size;
0874             }
0875         }
0876     }
0877 
0878     // slightly higher level API than copyAppend() that also preallocates space
0879     void growAppend(const T *b, const T *e)
0880     {
0881         if (b == e)
0882             return;
0883         Q_ASSERT(b < e);
0884         const qsizetype n = e - b;
0885         DataPointer old;
0886 
0887         // points into range:
0888         if (QtPrivate::q_points_into_range(b, *this))
0889             this->detachAndGrow(QArrayData::GrowsAtEnd, n, &b, &old);
0890         else
0891             this->detachAndGrow(QArrayData::GrowsAtEnd, n, nullptr, nullptr);
0892         Q_ASSERT(this->freeSpaceAtEnd() >= n);
0893         // b might be updated so use [b, n)
0894         this->copyAppend(b, b + n);
0895     }
0896 
0897     void appendUninitialized(qsizetype newSize)
0898     {
0899         Q_ASSERT(this->isMutable());
0900         Q_ASSERT(!this->isShared());
0901         Q_ASSERT(newSize > this->size);
0902         Q_ASSERT(newSize - this->size <= this->freeSpaceAtEnd());
0903 
0904 
0905         T *const b = this->begin() + this->size;
0906         T *const e = this->begin() + newSize;
0907         if constexpr (std::is_constructible_v<T, Qt::Initialization>)
0908             std::uninitialized_fill(b, e, Qt::Uninitialized);
0909         else
0910             std::uninitialized_default_construct(b, e);
0911         this->size = newSize;
0912     }
0913 
0914     using Base::assign;
0915 
0916     template <typename InputIterator, typename Projection = q20::identity>
0917     void assign(InputIterator first, InputIterator last, Projection proj = {})
0918     {
0919         // This function only provides the basic exception guarantee.
0920         using Category = typename std::iterator_traits<InputIterator>::iterator_category;
0921         constexpr bool IsFwdIt = std::is_convertible_v<Category, std::forward_iterator_tag>;
0922 
0923         const qsizetype n = IsFwdIt ? std::distance(first, last) : 0;
0924         bool undoPrependOptimization = true;
0925         bool needCapacity = n > this->constAllocatedCapacity();
0926         if (needCapacity || this->needsDetach()) {
0927             qsizetype newCapacity = this->detachCapacity(n);
0928             bool wasLastRef = !this->deref();
0929             if (wasLastRef && needCapacity) {
0930                 // free memory we can't reuse
0931                 this->destroyAll();
0932                 Data::deallocate(this->d);
0933             }
0934             if (!needCapacity && wasLastRef) {
0935                 // we were the last reference and can reuse the storage
0936                 this->d->ref_.storeRelaxed(1);
0937             } else {
0938                 // we must allocate new memory
0939                 std::tie(this->d, this->ptr) = Data::allocate(newCapacity);
0940                 this->size = 0;
0941                 undoPrependOptimization = false;
0942             }
0943         }
0944 
0945         if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>
0946                       || !std::is_nothrow_invocable_v<Projection, decltype(*first)>)
0947         {
0948             // If construction can throw, and we have freeSpaceAtBegin(),
0949             // it's easiest to just clear the container and start fresh.
0950             // The alternative would be to keep track of two active, disjoint ranges.
0951             if (undoPrependOptimization) {
0952                 this->truncate(0);
0953                 this->setBegin(Data::dataStart(this->d, alignof(typename Data::AlignmentDummy)));
0954                 undoPrependOptimization = false;
0955             }
0956         }
0957 
0958         const auto dend = this->end();
0959         T *dst = this->begin();
0960         T *capacityBegin = dst;
0961         if (undoPrependOptimization) {
0962             capacityBegin = Data::dataStart(this->d, alignof(typename Data::AlignmentDummy));
0963             this->setBegin(capacityBegin); // undo prepend optimization
0964         }
0965 
0966         assign_impl(first, last, capacityBegin, dst, dend, proj, Category{});
0967     }
0968 
0969     template <typename InputIterator, typename Projection>
0970     void assign_impl(InputIterator first, InputIterator last, T *capacityBegin, T *dst, T *dend,
0971                      Projection proj, std::input_iterator_tag)
0972     {
0973         if (qsizetype offset = dst - capacityBegin) {
0974             T *prependBufferEnd = dst;
0975             dst = capacityBegin;
0976 
0977             // By construction, the following loop is nothrow!
0978             // (otherwise, we can't reach here)
0979             // Assumes InputIterator operations don't throw.
0980             // (but we can't statically assert that, as these operations
0981             //  have preconditons, so typically aren't noexcept)
0982             while (true) {
0983                 if (dst == prependBufferEnd) {  // ran out of prepend buffer space
0984                     this->size += offset;
0985                     // we now have a contiguous buffer, continue with the main loop:
0986                     break;
0987                 }
0988                 if (first == last) {            // ran out of elements to assign
0989                     std::destroy(prependBufferEnd, dend);
0990                     this->size = dst - this->begin();
0991                     return;
0992                 }
0993                 // construct element in prepend buffer
0994                 q20::construct_at(dst, std::invoke(proj, *first));
0995                 ++dst;
0996                 ++first;
0997             }
0998         }
0999         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 
1068 template <class T>
1069 struct QArrayDataOps
1070     : QtPrivate::QCommonArrayOps<T>
1071 {
1072 };
1073 
1074 QT_END_NAMESPACE
1075 
1076 #endif // include guard