Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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:
19 typedef QTypedArrayData<T> Data;
20 typedef QArrayDataOps<T> DataOps;
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(std::pair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
51 : d(adata.first), ptr(adata.second), size(n)
52 {
53 }
54
55 Q_NODISCARD_CTOR explicit
61
63 static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
64 {
65 Q_ASSERT(rawData || !length);
66 return { nullptr, const_cast<T *>(rawData), length };
67 }
68
70 {
72 this->swap(tmp);
73 return *this;
74 }
75
78 : d(std::exchange(other.d, nullptr)),
79 ptr(std::exchange(other.ptr, nullptr)),
80 size(std::exchange(other.size, 0))
81 {
82 }
83
84 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
85
86 DataOps &operator*() noexcept
87 {
88 return *static_cast<DataOps *>(this);
89 }
90
91 DataOps *operator->() noexcept
92 {
93 return static_cast<DataOps *>(this);
94 }
95
96 const DataOps &operator*() const noexcept
97 {
98 return *static_cast<const DataOps *>(this);
99 }
100
101 const DataOps *operator->() const noexcept
102 {
103 return static_cast<const DataOps *>(this);
104 }
105
107 {
108 if (!deref()) {
109 (*this)->destroyAll();
110 free(d);
111 }
112 }
113
114 bool isNull() const noexcept
115 {
116 return !ptr;
117 }
118
119 T *data() noexcept { return ptr; }
120 const T *data() const noexcept { return ptr; }
121
122 T *begin() noexcept { return data(); }
123 T *end() noexcept { return data() + size; }
124 const T *begin() const noexcept { return data(); }
125 const T *end() const noexcept { return data() + size; }
126 const T *constBegin() const noexcept { return data(); }
127 const T *constEnd() const noexcept { return data() + size; }
128
130 {
131 qt_ptr_swap(d, other.d);
132 qt_ptr_swap(ptr, other.ptr);
133 std::swap(size, other.size);
134 }
135
136 void clear() noexcept(std::is_nothrow_destructible<T>::value)
137 {
139 swap(tmp);
140 }
141
142 void detach(QArrayDataPointer *old = nullptr)
143 {
144 if (needsDetach())
146 }
147
159 template <typename X> QArrayDataPointer<X> reinterpreted() &&
160 {
161 if (sizeof(T) != sizeof(X)) {
162 Q_ASSERT(!d->isShared());
163 d->alloc = d->alloc * sizeof(T) / sizeof(X);
164 }
165 auto od = reinterpret_cast<QTypedArrayData<X> *>(std::exchange(d, nullptr));
166 auto optr = reinterpret_cast<X *>(std::exchange(ptr, nullptr));
167 return { od, optr, std::exchange(size, 0) };
168 }
169
195 {
196 const bool detach = needsDetach();
197 bool readjusted = false;
198 if (!detach) {
199 if (!n || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
200 || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n))
201 return;
202 readjusted = tryReadjustFreeSpace(where, n, data);
203 Q_ASSERT(!readjusted
205 || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n));
206 }
207
208 if (!readjusted)
209 reallocateAndGrow(where, n, old);
210 }
211
219 QArrayDataPointer *old = nullptr)
220 {
221 if constexpr (QTypeInfo<T>::isRelocatable && alignof(T) <= alignof(std::max_align_t)) {
222 if (where == QArrayData::GrowsAtEnd && !old && !needsDetach() && n > 0) {
223 (*this)->reallocate(constAllocatedCapacity() - freeSpaceAtEnd() + n, QArrayData::Grow); // fast path
224 return;
225 }
226 }
227
228 QArrayDataPointer dp(allocateGrow(*this, n, where));
229 if (n > 0)
230 Q_CHECK_PTR(dp.data());
231 if (where == QArrayData::GrowsAtBeginning) {
232 Q_ASSERT(dp.freeSpaceAtBegin() >= n);
233 } else {
234 Q_ASSERT(dp.freeSpaceAtEnd() >= n);
235 }
236 if (size) {
237 qsizetype toCopy = size;
238 if (n < 0)
239 toCopy += n;
240 if (needsDetach() || old)
241 dp->copyAppend(begin(), begin() + toCopy);
242 else
243 dp->moveAppend(begin(), begin() + toCopy);
244 Q_ASSERT(dp.size == toCopy);
245 }
246
247 swap(dp);
248 if (old)
249 old->swap(dp);
250 }
251
270 {
271 Q_ASSERT(!this->needsDetach());
272 Q_ASSERT(n > 0);
274 || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() < n));
275
277 const qsizetype freeAtBegin = this->freeSpaceAtBegin();
278 const qsizetype freeAtEnd = this->freeSpaceAtEnd();
279
280 qsizetype dataStartOffset = 0;
281 // algorithm:
282 // a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3
283 // [all goes to free space at end]:
284 // new free space at begin = 0
285 //
286 // b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3
287 // [balance the free space]:
288 // new free space at begin = n + (total free space - n) / 2
289 if (pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
290 && ((3 * this->size) < (2 * capacity))) {
291 // dataStartOffset = 0; - done in declaration
292 } else if (pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
293 && ((3 * this->size) < capacity)) {
294 // total free space == capacity - size
295 dataStartOffset = n + qMax(0, (capacity - this->size - n) / 2);
296 } else {
297 // nothing to do otherwise
298 return false;
299 }
300
301 relocate(dataStartOffset - freeAtBegin, data);
302
304 || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() >= n));
305 return true;
306 }
307
313 void relocate(qsizetype offset, const T **data = nullptr)
314 {
315 T *res = this->ptr + offset;
317 // first update data pointer, then this->ptr
319 *data += offset;
320 this->ptr = res;
321 }
322
323 template <typename InputIterator, typename Projection = q20::identity>
324 void assign(InputIterator first, InputIterator last, Projection proj = {})
325 {
326 // This function only provides the basic exception guarantee.
327 constexpr bool IsFwdIt = std::is_convertible_v<
328 typename std::iterator_traits<InputIterator>::iterator_category,
329 std::forward_iterator_tag>;
330 constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>;
331
332 if constexpr (IsFwdIt) {
333 const qsizetype n = std::distance(first, last);
334 if (needsDetach() || n > constAllocatedCapacity()) {
336 swap(allocated);
337 }
338 } else if (needsDetach()) {
340 swap(allocated);
341 // We don't want to copy data that we know we'll overwrite
342 }
343
344 auto offset = freeSpaceAtBegin();
345 const auto capacityBegin = begin() - offset;
346 const auto prependBufferEnd = begin();
347
348 if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
349 // If construction can throw, and we have freeSpaceAtBegin(),
350 // it's easiest to just clear the container and start fresh.
351 // The alternative would be to keep track of two active, disjoint ranges.
352 if (offset) {
353 (*this)->truncate(0);
354 setBegin(capacityBegin);
355 offset = 0;
356 }
357 }
358
359 auto dst = capacityBegin;
360 const auto dend = end();
361 if (offset) { // avoids dead stores
362 setBegin(capacityBegin); // undo prepend optimization
363
364 // By construction, the following loop is nothrow!
365 // (otherwise, we can't reach here)
366 // Assumes InputIterator operations don't throw.
367 // (but we can't statically assert that, as these operations
368 // have preconditons, so typically aren't noexcept)
369 while (true) {
370 if (dst == prependBufferEnd) { // ran out of prepend buffer space
371 size += offset;
372 // we now have a contiguous buffer, continue with the main loop:
373 break;
374 }
375 if (first == last) { // ran out of elements to assign
376 std::destroy(prependBufferEnd, dend);
377 size = dst - begin();
378 return;
379 }
380 // construct element in prepend buffer
381 q20::construct_at(dst, std::invoke(proj, *first));
382 ++dst;
383 ++first;
384 }
385 }
386
387 while (true) {
388 if (first == last) { // ran out of elements to assign
389 std::destroy(dst, dend);
390 break;
391 }
392 if (dst == dend) { // ran out of existing elements to overwrite
393 if constexpr (IsFwdIt && IsIdentity) {
394 dst = std::uninitialized_copy(first, last, dst);
395 break;
396 } else if constexpr (IsFwdIt && !IsIdentity
397 && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
398 for (; first != last; ++dst, ++first) // uninitialized_copy with projection
399 q20::construct_at(dst, std::invoke(proj, *first));
400 break;
401 } else {
402 do {
403 (*this)->emplace(size, std::invoke(proj, *first));
404 } while (++first != last);
405 return; // size() is already correct (and dst invalidated)!
406 }
407 }
408 *dst = std::invoke(proj, *first); // overwrite existing element
409 ++dst;
410 ++first;
411 }
412 size = dst - begin();
413 }
414
416 {
418 std::uninitialized_copy_n(begin() + pos, n, result.begin());
419 result.size = n;
420 return result;
421 }
422
424 {
425 if (needsDetach())
426 return sliced(pos, n);
427 T *newBeginning = begin() + pos;
428 std::destroy(begin(), newBeginning);
429 std::destroy(newBeginning + n, end());
430 setBegin(newBeginning);
431 size = n;
432 return std::move(*this);
433 }
434
435 // forwards from QArrayData
436 qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
437 qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
438 void ref() noexcept { if (d) d->ref(); }
439 bool deref() noexcept { return !d || d->deref(); }
440 bool isMutable() const noexcept { return d; }
441 bool isShared() const noexcept { return !d || d->isShared(); }
442 bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
443 bool needsDetach() const noexcept { return !d || d->needsDetach(); }
444 qsizetype detachCapacity(qsizetype newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
445 const typename Data::ArrayOptions flags() const noexcept { return d ? d->flags : Data::ArrayOptionDefault; }
446 void setFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags |= f; }
447 void clearFlag(typename Data::ArrayOptions f) noexcept { if (d) d->flags &= ~f; }
448
449 Data *d_ptr() noexcept { return d; }
450 void setBegin(T *begin) noexcept { ptr = begin; }
451
453 {
454 if (d == nullptr)
455 return 0;
456 return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
457 }
458
459 qsizetype freeSpaceAtEnd() const noexcept
460 {
461 if (d == nullptr)
462 return 0;
463 return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
464 }
465
466 // allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end
468 {
469 // calculate new capacity. We keep the free capacity at the side that does not have to grow
470 // to avoid quadratic behavior with mixed append/prepend cases
471
472 // use qMax below, because constAllocatedCapacity() can be 0 when using fromRawData()
473 qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
474 // subtract the free space at the side we want to allocate. This ensures that the total size requested is
475 // the existing allocation at the other side + size + n.
476 minimalCapacity -= (position == QArrayData::GrowsAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
477 qsizetype capacity = from.detachCapacity(minimalCapacity);
478 const bool grows = capacity > from.constAllocatedCapacity();
479 auto [header, dataPtr] = Data::allocate(capacity, grows ? QArrayData::Grow : QArrayData::KeepSize);
480 const bool valid = header != nullptr && dataPtr != nullptr;
481 if (!valid)
482 return QArrayDataPointer(header, dataPtr);
483
484 // Idea: * when growing backwards, adjust pointer to prepare free space at the beginning
485 // * when growing forward, adjust by the previous data pointer offset
487 ? n + qMax(0, (header->alloc - from.size - n) / 2)
488 : from.freeSpaceAtBegin();
489 header->flags = from.flags();
490 return QArrayDataPointer(header, dataPtr);
491 }
492
493 friend bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
494 {
495 return lhs.data() == rhs.data() && lhs.size == rhs.size;
496 }
497
498 friend bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept
499 {
500 return lhs.data() != rhs.data() || lhs.size != rhs.size;
501 }
502
503 Data *d;
504 T *ptr;
506};
507
508template <class T>
509inline void swap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) noexcept
510{
511 p1.swap(p2);
512}
513
515// Q_ARRAY_LITERAL
516
517// The idea here is to place a (read-only) copy of header and array data in an
518// mmappable portion of the executable (typically, .rodata section).
519
520// Hide array inside a lambda
521#define Q_ARRAY_LITERAL(Type, ...) \
522 ([]() -> QArrayDataPointer<Type> { \
523 static Type const data[] = { __VA_ARGS__ }; \
524 return QArrayDataPointer<Type>::fromRawData(const_cast<Type *>(data), std::size(data)); \
525 }())
526
527
529
530#endif // include guard
void swap(QPixmap &other) noexcept
Swaps pixmap other with this pixmap.
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
void swap(QArrayDataPointer< T > &p1, QArrayDataPointer< T > &p2) noexcept
#define Q_NODISCARD_CTOR
#define Q_NEVER_INLINE
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLenum dst
GLenum GLuint GLintptr offset
GLint first
GLfloat n
GLuint res
GLuint64EXT * result
[6]
GLuint GLenum option
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:29
ptrdiff_t qsizetype
Definition qtypes.h:165
Q_CHECK_PTR(a=new int[80])
QSharedPointer< T > other(t)
[5]
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)
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
Q_NODISCARD_CTOR QArrayDataPointer(qsizetype alloc, qsizetype n=0, QArrayData::AllocationOption option=QArrayData::KeepSize)
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 sliced(qsizetype pos, qsizetype n) &&
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
QArrayDataPointer sliced(qsizetype pos, qsizetype n) const &
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
Q_NODISCARD_CTOR QArrayDataPointer(std::pair< QTypedArrayData< T > *, T * > adata, qsizetype n=0) noexcept
void assign(InputIterator first, InputIterator last, Projection proj={})
@ GrowsAtBeginning
Definition qarraydata.h:33