Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qarraydatapointer.h
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QARRAYDATAPOINTER_H
5#define QARRAYDATAPOINTER_H
6
7#include <QtCore/qarraydataops.h>
8#include <QtCore/qcontainertools_impl.h>
9
10#include <QtCore/q20functional.h>
11#include <QtCore/q20memory.h>
12
14
15template <class T>
17{
18private:
21
22public:
23 enum {
25 std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_enum<T>::value
26 };
27
28 typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
29
31 constexpr QArrayDataPointer() noexcept
32 : d(nullptr), ptr(nullptr), size(0)
33 {
34 }
35
38 : d(other.d), ptr(other.ptr), size(other.size)
39 {
40 ref();
41 }
42
44 constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n = 0) noexcept
45 : d(header), ptr(adata), size(n)
46 {
47 }
48
50 explicit QArrayDataPointer(QPair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
51 : d(adata.first), ptr(adata.second), size(n)
52 {
53 }
54
56 static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
57 {
58 Q_ASSERT(rawData || !length);
59 return { nullptr, const_cast<T *>(rawData), length };
60 }
61
63 {
65 this->swap(tmp);
66 return *this;
67 }
68
71 : d(other.d), ptr(other.ptr), size(other.size)
72 {
73 other.d = nullptr;
74 other.ptr = nullptr;
75 other.size = 0;
76 }
77
78 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
79
80 DataOps &operator*() noexcept
81 {
82 return *static_cast<DataOps *>(this);
83 }
84
85 DataOps *operator->() noexcept
86 {
87 return static_cast<DataOps *>(this);
88 }
89
90 const DataOps &operator*() const noexcept
91 {
92 return *static_cast<const DataOps *>(this);
93 }
94
95 const DataOps *operator->() const noexcept
96 {
97 return static_cast<const DataOps *>(this);
98 }
99
101 {
102 if (!deref()) {
103 (*this)->destroyAll();
104 free(d);
105 }
106 }
107
108 bool isNull() const noexcept
109 {
110 return !ptr;
111 }
112
113 T *data() noexcept { return ptr; }
114 const T *data() const noexcept { return ptr; }
115
116 T *begin() noexcept { return data(); }
117 T *end() noexcept { return data() + size; }
118 const T *begin() const noexcept { return data(); }
119 const T *end() const noexcept { return data() + size; }
120 const T *constBegin() const noexcept { return data(); }
121 const T *constEnd() const noexcept { return data() + size; }
122
124 {
125 qt_ptr_swap(d, other.d);
126 qt_ptr_swap(ptr, other.ptr);
127 std::swap(size, other.size);
128 }
129
130 void clear() noexcept(std::is_nothrow_destructible<T>::value)
131 {
133 swap(tmp);
134 }
135
136 void detach(QArrayDataPointer *old = nullptr)
137 {
138 if (needsDetach())
140 }
141
153 template <typename X> QArrayDataPointer<X> reinterpreted() &&
154 {
155 if (sizeof(T) != sizeof(X)) {
156 Q_ASSERT(!d->isShared());
157 d->alloc = d->alloc * sizeof(T) / sizeof(X);
158 }
159 auto od = reinterpret_cast<QTypedArrayData<X> *>(std::exchange(d, nullptr));
160 auto optr = reinterpret_cast<X *>(std::exchange(ptr, nullptr));
161 return { od, optr, std::exchange(size, 0) };
162 }
163
189 {
190 const bool detach = needsDetach();
191 bool readjusted = false;
192 if (!detach) {
193 if (!n || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
194 || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n))
195 return;
196 readjusted = tryReadjustFreeSpace(where, n, data);
197 Q_ASSERT(!readjusted
199 || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n));
200 }
201
202 if (!readjusted)
203 reallocateAndGrow(where, n, old);
204 }
205
213 QArrayDataPointer *old = nullptr)
214 {
215 if constexpr (QTypeInfo<T>::isRelocatable && alignof(T) <= alignof(std::max_align_t)) {
216 if (where == QArrayData::GrowsAtEnd && !old && !needsDetach() && n > 0) {
217 (*this)->reallocate(constAllocatedCapacity() - freeSpaceAtEnd() + n, QArrayData::Grow); // fast path
218 return;
219 }
220 }
221
222 QArrayDataPointer dp(allocateGrow(*this, n, where));
223 if (n > 0)
224 Q_CHECK_PTR(dp.data());
225 if (where == QArrayData::GrowsAtBeginning) {
226 Q_ASSERT(dp.freeSpaceAtBegin() >= n);
227 } else {
228 Q_ASSERT(dp.freeSpaceAtEnd() >= n);
229 }
230 if (size) {
231 qsizetype toCopy = size;
232 if (n < 0)
233 toCopy += n;
234 if (needsDetach() || old)
235 dp->copyAppend(begin(), begin() + toCopy);
236 else
237 dp->moveAppend(begin(), begin() + toCopy);
238 Q_ASSERT(dp.size == toCopy);
239 }
240
241 swap(dp);
242 if (old)
243 old->swap(dp);
244 }
245
264 {
265 Q_ASSERT(!this->needsDetach());
266 Q_ASSERT(n > 0);
268 || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() < n));
269
271 const qsizetype freeAtBegin = this->freeSpaceAtBegin();
272 const qsizetype freeAtEnd = this->freeSpaceAtEnd();
273
274 qsizetype dataStartOffset = 0;
275 // algorithm:
276 // a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3
277 // [all goes to free space at end]:
278 // new free space at begin = 0
279 //
280 // b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3
281 // [balance the free space]:
282 // new free space at begin = n + (total free space - n) / 2
283 if (pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
284 && ((3 * this->size) < (2 * capacity))) {
285 // dataStartOffset = 0; - done in declaration
286 } else if (pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
287 && ((3 * this->size) < capacity)) {
288 // total free space == capacity - size
289 dataStartOffset = n + qMax(0, (capacity - this->size - n) / 2);
290 } else {
291 // nothing to do otherwise
292 return false;
293 }
294
295 relocate(dataStartOffset - freeAtBegin, data);
296
298 || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() >= n));
299 return true;
300 }
301
307 void relocate(qsizetype offset, const T **data = nullptr)
308 {
309 T *res = this->ptr + offset;
311 // first update data pointer, then this->ptr
313 *data += offset;
314 this->ptr = res;
315 }
316
317 template <typename InputIterator, typename Projection = q20::identity>
318 void assign(InputIterator first, InputIterator last, Projection proj = {})
319 {
320 // This function only provides the basic exception guarantee.
321 constexpr bool IsFwdIt = std::is_convertible_v<
322 typename std::iterator_traits<InputIterator>::iterator_category,
323 std::forward_iterator_tag>;
324 constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>;
325
326 if constexpr (IsFwdIt) {
327 const qsizetype n = std::distance(first, last);
328 if (needsDetach() || n > constAllocatedCapacity()) {
330 Q_CHECK_PTR(allocated.data());
331 swap(allocated);
332 }
333 } else if (needsDetach()) {
335 Q_CHECK_PTR(allocated.data());
336 swap(allocated);
337 // We don't want to copy data that we know we'll overwrite
338 }
339
340 auto offset = freeSpaceAtBegin();
341 const auto capacityBegin = begin() - offset;
342 const auto prependBufferEnd = begin();
343
344 if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
345 // If construction can throw, and we have freeSpaceAtBegin(),
346 // it's easiest to just clear the container and start fresh.
347 // The alternative would be to keep track of two active, disjoint ranges.
348 if (offset) {
349 (*this)->truncate(0);
350 setBegin(capacityBegin);
351 offset = 0;
352 }
353 }
354
355 auto dst = capacityBegin;
356 const auto dend = end();
357 if (offset) { // avoids dead stores
358 setBegin(capacityBegin); // undo prepend optimization
359
360 // By construction, the following loop is nothrow!
361 // (otherwise, we can't reach here)
362 // Assumes InputIterator operations don't throw.
363 // (but we can't statically assert that, as these operations
364 // have preconditons, so typically aren't noexcept)
365 while (true) {
366 if (dst == prependBufferEnd) { // ran out of prepend buffer space
367 size += offset;
368 // we now have a contiguous buffer, continue with the main loop:
369 break;
370 }
371 if (first == last) { // ran out of elements to assign
372 std::destroy(prependBufferEnd, dend);
373 size = dst - begin();
374 return;
375 }
376 // construct element in prepend buffer
377 q20::construct_at(dst, std::invoke(proj, *first));
378 ++dst;
379 ++first;
380 }
381 }
382
383 while (true) {
384 if (first == last) { // ran out of elements to assign
385 std::destroy(dst, dend);
386 break;
387 }
388 if (dst == dend) { // ran out of existing elements to overwrite
389 if constexpr (IsFwdIt && IsIdentity) {
390 dst = std::uninitialized_copy(first, last, dst);
391 break;
392 } else if constexpr (IsFwdIt && !IsIdentity
393 && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
394 for (; first != last; ++dst, ++first) // uninitialized_copy with projection
395 q20::construct_at(dst, std::invoke(proj, *first));
396 break;
397 } else {
398 do {
399 (*this)->emplace(size, std::invoke(proj, *first));
400 } while (++first != last);
401 return; // size() is already correct (and dst invalidated)!
402 }
403 }
404 *dst = std::invoke(proj, *first); // overwrite existing element
405 ++dst;
406 ++first;
407 }
408 size = dst - begin();
409 }
410
411 // forwards from QArrayData
412 qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
413 qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
414 void ref() noexcept { if (d) d->ref(); }
415 bool deref() noexcept { return !d || d->deref(); }
416 bool isMutable() const noexcept { return d; }
417 bool isShared() const noexcept { return !d || d->isShared(); }
418 bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
419 bool needsDetach() const noexcept { return !d || d->needsDetach(); }
420 qsizetype detachCapacity(qsizetype newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
421 const typename Data::ArrayOptions flags() const noexcept { return d ? d->flags : Data::ArrayOptionDefault; }
422 void setFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags |= f; }
423 void clearFlag(typename Data::ArrayOptions f) noexcept { if (d) d->flags &= ~f; }
424
425 Data *d_ptr() noexcept { return d; }
426 void setBegin(T *begin) noexcept { ptr = begin; }
427
429 {
430 if (d == nullptr)
431 return 0;
432 return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
433 }
434
435 qsizetype freeSpaceAtEnd() const noexcept
436 {
437 if (d == nullptr)
438 return 0;
439 return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
440 }
441
442 // allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end
444 {
445 // calculate new capacity. We keep the free capacity at the side that does not have to grow
446 // to avoid quadratic behavior with mixed append/prepend cases
447
448 // use qMax below, because constAllocatedCapacity() can be 0 when using fromRawData()
449 qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
450 // subtract the free space at the side we want to allocate. This ensures that the total size requested is
451 // the existing allocation at the other side + size + n.
452 minimalCapacity -= (position == QArrayData::GrowsAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
453 qsizetype capacity = from.detachCapacity(minimalCapacity);
454 const bool grows = capacity > from.constAllocatedCapacity();
456 const bool valid = header != nullptr && dataPtr != nullptr;
457 if (!valid)
458 return QArrayDataPointer(header, dataPtr);
459
460 // Idea: * when growing backwards, adjust pointer to prepare free space at the beginning
461 // * when growing forward, adjust by the previous data pointer offset
463 ? n + qMax(0, (header->alloc - from.size - n) / 2)
464 : from.freeSpaceAtBegin();
465 header->flags = from.flags();
466 return QArrayDataPointer(header, dataPtr);
467 }
468
469 friend bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
470 {
471 return lhs.data() == rhs.data() && lhs.size == rhs.size;
472 }
473
474 friend bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
475 {
476 return lhs.data() != rhs.data() || lhs.size != rhs.size;
477 }
478
480 T *ptr;
482};
483
484template <class T>
486{
487 p1.swap(p2);
488}
489
491// Q_ARRAY_LITERAL
492
493// The idea here is to place a (read-only) copy of header and array data in an
494// mmappable portion of the executable (typically, .rodata section).
495
496// Hide array inside a lambda
497#define Q_ARRAY_LITERAL(Type, ...) \
498 ([]() -> QArrayDataPointer<Type> { \
499 static Type const data[] = { __VA_ARGS__ }; \
500 return QArrayDataPointer<Type>::fromRawData(const_cast<Type *>(data), std::size(data)); \
501 }())
502
503
505
506#endif // include guard
void swap(QPixmap &other) noexcept
Definition qpixmap.h:43
QPixmap p2
QPixmap p1
[0]
Combined button and popup list for selecting options.
static constexpr bool q_points_into_range(const T *p, const T *b, const T *e, Cmp less={}) noexcept
void q_relocate_overlap_n(T *first, N n, T *d_first)
T * construct_at(T *ptr, Args &&... args)
Definition q20memory.h:41
#define Q_NODISCARD_CTOR
#define Q_NEVER_INLINE
std::pair< T1, T2 > QPair
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
NSUInteger capacity
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLfloat GLfloat f
GLenum GLenum dst
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLint first
GLfloat n
GLuint res
#define X(name)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
constexpr void qt_ptr_swap(T *&lhs, T *&rhs) noexcept
Definition qswap.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
Q_CHECK_PTR(a=new int[80])
QSharedPointer< T > other(t)
[5]
this swap(other)
Q_NODISCARD_CTOR constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n=0) noexcept
friend bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
void setFlag(typename Data::ArrayOptions f) noexcept
void detachAndGrow(QArrayData::GrowthPosition where, qsizetype n, const T **data, QArrayDataPointer *old)
bool isShared() const noexcept
void relocate(qsizetype offset, const T **data=nullptr)
Q_NODISCARD_CTOR QArrayDataPointer(QPair< QTypedArrayData< T > *, T * > adata, qsizetype n=0) noexcept
bool isNull() const noexcept
const T * data() const noexcept
const T * constBegin() const noexcept
qsizetype freeSpaceAtBegin() const noexcept
const DataOps & operator*() const noexcept
Q_NEVER_INLINE void reallocateAndGrow(QArrayData::GrowthPosition where, qsizetype n, QArrayDataPointer *old=nullptr)
void detach(QArrayDataPointer *old=nullptr)
bool needsDetach() const noexcept
bool isSharedWith(const QArrayDataPointer &other) const noexcept
qsizetype allocatedCapacity() noexcept
bool deref() noexcept
const DataOps * operator->() const noexcept
const T * constEnd() const noexcept
std::conditional< pass_parameter_by_value, T, constT & >::type parameter_type
void setBegin(T *begin) noexcept
QArrayDataPointer< X > reinterpreted() &&
DataOps * operator->() noexcept
Q_NODISCARD_CTOR constexpr QArrayDataPointer() noexcept
qsizetype freeSpaceAtEnd() const noexcept
Q_NODISCARD_CTOR QArrayDataPointer(const QArrayDataPointer &other) noexcept
void ref() noexcept
void swap(QArrayDataPointer &other) noexcept
const T * begin() const noexcept
const Data::ArrayOptions flags() const noexcept
qsizetype constAllocatedCapacity() const noexcept
void clear() noexcept(std::is_nothrow_destructible< T >::value)
const T * end() const noexcept
bool tryReadjustFreeSpace(QArrayData::GrowthPosition pos, qsizetype n, const T **data=nullptr)
friend bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
QArrayDataPointer & operator=(const QArrayDataPointer &other) noexcept
static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype n, QArrayData::GrowthPosition position)
Data * d_ptr() noexcept
Q_NODISCARD_CTOR QArrayDataPointer(QArrayDataPointer &&other) noexcept
bool isMutable() const noexcept
qsizetype detachCapacity(qsizetype newSize) const noexcept
void clearFlag(typename Data::ArrayOptions f) noexcept
static Q_NODISCARD_CTOR QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
void assign(InputIterator first, InputIterator last, Projection proj={})
qsizetype detachCapacity(qsizetype newSize) const noexcept
Definition qarraydata.h:74
@ ArrayOptionDefault
Definition qarraydata.h:29
bool needsDetach() const noexcept
Definition qarraydata.h:69
bool deref() noexcept
Returns false if deallocation is necessary.
Definition qarraydata.h:56
ArrayOptions flags
Definition qarraydata.h:35
bool isShared() const noexcept
Definition qarraydata.h:61
qsizetype alloc
Definition qarraydata.h:36
qsizetype constAllocatedCapacity() const noexcept
Definition qarraydata.h:43
qsizetype allocatedCapacity() noexcept
Definition qarraydata.h:38
@ GrowsAtBeginning
Definition qarraydata.h:25
bool ref() noexcept
Returns true if sharing took place.
Definition qarraydata.h:49
static T * dataStart(QArrayData *data, qsizetype alignment) noexcept
Definition qarraydata.h:127
static QPair< QTypedArrayData *, T * > allocate(qsizetype capacity, AllocationOption option=QArrayData::KeepSize)
Definition qarraydata.h:101
void copyAppend(const T *b, const T *e)