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
qarraydata.cpp
Go to the documentation of this file.
1
// Copyright (C) 2021 The Qt Company Ltd.
2
// Copyright (C) 2016 Intel Corporation.
3
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
// Qt-Security score:significant reason:default
5
6
#
include
<
QtCore
/
qalloc
.
h
>
7
#
include
<
QtCore
/
qarraydata
.
h
>
8
#
include
<
QtCore
/
private
/
qnumeric_p
.
h
>
9
#
include
<
QtCore
/
private
/
qtools_p
.
h
>
10
#
include
<
QtCore
/
qmath
.
h
>
11
12
#
include
<
QtCore
/
qbytearray
.
h
>
// QBA::value_type
13
#
include
<
QtCore
/
qstring
.
h
>
// QString::value_type
14
15
#
include
<
stdlib
.
h
>
16
17
QT_BEGIN_NAMESPACE
18
19
/*
20
* This pair of functions is declared in qtools_p.h and is used by the Qt
21
* containers to allocate memory and grow the memory block during append
22
* operations.
23
*
24
* They take qsizetype parameters and return qsizetype so they will change sizes
25
* according to the pointer width. However, knowing Qt containers store the
26
* container size and element indexes in ints, these functions never return a
27
* size larger than INT_MAX. This is done by casting the element count and
28
* memory block size to int in several comparisons: the check for negative is
29
* very fast on most platforms as the code only needs to check the sign bit.
30
*
31
* These functions return SIZE_MAX on overflow, which can be passed to malloc()
32
* and will surely cause a NULL return (there's no way you can allocate a
33
* memory block the size of your entire VM space).
34
*/
35
36
/*!
37
\internal
38
\since 5.7
39
40
Returns the memory block size for a container containing \a elementCount
41
elements, each of \a elementSize bytes, plus a header of \a headerSize
42
bytes. That is, this function returns \c
43
{elementCount * elementSize + headerSize}
44
45
but unlike the simple calculation, it checks for overflows during the
46
multiplication and the addition.
47
48
Both \a elementCount and \a headerSize can be zero, but \a elementSize
49
cannot.
50
51
This function returns -1 on overflow or if the memory block size
52
would not fit a qsizetype.
53
*/
54
qsizetype qCalculateBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize)
noexcept
55
{
56
Q_ASSERT(elementSize);
57
58
size_t bytes;
59
if
(Q_UNLIKELY(qMulOverflow(size_t(elementSize), size_t(elementCount), &bytes)) ||
60
Q_UNLIKELY(qAddOverflow(bytes, size_t(headerSize), &bytes)))
61
return
-1;
62
if
(Q_UNLIKELY(qsizetype(bytes) < 0))
63
return
-1;
64
65
return
qsizetype(bytes);
66
}
67
68
/*!
69
\internal
70
\since 5.7
71
72
Returns the memory block size and the number of elements that will fit in
73
that block for a container containing \a elementCount elements, each of \a
74
elementSize bytes, plus a header of \a headerSize bytes. This function
75
assumes the container will grow and pre-allocates a growth factor.
76
77
Both \a elementCount and \a headerSize can be zero, but \a elementSize
78
cannot.
79
80
This function returns -1 on overflow or if the memory block size
81
would not fit a qsizetype.
82
83
\note The memory block may contain up to \a elementSize - 1 bytes more than
84
needed.
85
*/
86
CalculateGrowingBlockSizeResult
87
qCalculateGrowingBlockSize
(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize)
noexcept
88
{
89
CalculateGrowingBlockSizeResult result = {
90
qsizetype(-1), qsizetype(-1)
91
};
92
93
qsizetype bytes = qCalculateBlockSize(elementCount, elementSize, headerSize);
94
if
(bytes < 0)
95
return
result;
96
97
size_t morebytes =
static_cast
<size_t>(qNextPowerOfTwo(quint64(bytes)));
98
if
(Q_UNLIKELY(qsizetype(morebytes) < 0)) {
99
// grow by half the difference between bytes and morebytes
100
// this slows the growth and avoids trying to allocate exactly
101
// 2G of memory (on 32bit), something that many OSes can't deliver
102
bytes += (morebytes - bytes) / 2;
103
}
else
{
104
bytes = qsizetype(morebytes);
105
}
106
size_t fittedBytes = QtPrivate::expectedAllocSize(bytes,
alignof
(std::max_align_t));
107
if
(fittedBytes != 0)
108
bytes = fittedBytes;
109
110
result.elementCount = (bytes - headerSize) / elementSize;
111
result.size = result.elementCount * elementSize + headerSize;
112
return
result;
113
}
114
115
using
QtPrivate::AlignedQArrayData;
116
117
static
qsizetype
calculateHeaderSize
(qsizetype alignment)
118
{
119
qsizetype headerSize =
sizeof
(AlignedQArrayData);
120
const
qsizetype headerAlignment =
alignof
(AlignedQArrayData);
121
122
if
(alignment > headerAlignment) {
123
// Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
124
// bytes so we can properly align the data array. This assumes malloc is
125
// able to provide appropriate alignment for the header -- as it should!
126
// Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy.
127
headerSize += alignment - headerAlignment;
128
}
129
Q_ASSERT(headerSize > 0);
130
131
return
headerSize;
132
}
133
134
/*
135
Calculate the byte size for a block of \a capacity objects of size \a
136
objectSize, with a header of size \a headerSize. If the \a option is
137
QArrayData::Grow, the capacity itself adjusted up, preallocating room for
138
more elements to be added later; otherwise, it is an exact calculation.
139
140
Returns a structure containing the size in bytes and elements available.
141
*/
142
static
inline
CalculateGrowingBlockSizeResult
143
calculateBlockSize
(qsizetype capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
144
{
145
// Adjust the header size up to account for the trailing null for QString
146
// and QByteArray. This is not checked for overflow because headers sizes
147
// should not be anywhere near the overflow limit.
148
constexpr
qsizetype FooterSize = qMax(
sizeof
(QString::value_type),
sizeof
(QByteArray::value_type));
149
if
(objectSize <= FooterSize)
150
headerSize += FooterSize;
151
152
// allocSize = objectSize * capacity + headerSize, but checked for overflow
153
// plus padded to grow in size
154
if
(option == QArrayData::Grow) {
155
return
qCalculateGrowingBlockSize(capacity, objectSize, headerSize);
156
}
else
{
157
return
{ qCalculateBlockSize(capacity, objectSize, headerSize), capacity };
158
}
159
}
160
161
static
inline
void
*
162
allocateHelper
(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, qsizetype capacity,
163
QArrayData::AllocationOption option)
noexcept
164
{
165
*dptr =
nullptr
;
166
if
(capacity <= 0)
167
return
{};
168
169
const
qsizetype headerSize = calculateHeaderSize(alignment);
170
Q_ASSERT(headerSize > 0);
171
172
auto
blockSize = calculateBlockSize(capacity, objectSize, headerSize, option);
173
capacity = blockSize.elementCount;
174
qsizetype allocSize = blockSize.size;
175
if
(Q_UNLIKELY(allocSize < 0))
// handle overflow. cannot allocate reliably
176
return
{};
177
178
void
*data =
nullptr
;
179
void
*mem = ::malloc(size_t(allocSize));
180
if
(Q_LIKELY(mem)) {
181
*dptr =
new
(mem) QArrayData{1, {}, capacity};
182
// find where offset should point to so that data() is aligned to alignment bytes
183
data = QTypedArrayData<
void
>::dataStart(*dptr, alignment);
184
}
185
186
return
data;
187
}
188
189
// Generic size and alignment allocation function
190
void
*QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment,
191
qsizetype capacity, AllocationOption option)
noexcept
192
{
193
Q_ASSERT(dptr);
194
// Alignment is a power of two
195
Q_ASSERT(alignment >= qsizetype(
alignof
(QArrayData))
196
&& !(alignment & (alignment - 1)));
197
198
return
allocateHelper(dptr, objectSize, alignment, capacity, option);
199
}
200
201
// Fixed size and alignment allocation functions
202
void
*QArrayData::allocate1(QArrayData **dptr, qsizetype capacity, AllocationOption option)
noexcept
203
{
204
Q_ASSERT(dptr);
205
206
return
allocateHelper(dptr, 1,
alignof
(AlignedQArrayData), capacity, option);
207
}
208
209
void
*QArrayData::allocate2(QArrayData **dptr, qsizetype capacity, AllocationOption option)
noexcept
210
{
211
Q_ASSERT(dptr);
212
213
return
allocateHelper(dptr, 2,
alignof
(AlignedQArrayData), capacity, option);
214
}
215
216
std::pair<QArrayData *,
void
*>
217
QArrayData::reallocateUnaligned(QArrayData *data,
void
*dataPointer,
218
qsizetype objectSize, qsizetype capacity, AllocationOption option)
noexcept
219
{
220
Q_ASSERT(!data || !data->isShared());
221
222
const
qsizetype headerSize =
sizeof
(AlignedQArrayData);
223
auto
r = calculateBlockSize(capacity, objectSize, headerSize, option);
224
qsizetype allocSize = r.size;
225
capacity = r.elementCount;
226
if
(Q_UNLIKELY(allocSize < 0))
227
return
{};
228
229
const
qptrdiff offset = dataPointer
230
?
reinterpret_cast
<
char
*>(dataPointer) -
reinterpret_cast
<
char
*>(data)
231
: headerSize;
232
Q_ASSERT(offset > 0);
233
Q_ASSERT(offset <= allocSize);
// equals when all free space is at the beginning
234
235
const
bool
hadData = data;
236
void
*mem = ::realloc(data, size_t(allocSize));
237
QArrayData *header =
static_cast
<QArrayData *>(mem);
238
if
(mem) {
239
if
(!hadData)
240
header =
new
(mem) QArrayData{0, {}, {}};
241
header->alloc = capacity;
242
dataPointer =
reinterpret_cast
<
char
*>(header) + offset;
243
}
else
{
244
dataPointer =
nullptr
;
245
}
246
return
{header, dataPointer};
247
}
248
249
void
QArrayData::deallocate(QArrayData *data, qsizetype objectSize,
250
qsizetype alignment)
noexcept
251
{
252
// Alignment is a power of two
253
Q_ASSERT(alignment >= qsizetype(
alignof
(QArrayData))
254
&& !(alignment & (alignment - 1)));
255
256
const
qsizetype capacity = data->alloc;
257
const
qsizetype headerSize = calculateHeaderSize(alignment);
258
Q_ASSERT(headerSize > 0);
259
const
auto
blockSize = calculateBlockSize(capacity, objectSize,
260
headerSize, QArrayData::KeepSize);
261
const
qsizetype allocSize = blockSize.size;
262
263
if
(Q_LIKELY(allocSize > 0))
264
QtPrivate::sizedFree(data, size_t(allocSize));
265
else
// something went wrong, fallback to slow free()
266
free(data);
267
}
268
269
QT_END_NAMESPACE
calculateBlockSize
static CalculateGrowingBlockSizeResult calculateBlockSize(qsizetype capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
Definition
qarraydata.cpp:143
allocateHelper
static void * allocateHelper(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, qsizetype capacity, QArrayData::AllocationOption option) noexcept
Definition
qarraydata.cpp:162
qCalculateGrowingBlockSize
CalculateGrowingBlockSizeResult qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize) noexcept
Definition
qarraydata.cpp:87
calculateHeaderSize
static qsizetype calculateHeaderSize(qsizetype alignment)
Definition
qarraydata.cpp:117
qtbase
src
corelib
tools
qarraydata.cpp
Generated on
for Qt by
1.18.0