Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
// Qt-Security score:significant reason:default
4
5
#
ifndef
QARRAYDATAPOINTER_H
6
#
define
QARRAYDATAPOINTER_H
7
8
#
include
<
QtCore
/
qarraydataops
.
h
>
9
#
include
<
QtCore
/
qcontainertools_impl
.
h
>
10
11
#
include
<
QtCore
/
q20utility
.
h
>
12
13
QT_BEGIN_NAMESPACE
14
15
template
<
class
T>
16
struct
QArrayDataPointer
17
{
18
private
:
19
typedef
QTypedArrayData<T> Data;
20
typedef
QArrayDataOps<T> DataOps;
21
22
public
:
23
enum
{
24
pass_parameter_by_value = std::disjunction_v<
25
std::is_arithmetic<T>,
26
std::is_pointer<T>,
27
std::is_enum<T>
28
>,
29
};
30
31
typedef
typename
std::conditional<pass_parameter_by_value, T,
const
T &>::type parameter_type;
32
33
Q_NODISCARD_CTOR
34
constexpr
QArrayDataPointer() =
default
;
35
36
Q_NODISCARD_CTOR
37
Q_DECL_CONSTEXPR_DTOR
38
QArrayDataPointer(
const
QArrayDataPointer &other)
noexcept
39
: QArrayDataPointer(other.d, other.ptr, other.size)
40
{
41
ref();
42
}
43
44
Q_NODISCARD_CTOR
45
constexpr
QArrayDataPointer(Data *header, T *adata, qsizetype n = 0)
noexcept
46
#
if
QT_VERSION
>=
QT_VERSION_CHECK
(
7
,
0
,
0
)
||
defined
(
QT_BOOTSTRAPPED
)
47
: ptr(adata), size(n), d(header)
48
#
else
49
: d(header), ptr(adata), size(n)
50
#
endif
51
{
52
}
53
54
Q_NODISCARD_CTOR
55
explicit
QArrayDataPointer(QTypedArrayAllocationResult<T> adata, qsizetype n = 0)
noexcept
56
// a failed allocation must not yield size != 0, or data() will
57
// Q_PRESUME() non-null and optimize away callers' Q_CHECK_PTR():
58
: QArrayDataPointer(adata.header, adata.ptr, adata.ptr ? n : 0)
59
{
60
}
61
62
Q_NODISCARD_CTOR
explicit
63
QArrayDataPointer(qsizetype alloc, qsizetype n = 0,
64
QArrayData::AllocationOption option = QArrayData::KeepSize)
65
: QArrayDataPointer(Data::allocate(alloc, option), n)
66
{
67
}
68
69
Q_NODISCARD_CTOR
70
static
QArrayDataPointer fromRawData(
const
T *rawData, qsizetype length)
noexcept
71
{
72
Q_ASSERT(rawData || !length);
73
return
{
nullptr
,
const_cast
<T *>(rawData), length };
74
}
75
76
Q_DECL_CONSTEXPR_DTOR
77
QArrayDataPointer &operator=(
const
QArrayDataPointer &other)
noexcept
78
{
79
QArrayDataPointer tmp(other);
80
this
->swap(tmp);
81
return
*
this
;
82
}
83
84
Q_NODISCARD_CTOR
85
constexpr
86
QArrayDataPointer(QArrayDataPointer &&other)
noexcept
87
: QArrayDataPointer(q20::exchange(other.d,
nullptr
),
88
q20::exchange(other.ptr,
nullptr
),
89
q20::exchange(other.size, 0))
90
{
91
}
92
93
Q_DECL_CONSTEXPR_DTOR
94
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
95
96
DataOps operator*()
noexcept
97
{
98
return
DataOps(*
this
);
99
}
100
101
DataOps operator->()
noexcept
102
{
103
return
DataOps(*
this
);
104
}
105
106
Q_DECL_CONSTEXPR_DTOR
107
~QArrayDataPointer()
108
{
109
if
(!deref())
110
destroyAndDeallocate();
111
}
112
113
// Avoid instantiating QArrayDataOps<T> inside the constexpr destructor.
114
void
destroyAndDeallocate()
115
{
116
(*
this
)->destroyAll();
117
Data::deallocate(d);
118
}
119
120
constexpr
bool
isNull()
const
noexcept
121
{
122
return
!ptr;
123
}
124
125
T *data()
noexcept
{ T *p = ptr;
if
(size) Q_PRESUME(p);
return
p; }
126
constexpr
const
T *data()
const
noexcept
{ T *p = ptr;
if
(size) Q_PRESUME(p);
return
p; }
127
128
T *begin()
noexcept
{
return
data(); }
129
T *end()
noexcept
{
return
data() + size; }
130
constexpr
131
const
T *begin()
const
noexcept
{
return
data(); }
132
constexpr
133
const
T *end()
const
noexcept
{
return
data() + size; }
134
constexpr
135
const
T *constBegin()
const
noexcept
{
return
data(); }
136
constexpr
137
const
T *constEnd()
const
noexcept
{
return
data() + size; }
138
139
constexpr
void
swap(QArrayDataPointer &other)
noexcept
140
{
141
qt_ptr_swap(d, other.d);
142
qt_ptr_swap(ptr, other.ptr);
143
std::swap(size, other.size);
144
}
145
146
void
clear()
noexcept
(std::is_nothrow_destructible<T>::value)
147
{
148
QArrayDataPointer tmp;
149
swap(tmp);
150
}
151
152
void
detach(QArrayDataPointer *old =
nullptr
)
153
{
154
if
(needsDetach())
155
reallocateAndGrow(QArrayData::GrowsAtEnd, 0, old);
156
}
157
158
/*! \internal
159
160
Reinterprets the data of this QArrayDataPointer to type X. It's the
161
caller's responsibility to ensure that the data contents are valid and
162
properly aligned, particularly if T and X are not trivial types (i.e,
163
don't do that). The current size is kept and the allocated capacity is
164
updated to account for the difference in the element type's size.
165
166
This is used in QString::fromLatin1 to perform in-place conversion of
167
QString to QByteArray.
168
*/
169
template
<
typename
X> QArrayDataPointer<X> reinterpreted() &&
170
{
171
if
(
sizeof
(T) !=
sizeof
(X)) {
172
Q_ASSERT(!d->isShared());
173
d->alloc = d->alloc *
sizeof
(T) /
sizeof
(X);
174
}
175
auto
od =
reinterpret_cast
<QTypedArrayData<X> *>(std::exchange(d,
nullptr
));
176
auto
optr =
reinterpret_cast
<X *>(std::exchange(ptr,
nullptr
));
177
return
{ od, optr, std::exchange(size, 0) };
178
}
179
180
/*! \internal
181
182
Detaches this (optionally) and grows to accommodate the free space for
183
\a n elements at the required side. The side is determined from \a pos.
184
185
\a data pointer can be provided when the caller knows that \a data
186
points into range [this->begin(), this->end()). In case it is, *data
187
would be updated so that it continues to point to the element it was
188
pointing to before the data move. if \a data does not point into range,
189
one can/should pass \c nullptr.
190
191
Similarly to \a data, \a old, pointer to a default-constructed QADP, can
192
be provided when the caller expects to e.g. copy the data from this to
193
itself:
194
\code
195
QList<T> list(5);
196
qsizetype pos = getArbitraryPos();
197
list.insert(pos, list.begin(), list.end());
198
\endcode
199
200
The default rule would be: \a data and \a old must either both be valid
201
pointers, or both equal to \c nullptr.
202
*/
203
void
detachAndGrow(QArrayData::GrowthPosition where, qsizetype n,
const
T **data,
204
QArrayDataPointer *old)
205
{
206
const
bool
detach = needsDetach();
207
bool
readjusted =
false
;
208
if
(!detach) {
209
if
(!n || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
210
|| (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n))
211
return
;
212
readjusted = tryReadjustFreeSpace(where, n, data);
213
Q_ASSERT(!readjusted
214
|| (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
215
|| (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n));
216
}
217
218
if
(!readjusted)
219
reallocateAndGrow(where, n, old);
220
}
221
222
/*! \internal
223
224
Reallocates to accommodate the free space for \a n elements at the
225
required side. The side is determined from \a pos. Might also shrink
226
when n < 0.
227
*/
228
Q_NEVER_INLINE
void
reallocateAndGrow(QArrayData::GrowthPosition where, qsizetype n,
229
QArrayDataPointer *old =
nullptr
)
230
{
231
if
constexpr
(QTypeInfo<T>::isRelocatable &&
alignof
(T) <=
alignof
(std::max_align_t)) {
232
if
(where == QArrayData::GrowsAtEnd && !old && !needsDetach() && n > 0) {
233
(*
this
)->reallocate(constAllocatedCapacity() - freeSpaceAtEnd() + n, QArrayData::Grow);
// fast path
234
return
;
235
}
236
}
237
238
QArrayDataPointer dp(allocateGrow(*
this
, n, where));
239
if
(n > 0)
240
Q_CHECK_PTR(dp.data());
241
if
(where == QArrayData::GrowsAtBeginning) {
242
Q_ASSERT(dp.freeSpaceAtBegin() >= n);
243
}
else
{
244
Q_ASSERT(dp.freeSpaceAtEnd() >= n);
245
}
246
if
(size) {
247
qsizetype toCopy = size;
248
if
(n < 0)
249
toCopy += n;
250
if
(needsDetach() || old)
251
dp->copyAppend(begin(), begin() + toCopy);
252
else
253
dp->moveAppend(begin(), begin() + toCopy);
254
Q_ASSERT(dp.size == toCopy);
255
}
256
257
swap(dp);
258
if
(old)
259
old->swap(dp);
260
}
261
262
/*! \internal
263
264
Attempts to relocate [begin(), end()) to accommodate the free space for
265
\a n elements at the required side. The side is determined from \a pos.
266
267
Returns \c true if the internal data is moved. Returns \c false when
268
there is no point in moving the data or the move is impossible. If \c
269
false is returned, it is the responsibility of the caller to figure out
270
how to accommodate the free space for \a n elements at \a pos.
271
272
This function expects that certain preconditions are met, e.g. the
273
detach is not needed, n > 0 and so on. This is intentional to reduce the
274
number of if-statements when the caller knows that preconditions would
275
be satisfied.
276
277
\sa reallocateAndGrow
278
*/
279
bool
tryReadjustFreeSpace(QArrayData::GrowthPosition pos, qsizetype n,
const
T **data =
nullptr
)
280
{
281
Q_ASSERT(!
this
->needsDetach());
282
Q_ASSERT(n > 0);
283
Q_ASSERT((pos == QArrayData::GrowsAtEnd &&
this
->freeSpaceAtEnd() < n)
284
|| (pos == QArrayData::GrowsAtBeginning &&
this
->freeSpaceAtBegin() < n));
285
286
const
qsizetype capacity =
this
->constAllocatedCapacity();
287
const
qsizetype freeAtBegin =
this
->freeSpaceAtBegin();
288
const
qsizetype freeAtEnd =
this
->freeSpaceAtEnd();
289
290
qsizetype dataStartOffset = 0;
291
// algorithm:
292
// a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3
293
// [all goes to free space at end]:
294
// new free space at begin = 0
295
//
296
// b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3
297
// [balance the free space]:
298
// new free space at begin = n + (total free space - n) / 2
299
if
(pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
300
&& ((3 *
this
->size) < (2 * capacity))) {
301
// dataStartOffset = 0; - done in declaration
302
}
else
if
(pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
303
&& ((3 *
this
->size) < capacity)) {
304
// total free space == capacity - size
305
dataStartOffset = n + qMax(0, (capacity -
this
->size - n) / 2);
306
}
else
{
307
// nothing to do otherwise
308
return
false
;
309
}
310
311
relocate(dataStartOffset - freeAtBegin, data);
312
313
Q_ASSERT((pos == QArrayData::GrowsAtEnd &&
this
->freeSpaceAtEnd() >= n)
314
|| (pos == QArrayData::GrowsAtBeginning &&
this
->freeSpaceAtBegin() >= n));
315
return
true
;
316
}
317
318
/*! \internal
319
320
Relocates [begin(), end()) by \a offset and updates \a data if it is not
321
\c nullptr and points into [begin(), end()).
322
*/
323
void
relocate(qsizetype offset,
const
T **data =
nullptr
)
324
{
325
T *res =
this
->ptr + offset;
326
QtPrivate::q_relocate_overlap_n(
this
->ptr,
this
->size, res);
327
// first update data pointer, then this->ptr
328
if
(data && QtPrivate::q_points_into_range(*data, *
this
))
329
*data += offset;
330
this
->ptr = res;
331
}
332
333
QArrayDataPointer sliced(qsizetype pos, qsizetype n)
const
&
334
{
335
QArrayDataPointer result(n);
336
std::uninitialized_copy_n(begin() + pos, n, result.begin());
337
result.size = n;
338
return
result;
339
}
340
341
QArrayDataPointer sliced(qsizetype pos, qsizetype n) &&
342
{
343
if
(needsDetach())
344
return
sliced(pos, n);
345
T *newBeginning = begin() + pos;
346
std::destroy(begin(), newBeginning);
347
std::destroy(newBeginning + n, end());
348
setBegin(newBeginning);
349
size = n;
350
return
std::move(*
this
);
351
}
352
353
void
appendInitialize(qsizetype newSize)
354
{
355
Q_ASSERT(
this
->isMutable());
356
Q_ASSERT(!
this
->isShared());
357
Q_ASSERT(newSize >
this
->size);
358
Q_ASSERT(newSize -
this
->size <=
this
->freeSpaceAtEnd());
359
360
T *
const
b =
this
->begin() +
this
->size;
361
T *
const
e =
this
->begin() + newSize;
362
q17::uninitialized_value_construct(b, e);
363
this
->size = newSize;
364
}
365
366
// forwards from QArrayData
367
constexpr
qsizetype allocatedCapacity()
noexcept
{
return
d ? d->allocatedCapacity() : 0; }
368
constexpr
369
qsizetype constAllocatedCapacity()
const
noexcept
{
return
d ? d->constAllocatedCapacity() : 0; }
370
constexpr
void
ref()
noexcept
{
if
(d) d->ref(); }
371
constexpr
bool
deref()
noexcept
{
return
!d || d->deref(); }
372
constexpr
373
bool
isMutable()
const
noexcept
{
return
d; }
// Returns false if this object is fromRawData()
374
constexpr
bool
isShared()
const
noexcept
{
return
!d || d->isShared(); }
375
constexpr
376
bool
isSharedWith(
const
QArrayDataPointer &other)
const
noexcept
{
return
d && d == other.d; }
377
constexpr
bool
needsDetach()
const
noexcept
{
return
!d || d->needsDetach(); }
378
constexpr
379
qsizetype detachCapacity(qsizetype newSize)
const
noexcept
{
return
d ? d->detachCapacity(newSize) : newSize; }
380
constexpr
381
const
typename
Data::ArrayOptions flags()
const
noexcept
{
return
d ? d->flags : Data::ArrayOptionDefault; }
382
void
setFlag(
typename
Data::ArrayOptions f)
noexcept
{ Q_ASSERT(d); d->flags |= f; }
383
void
clearFlag(
typename
Data::ArrayOptions f)
noexcept
{
if
(d) d->flags &= ~f; }
384
385
constexpr
Data *d_ptr()
noexcept
{
return
d; }
386
constexpr
void
setBegin(T *begin)
noexcept
{ ptr = begin; }
387
388
constexpr
qsizetype freeSpaceAtBegin()
const
noexcept
389
{
390
if
(d ==
nullptr
)
391
return
0;
392
return
this
->ptr - Data::dataStart(d,
alignof
(
typename
Data::AlignmentDummy));
393
}
394
395
constexpr
qsizetype freeSpaceAtEnd()
const
noexcept
396
{
397
if
(d ==
nullptr
)
398
return
0;
399
return
d->constAllocatedCapacity() - freeSpaceAtBegin() -
this
->size;
400
}
401
402
// allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end
403
static
QArrayDataPointer allocateGrow(
const
QArrayDataPointer &from, qsizetype n, QArrayData::GrowthPosition position)
404
{
405
// calculate new capacity. We keep the free capacity at the side that does not have to grow
406
// to avoid quadratic behavior with mixed append/prepend cases
407
408
// use qMax below, because constAllocatedCapacity() can be 0 when using fromRawData()
409
qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
410
// subtract the free space at the side we want to allocate. This ensures that the total size requested is
411
// the existing allocation at the other side + size + n.
412
minimalCapacity -= (position == QArrayData::GrowsAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
413
qsizetype capacity = from.detachCapacity(minimalCapacity);
414
const
bool
grows = capacity > from.constAllocatedCapacity();
415
auto
[header, dataPtr] = Data::allocate(capacity, grows ? QArrayData::Grow : QArrayData::KeepSize);
416
const
bool
valid = header !=
nullptr
&& dataPtr !=
nullptr
;
417
if
(!valid)
418
return
QArrayDataPointer(header, dataPtr);
419
420
// Idea: * when growing backwards, adjust pointer to prepare free space at the beginning
421
// * when growing forward, adjust by the previous data pointer offset
422
dataPtr += (position == QArrayData::GrowsAtBeginning)
423
? n + qMax(0, (header->alloc - from.size - n) / 2)
424
: from.freeSpaceAtBegin();
425
header->flags = from.flags();
426
return
QArrayDataPointer(header, dataPtr);
427
}
428
429
friend
bool
operator==(
const
QArrayDataPointer &lhs,
const
QArrayDataPointer &rhs)
noexcept
430
{
431
return
lhs.data() == rhs.data() && lhs.size == rhs.size;
432
}
433
434
friend
bool
operator!=(
const
QArrayDataPointer &lhs,
const
QArrayDataPointer &rhs)
noexcept
435
{
436
return
lhs.data() != rhs.data() || lhs.size != rhs.size;
437
}
438
439
#
if
QT_VERSION
<
QT_VERSION_CHECK
(
7
,
0
,
0
)
&&
!
defined
(
QT_BOOTSTRAPPED
)
440
Data *d =
nullptr
;
441
#
endif
442
T *ptr =
nullptr
;
443
qsizetype size = 0;
444
#
if
QT_VERSION
>=
QT_VERSION_CHECK
(
7
,
0
,
0
)
||
defined
(
QT_BOOTSTRAPPED
)
445
Data *d =
nullptr
;
446
#
endif
447
};
448
449
template
<
class
T>
450
constexpr
void
swap
(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2)
noexcept
451
{
452
p1.swap(p2);
453
}
454
455
template
<
class
T>
456
Q_DECLARE_TYPEINFO_BODY
(
QArrayDataPointer
<
T
>,
Q_RELOCATABLE_TYPE
);
457
458
////////////////////////////////////////////////////////////////////////////////
459
// Q_ARRAY_LITERAL
460
461
// The idea here is to place a (read-only) copy of header and array data in an
462
// mmappable portion of the executable (typically, .rodata section).
463
464
// Hide array inside a lambda
465
#
define
Q_ARRAY_LITERAL
(
Type
,
...
)
466
(
[
]
(
)
->
QArrayDataPointer
<
Type
>
{
467
static
Type
const
data
[
]
=
{
__VA_ARGS__
}
;
468
return
QArrayDataPointer
<
Type
>
::
fromRawData
(
const_cast
<
Type
*
>
(
data
)
,
std
::
size
(
data
)
)
;
469
}
(
)
)
470
/**/
471
472
QT_END_NAMESPACE
473
474
#
endif
// include guard
swap
constexpr void swap(QArrayDataPointer< T > &p1, QArrayDataPointer< T > &p2) noexcept
Definition
qarraydatapointer.h:450
Q_DECLARE_TYPEINFO_BODY
Q_DECLARE_TYPEINFO_BODY(QArrayDataPointer< T >, Q_RELOCATABLE_TYPE)
qtbase
src
corelib
tools
qarraydatapointer.h
Generated on
for Qt by
1.18.0