File indexing completed on 2025-01-18 10:07:16
0001
0002
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
0149
0150
0151
0152
0153
0154
0155
0156
0157
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
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
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
0213
0214
0215
0216
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);
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
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
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
0282
0283
0284
0285
0286
0287
0288
0289 if (pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
0290 && ((3 * this->size) < (2 * capacity))) {
0291
0292 } else if (pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
0293 && ((3 * this->size) < capacity)) {
0294
0295 dataStartOffset = n + qMax(0, (capacity - this->size - n) / 2);
0296 } else {
0297
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
0309
0310
0311
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
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
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
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
0350
0351
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) {
0362 setBegin(capacityBegin);
0363
0364
0365
0366
0367
0368
0369 while (true) {
0370 if (dst == prependBufferEnd) {
0371 size += offset;
0372
0373 break;
0374 }
0375 if (first == last) {
0376 std::destroy(prependBufferEnd, dend);
0377 size = dst - begin();
0378 return;
0379 }
0380
0381 q20::construct_at(dst, std::invoke(proj, *first));
0382 ++dst;
0383 ++first;
0384 }
0385 }
0386
0387 while (true) {
0388 if (first == last) {
0389 std::destroy(dst, dend);
0390 break;
0391 }
0392 if (dst == dend) {
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)
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;
0406 }
0407 }
0408 *dst = std::invoke(proj, *first);
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
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
0467 static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype n, QArrayData::GrowthPosition position)
0468 {
0469
0470
0471
0472
0473 qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
0474
0475
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
0485
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
0516
0517
0518
0519
0520
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