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
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
5#include <QtCore/qarraydata.h>
6#include <QtCore/private/qnumeric_p.h>
7#include <QtCore/private/qtools_p.h>
8#include <QtCore/qmath.h>
9
10#include <QtCore/qbytearray.h> // QBA::value_type
11#include <QtCore/qstring.h> // QString::value_type
12
13#include <stdlib.h>
14
16
17/*
18 * This pair of functions is declared in qtools_p.h and is used by the Qt
19 * containers to allocate memory and grow the memory block during append
20 * operations.
21 *
22 * They take qsizetype parameters and return qsizetype so they will change sizes
23 * according to the pointer width. However, knowing Qt containers store the
24 * container size and element indexes in ints, these functions never return a
25 * size larger than INT_MAX. This is done by casting the element count and
26 * memory block size to int in several comparisons: the check for negative is
27 * very fast on most platforms as the code only needs to check the sign bit.
28 *
29 * These functions return SIZE_MAX on overflow, which can be passed to malloc()
30 * and will surely cause a NULL return (there's no way you can allocate a
31 * memory block the size of your entire VM space).
32 */
33
53{
54 Q_ASSERT(elementSize);
55
56 size_t bytes;
57 if (Q_UNLIKELY(qMulOverflow(size_t(elementSize), size_t(elementCount), &bytes)) ||
58 Q_UNLIKELY(qAddOverflow(bytes, size_t(headerSize), &bytes)))
59 return -1;
60 if (Q_UNLIKELY(qsizetype(bytes) < 0))
61 return -1;
62
63 return qsizetype(bytes);
64}
65
86{
88 qsizetype(-1), qsizetype(-1)
89 };
90
91 qsizetype bytes = qCalculateBlockSize(elementCount, elementSize, headerSize);
92 if (bytes < 0)
93 return result;
94
95 size_t morebytes = static_cast<size_t>(qNextPowerOfTwo(quint64(bytes)));
96 if (Q_UNLIKELY(qsizetype(morebytes) < 0)) {
97 // grow by half the difference between bytes and morebytes
98 // this slows the growth and avoids trying to allocate exactly
99 // 2G of memory (on 32bit), something that many OSes can't deliver
100 bytes += (morebytes - bytes) / 2;
101 } else {
102 bytes = qsizetype(morebytes);
103 }
104
105 result.elementCount = (bytes - headerSize) / elementSize;
106 result.size = result.elementCount * elementSize + headerSize;
107 return result;
108}
109
110/*
111 Calculate the byte size for a block of \a capacity objects of size \a
112 objectSize, with a header of size \a headerSize. If the \a option is
113 QArrayData::Grow, the capacity itself adjusted up, preallocating room for
114 more elements to be added later; otherwise, it is an exact calculation.
115
116 Returns a structure containing the size in bytes and elements available.
117*/
120{
121 // Adjust the header size up to account for the trailing null for QString
122 // and QByteArray. This is not checked for overflow because headers sizes
123 // should not be anywhere near the overflow limit.
124 constexpr qsizetype FooterSize = qMax(sizeof(QString::value_type), sizeof(QByteArray::value_type));
125 if (objectSize <= FooterSize)
126 headerSize += FooterSize;
127
128 // allocSize = objectSize * capacity + headerSize, but checked for overflow
129 // plus padded to grow in size
130 if (option == QArrayData::Grow) {
132 } else {
133 return { qCalculateBlockSize(capacity, objectSize, headerSize), capacity };
134 }
135}
136
138{
139 QArrayData *header = static_cast<QArrayData *>(::malloc(size_t(allocSize)));
140 if (header) {
141 header->ref_.storeRelaxed(1);
142 header->flags = {};
143 header->alloc = 0;
144 }
145 return header;
146}
147
148namespace {
149struct AllocationResult {
150 void *data;
151 QArrayData *header;
152};
153}
155
156static inline AllocationResult
159{
160 if (capacity == 0)
161 return {};
162
164 const qsizetype headerAlignment = alignof(AlignedQArrayData);
165
166 if (alignment > headerAlignment) {
167 // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
168 // bytes so we can properly align the data array. This assumes malloc is
169 // able to provide appropriate alignment for the header -- as it should!
170 // Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy.
171 headerSize += alignment - headerAlignment;
172 }
173 Q_ASSERT(headerSize > 0);
174
176 capacity = blockSize.elementCount;
177 qsizetype allocSize = blockSize.size;
178 if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot allocate reliably
179 return {};
180
181 QArrayData *header = allocateData(allocSize);
182 void *data = nullptr;
183 if (header) {
184 // find where offset should point to so that data() is aligned to alignment bytes
186 header->alloc = qsizetype(capacity);
187 }
188
189 return { data, header };
190}
191
192// Generic size and alignment allocation function
195{
196 Q_ASSERT(dptr);
197 // Alignment is a power of two
199 && !(alignment & (alignment - 1)));
200
201 auto r = allocateHelper(objectSize, alignment, capacity, option);
202 *dptr = r.header;
203 return r.data;
204}
205
206// Fixed size and alignment allocation functions
208{
209 Q_ASSERT(dptr);
210
211 auto r = allocateHelper(1, alignof(AlignedQArrayData), capacity, option);
212 *dptr = r.header;
213 return r.data;
214}
215
217{
218 Q_ASSERT(dptr);
219
220 auto r = allocateHelper(2, alignof(AlignedQArrayData), capacity, option);
221 *dptr = r.header;
222 return r.data;
223}
224
225std::pair<QArrayData *, void *>
227 qsizetype objectSize, qsizetype capacity, AllocationOption option) noexcept
228{
229 Q_ASSERT(!data || !data->isShared());
230
231 const qsizetype headerSize = sizeof(AlignedQArrayData);
232 auto r = calculateBlockSize(capacity, objectSize, headerSize, option);
233 qsizetype allocSize = r.size;
234 capacity = r.elementCount;
235 if (Q_UNLIKELY(allocSize < 0))
236 return {};
237
238 const qptrdiff offset = dataPointer
239 ? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data)
240 : headerSize;
241 Q_ASSERT(offset > 0);
242 Q_ASSERT(offset <= allocSize); // equals when all free space is at the beginning
243
244 QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize)));
245 if (header) {
246 header->alloc = capacity;
247 dataPointer = reinterpret_cast<char *>(header) + offset;
248 } else {
249 dataPointer = nullptr;
250 }
251 return {header, dataPointer};
252}
253
255 qsizetype alignment) noexcept
256{
257 // Alignment is a power of two
259 && !(alignment & (alignment - 1)));
260 Q_UNUSED(objectSize);
262
263 ::free(data);
264}
265
char value_type
Definition qbytearray.h:465
\inmodule QtCore
uint alignment
Combined button and popup list for selecting options.
static CalculateGrowingBlockSizeResult calculateBlockSize(qsizetype capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
static QArrayData * allocateData(qsizetype allocSize)
QT_BEGIN_NAMESPACE qsizetype qCalculateBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize) noexcept
CalculateGrowingBlockSizeResult qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize) noexcept
static AllocationResult allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity, QArrayData::AllocationOption option) noexcept
const int blockSize
#define Q_UNLIKELY(x)
static QString header(const QString &name)
static const qint64 headerSize
NSUInteger capacity
constexpr quint32 qNextPowerOfTwo(quint32 v)
Definition qmath.h:335
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
std::enable_if_t< std::is_unsigned_v< T >||std::is_signed_v< T >, bool > qMulOverflow(T v1, T v2, T *r)
Definition qnumeric.h:182
GLboolean r
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint64EXT * result
[6]
GLuint GLenum option
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
ptrdiff_t qptrdiff
Definition qtypes.h:164
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
static Q_CORE_EXPORT std::pair< QArrayData *, void * > reallocateUnaligned(QArrayData *data, void *dataPointer, qsizetype objectSize, qsizetype newCapacity, AllocationOption option) noexcept
static Q_DECL_MALLOCLIKE Q_CORE_EXPORT void * allocate(QArrayData **pdata, qsizetype objectSize, qsizetype alignment, qsizetype capacity, AllocationOption option=QArrayData::KeepSize) noexcept
static Q_CORE_EXPORT void deallocate(QArrayData *data, qsizetype objectSize, qsizetype alignment) noexcept
static Q_DECL_MALLOCLIKE Q_CORE_EXPORT void * allocate2(QArrayData **pdata, qsizetype capacity, AllocationOption option=QArrayData::KeepSize) noexcept
static Q_DECL_MALLOCLIKE Q_CORE_EXPORT void * allocate1(QArrayData **pdata, qsizetype capacity, AllocationOption option=QArrayData::KeepSize) noexcept
static T * dataStart(QArrayData *data, qsizetype alignment) noexcept
Definition qarraydata.h:165