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 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 
0004 #ifndef QARRAYDATAPOINTER_H
0005 #define QARRAYDATAPOINTER_H
0006 
0007 #include <QtCore/qarraydataops.h>
0008 #include <QtCore/qcontainertools_impl.h>
0009 
0010 #include <QtCore/q20functional.h>
0011 #include <QtCore/q20memory.h>
0012 
0013 QT_BEGIN_NAMESPACE
0014 
0015 template <class T>
0016 struct QArrayDataPointer
0017 {
0018 private:
0019     typedef QTypedArrayData<T> Data;
0020     typedef QArrayDataOps<T> DataOps;
0021 
0022 public:
0023     enum {
0024         pass_parameter_by_value =
0025                 std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_enum<T>::value
0026     };
0027 
0028     typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
0029 
0030     Q_NODISCARD_CTOR
0031     constexpr QArrayDataPointer() noexcept
0032         : d(nullptr), ptr(nullptr), size(0)
0033     {
0034     }
0035 
0036     Q_NODISCARD_CTOR
0037     QArrayDataPointer(const QArrayDataPointer &other) noexcept
0038         : d(other.d), ptr(other.ptr), size(other.size)
0039     {
0040         ref();
0041     }
0042 
0043     Q_NODISCARD_CTOR
0044     constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n = 0) noexcept
0045         : d(header), ptr(adata), size(n)
0046     {
0047     }
0048 
0049     Q_NODISCARD_CTOR
0050     explicit QArrayDataPointer(std::pair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
0051         : d(adata.first), ptr(adata.second), size(n)
0052     {
0053     }
0054 
0055     Q_NODISCARD_CTOR explicit
0056     QArrayDataPointer(qsizetype alloc, qsizetype n = 0,
0057                       QArrayData::AllocationOption option = QArrayData::KeepSize)
0058         : QArrayDataPointer(Data::allocate(alloc, option), n)
0059     {
0060     }
0061 
0062     Q_NODISCARD_CTOR
0063     static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
0064     {
0065         Q_ASSERT(rawData || !length);
0066         return { nullptr, const_cast<T *>(rawData), length };
0067     }
0068 
0069     QArrayDataPointer &operator=(const QArrayDataPointer &other) noexcept
0070     {
0071         QArrayDataPointer tmp(other);
0072         this->swap(tmp);
0073         return *this;
0074     }
0075 
0076     Q_NODISCARD_CTOR
0077     QArrayDataPointer(QArrayDataPointer &&other) noexcept
0078         : d(std::exchange(other.d, nullptr)),
0079           ptr(std::exchange(other.ptr, nullptr)),
0080           size(std::exchange(other.size, 0))
0081     {
0082     }
0083 
0084     QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
0085 
0086     DataOps &operator*() noexcept
0087     {
0088         return *static_cast<DataOps *>(this);
0089     }
0090 
0091     DataOps *operator->() noexcept
0092     {
0093         return static_cast<DataOps *>(this);
0094     }
0095 
0096     const DataOps &operator*() const noexcept
0097     {
0098         return *static_cast<const DataOps *>(this);
0099     }
0100 
0101     const DataOps *operator->() const noexcept
0102     {
0103         return static_cast<const DataOps *>(this);
0104     }
0105 
0106     ~QArrayDataPointer()
0107     {
0108         if (!deref()) {
0109             (*this)->destroyAll();
0110             free(d);
0111         }
0112     }
0113 
0114     bool isNull() const noexcept
0115     {
0116         return !ptr;
0117     }
0118 
0119     T *data() noexcept { return ptr; }
0120     const T *data() const noexcept { return ptr; }
0121 
0122     T *begin() noexcept { return data(); }
0123     T *end() noexcept { return data() + size; }
0124     const T *begin() const noexcept { return data(); }
0125     const T *end() const noexcept { return data() + size; }
0126     const T *constBegin() const noexcept { return data(); }
0127     const T *constEnd() const noexcept { return data() + size; }
0128 
0129     void swap(QArrayDataPointer &other) noexcept
0130     {
0131         qt_ptr_swap(d, other.d);
0132         qt_ptr_swap(ptr, other.ptr);
0133         std::swap(size, other.size);
0134     }
0135 
0136     void clear() noexcept(std::is_nothrow_destructible<T>::value)
0137     {
0138         QArrayDataPointer tmp;
0139         swap(tmp);
0140     }
0141 
0142     void detach(QArrayDataPointer *old = nullptr)
0143     {
0144         if (needsDetach())
0145             reallocateAndGrow(QArrayData::GrowsAtEnd, 0, old);
0146     }
0147 
0148     /*! \internal
0149 
0150         Reinterprets the data of this QArrayDataPointer to type X. It's the
0151         caller's responsibility to ensure that the data contents are valid and
0152         properly aligned, particularly if T and X are not trivial types (i.e,
0153         don't do that). The current size is kept and the allocated capacity is
0154         updated to account for the difference in the element type's size.
0155 
0156         This is used in QString::fromLatin1 to perform in-place conversion of
0157         QString to QByteArray.
0158     */
0159     template <typename X> QArrayDataPointer<X> reinterpreted() &&
0160     {
0161         if (sizeof(T) != sizeof(X)) {
0162             Q_ASSERT(!d->isShared());
0163             d->alloc = d->alloc * sizeof(T) / sizeof(X);
0164         }
0165         auto od = reinterpret_cast<QTypedArrayData<X> *>(std::exchange(d, nullptr));
0166         auto optr = reinterpret_cast<X *>(std::exchange(ptr, nullptr));
0167         return { od, optr, std::exchange(size, 0) };
0168     }
0169 
0170     /*! \internal
0171 
0172         Detaches this (optionally) and grows to accommodate the free space for
0173         \a n elements at the required side. The side is determined from \a pos.
0174 
0175         \a data pointer can be provided when the caller knows that \a data
0176         points into range [this->begin(), this->end()). In case it is, *data
0177         would be updated so that it continues to point to the element it was
0178         pointing to before the data move. if \a data does not point into range,
0179         one can/should pass \c nullptr.
0180 
0181         Similarly to \a data, \a old, pointer to a default-constructed QADP, can
0182         be provided when the caller expects to e.g. copy the data from this to
0183         itself:
0184         \code
0185         QList<T> list(5);
0186         qsizetype pos = getArbitraryPos();
0187         list.insert(pos, list.begin(), list.end());
0188         \endcode
0189 
0190         The default rule would be: \a data and \a old must either both be valid
0191         pointers, or both equal to \c nullptr.
0192     */
0193     void detachAndGrow(QArrayData::GrowthPosition where, qsizetype n, const T **data,
0194                        QArrayDataPointer *old)
0195     {
0196         const bool detach = needsDetach();
0197         bool readjusted = false;
0198         if (!detach) {
0199             if (!n || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
0200                 || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n))
0201                 return;
0202             readjusted = tryReadjustFreeSpace(where, n, data);
0203             Q_ASSERT(!readjusted
0204                      || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
0205                      || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n));
0206         }
0207 
0208         if (!readjusted)
0209             reallocateAndGrow(where, n, old);
0210     }
0211 
0212     /*! \internal
0213 
0214         Reallocates to accommodate the free space for \a n elements at the
0215         required side. The side is determined from \a pos. Might also shrink
0216         when n < 0.
0217     */
0218     Q_NEVER_INLINE void reallocateAndGrow(QArrayData::GrowthPosition where, qsizetype n,
0219                                           QArrayDataPointer *old = nullptr)
0220     {
0221         if constexpr (QTypeInfo<T>::isRelocatable && alignof(T) <= alignof(std::max_align_t)) {
0222             if (where == QArrayData::GrowsAtEnd && !old && !needsDetach() && n > 0) {
0223                 (*this)->reallocate(constAllocatedCapacity() - freeSpaceAtEnd() + n, QArrayData::Grow); // fast path
0224                 return;
0225             }
0226         }
0227 
0228         QArrayDataPointer dp(allocateGrow(*this, n, where));
0229         if (n > 0)
0230             Q_CHECK_PTR(dp.data());
0231         if (where == QArrayData::GrowsAtBeginning) {
0232             Q_ASSERT(dp.freeSpaceAtBegin() >= n);
0233         } else {
0234             Q_ASSERT(dp.freeSpaceAtEnd() >= n);
0235         }
0236         if (size) {
0237             qsizetype toCopy = size;
0238             if (n < 0)
0239                 toCopy += n;
0240             if (needsDetach() || old)
0241                 dp->copyAppend(begin(), begin() + toCopy);
0242             else
0243                 dp->moveAppend(begin(), begin() + toCopy);
0244             Q_ASSERT(dp.size == toCopy);
0245         }
0246 
0247         swap(dp);
0248         if (old)
0249             old->swap(dp);
0250     }
0251 
0252     /*! \internal
0253 
0254         Attempts to relocate [begin(), end()) to accommodate the free space for
0255         \a n elements at the required side. The side is determined from \a pos.
0256 
0257         Returns \c true if the internal data is moved. Returns \c false when
0258         there is no point in moving the data or the move is impossible. If \c
0259         false is returned, it is the responsibility of the caller to figure out
0260         how to accommodate the free space for \a n elements at \a pos.
0261 
0262         This function expects that certain preconditions are met, e.g. the
0263         detach is not needed, n > 0 and so on. This is intentional to reduce the
0264         number of if-statements when the caller knows that preconditions would
0265         be satisfied.
0266 
0267         \sa reallocateAndGrow
0268     */
0269     bool tryReadjustFreeSpace(QArrayData::GrowthPosition pos, qsizetype n, const T **data = nullptr)
0270     {
0271         Q_ASSERT(!this->needsDetach());
0272         Q_ASSERT(n > 0);
0273         Q_ASSERT((pos == QArrayData::GrowsAtEnd && this->freeSpaceAtEnd() < n)
0274                  || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() < n));
0275 
0276         const qsizetype capacity = this->constAllocatedCapacity();
0277         const qsizetype freeAtBegin = this->freeSpaceAtBegin();
0278         const qsizetype freeAtEnd = this->freeSpaceAtEnd();
0279 
0280         qsizetype dataStartOffset = 0;
0281         // algorithm:
0282         //   a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3
0283         //      [all goes to free space at end]:
0284         //      new free space at begin = 0
0285         //
0286         //   b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3
0287         //      [balance the free space]:
0288         //      new free space at begin = n + (total free space - n) / 2
0289         if (pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
0290             && ((3 * this->size) < (2 * capacity))) {
0291             // dataStartOffset = 0; - done in declaration
0292         } else if (pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
0293                    && ((3 * this->size) < capacity)) {
0294             // total free space == capacity - size
0295             dataStartOffset = n + qMax(0, (capacity - this->size - n) / 2);
0296         } else {
0297             // nothing to do otherwise
0298             return false;
0299         }
0300 
0301         relocate(dataStartOffset - freeAtBegin, data);
0302 
0303         Q_ASSERT((pos == QArrayData::GrowsAtEnd && this->freeSpaceAtEnd() >= n)
0304                  || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() >= n));
0305         return true;
0306     }
0307 
0308     /*! \internal
0309 
0310         Relocates [begin(), end()) by \a offset and updates \a data if it is not
0311         \c nullptr and points into [begin(), end()).
0312     */
0313     void relocate(qsizetype offset, const T **data = nullptr)
0314     {
0315         T *res = this->ptr + offset;
0316         QtPrivate::q_relocate_overlap_n(this->ptr, this->size, res);
0317         // first update data pointer, then this->ptr
0318         if (data && QtPrivate::q_points_into_range(*data, *this))
0319             *data += offset;
0320         this->ptr = res;
0321     }
0322 
0323     template <typename InputIterator, typename Projection = q20::identity>
0324     void assign(InputIterator first, InputIterator last, Projection proj = {})
0325     {
0326         // This function only provides the basic exception guarantee.
0327         constexpr bool IsFwdIt = std::is_convertible_v<
0328                 typename std::iterator_traits<InputIterator>::iterator_category,
0329                 std::forward_iterator_tag>;
0330         constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>;
0331 
0332         if constexpr (IsFwdIt) {
0333             const qsizetype n = std::distance(first, last);
0334             if (needsDetach() || n > constAllocatedCapacity()) {
0335                 QArrayDataPointer allocated(detachCapacity(n));
0336                 swap(allocated);
0337             }
0338         } else if (needsDetach()) {
0339             QArrayDataPointer allocated(allocatedCapacity());
0340             swap(allocated);
0341             // We don't want to copy data that we know we'll overwrite
0342         }
0343 
0344         auto offset = freeSpaceAtBegin();
0345         const auto capacityBegin = begin() - offset;
0346         const auto prependBufferEnd = begin();
0347 
0348         if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
0349             // If construction can throw, and we have freeSpaceAtBegin(),
0350             // it's easiest to just clear the container and start fresh.
0351             // The alternative would be to keep track of two active, disjoint ranges.
0352             if (offset) {
0353                 (*this)->truncate(0);
0354                 setBegin(capacityBegin);
0355                 offset = 0;
0356             }
0357         }
0358 
0359         auto dst = capacityBegin;
0360         const auto dend = end();
0361         if (offset) { // avoids dead stores
0362             setBegin(capacityBegin); // undo prepend optimization
0363 
0364             // By construction, the following loop is nothrow!
0365             // (otherwise, we can't reach here)
0366             // Assumes InputIterator operations don't throw.
0367             // (but we can't statically assert that, as these operations
0368             //  have preconditons, so typically aren't noexcept)
0369             while (true) {
0370                 if (dst == prependBufferEnd) {  // ran out of prepend buffer space
0371                     size += offset;
0372                     // we now have a contiguous buffer, continue with the main loop:
0373                     break;
0374                 }
0375                 if (first == last) {            // ran out of elements to assign
0376                     std::destroy(prependBufferEnd, dend);
0377                     size = dst - begin();
0378                     return;
0379                 }
0380                 // construct element in prepend buffer
0381                 q20::construct_at(dst, std::invoke(proj, *first));
0382                 ++dst;
0383                 ++first;
0384             }
0385         }
0386 
0387         while (true) {
0388             if (first == last) {    // ran out of elements to assign
0389                 std::destroy(dst, dend);
0390                 break;
0391             }
0392             if (dst == dend) {      // ran out of existing elements to overwrite
0393                 if constexpr (IsFwdIt && IsIdentity) {
0394                     dst = std::uninitialized_copy(first, last, dst);
0395                     break;
0396                 } else if constexpr (IsFwdIt && !IsIdentity
0397                            && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
0398                     for (; first != last; ++dst, ++first)   // uninitialized_copy with projection
0399                         q20::construct_at(dst, std::invoke(proj, *first));
0400                     break;
0401                 } else {
0402                     do {
0403                         (*this)->emplace(size, std::invoke(proj, *first));
0404                     } while (++first != last);
0405                     return;         // size() is already correct (and dst invalidated)!
0406                 }
0407             }
0408             *dst = std::invoke(proj, *first);    // overwrite existing element
0409             ++dst;
0410             ++first;
0411         }
0412         size = dst - begin();
0413     }
0414 
0415     QArrayDataPointer sliced(qsizetype pos, qsizetype n) const &
0416     {
0417         QArrayDataPointer result(n);
0418         std::uninitialized_copy_n(begin() + pos, n, result.begin());
0419         result.size = n;
0420         return result;
0421     }
0422 
0423     QArrayDataPointer sliced(qsizetype pos, qsizetype n) &&
0424     {
0425         if (needsDetach())
0426             return sliced(pos, n);
0427         T *newBeginning = begin() + pos;
0428         std::destroy(begin(), newBeginning);
0429         std::destroy(newBeginning + n, end());
0430         setBegin(newBeginning);
0431         size = n;
0432         return std::move(*this);
0433     }
0434 
0435     // forwards from QArrayData
0436     qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
0437     qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
0438     void ref() noexcept { if (d) d->ref(); }
0439     bool deref() noexcept { return !d || d->deref(); }
0440     bool isMutable() const noexcept { return d; }
0441     bool isShared() const noexcept { return !d || d->isShared(); }
0442     bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
0443     bool needsDetach() const noexcept { return !d || d->needsDetach(); }
0444     qsizetype detachCapacity(qsizetype newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
0445     const typename Data::ArrayOptions flags() const noexcept { return d ? d->flags : Data::ArrayOptionDefault; }
0446     void setFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags |= f; }
0447     void clearFlag(typename Data::ArrayOptions f) noexcept { if (d) d->flags &= ~f; }
0448 
0449     Data *d_ptr() noexcept { return d; }
0450     void setBegin(T *begin) noexcept { ptr = begin; }
0451 
0452     qsizetype freeSpaceAtBegin() const noexcept
0453     {
0454         if (d == nullptr)
0455             return 0;
0456         return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
0457     }
0458 
0459     qsizetype freeSpaceAtEnd() const noexcept
0460     {
0461         if (d == nullptr)
0462             return 0;
0463         return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
0464     }
0465 
0466     // allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end
0467     static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype n, QArrayData::GrowthPosition position)
0468     {
0469         // calculate new capacity. We keep the free capacity at the side that does not have to grow
0470         // to avoid quadratic behavior with mixed append/prepend cases
0471 
0472         // use qMax below, because constAllocatedCapacity() can be 0 when using fromRawData()
0473         qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
0474         // subtract the free space at the side we want to allocate. This ensures that the total size requested is
0475         // the existing allocation at the other side + size + n.
0476         minimalCapacity -= (position == QArrayData::GrowsAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
0477         qsizetype capacity = from.detachCapacity(minimalCapacity);
0478         const bool grows = capacity > from.constAllocatedCapacity();
0479         auto [header, dataPtr] = Data::allocate(capacity, grows ? QArrayData::Grow : QArrayData::KeepSize);
0480         const bool valid = header != nullptr && dataPtr != nullptr;
0481         if (!valid)
0482             return QArrayDataPointer(header, dataPtr);
0483 
0484         // Idea: * when growing backwards, adjust pointer to prepare free space at the beginning
0485         //       * when growing forward, adjust by the previous data pointer offset
0486         dataPtr += (position == QArrayData::GrowsAtBeginning)
0487                 ? n + qMax(0, (header->alloc - from.size - n) / 2)
0488                 : from.freeSpaceAtBegin();
0489         header->flags = from.flags();
0490         return QArrayDataPointer(header, dataPtr);
0491     }
0492 
0493     friend bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
0494     {
0495         return lhs.data() == rhs.data() && lhs.size == rhs.size;
0496     }
0497 
0498     friend bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
0499     {
0500         return lhs.data() != rhs.data() || lhs.size != rhs.size;
0501     }
0502 
0503     Data *d;
0504     T *ptr;
0505     qsizetype size;
0506 };
0507 
0508 template <class T>
0509 inline void swap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) noexcept
0510 {
0511     p1.swap(p2);
0512 }
0513 
0514 ////////////////////////////////////////////////////////////////////////////////
0515 //  Q_ARRAY_LITERAL
0516 
0517 // The idea here is to place a (read-only) copy of header and array data in an
0518 // mmappable portion of the executable (typically, .rodata section).
0519 
0520 // Hide array inside a lambda
0521 #define Q_ARRAY_LITERAL(Type, ...) \
0522     ([]() -> QArrayDataPointer<Type> { \
0523         static Type const data[] = { __VA_ARGS__ }; \
0524         return QArrayDataPointer<Type>::fromRawData(const_cast<Type *>(data), std::size(data)); \
0525     }())
0526 /**/
0527 
0528 QT_END_NAMESPACE
0529 
0530 #endif // include guard