Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:07:16

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