Qt 6.x
The Qt SDK
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
115static inline qsizetype reserveExtraBytes(qsizetype allocSize)
116{
117 // We deal with QByteArray and QString only
118 constexpr qsizetype extra = qMax(sizeof(QByteArray::value_type), sizeof(QString::value_type));
119 if (Q_UNLIKELY(allocSize < 0))
120 return -1;
121 if (Q_UNLIKELY(qAddOverflow(allocSize, extra, &allocSize)))
122 return -1;
123 return allocSize;
124}
125
127{
128 // Calculate the byte size
129 // allocSize = objectSize * capacity + headerSize, but checked for overflow
130 // plus padded to grow in size
131 if (option == QArrayData::Grow) {
132 auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize);
133 capacity = r.elementCount;
134 return r.size;
135 } else {
136 return qCalculateBlockSize(capacity, objectSize, headerSize);
137 }
138}
139
141{
142 QArrayData *header = static_cast<QArrayData *>(::malloc(size_t(allocSize)));
143 if (header) {
144 header->ref_.storeRelaxed(1);
145 header->flags = {};
146 header->alloc = 0;
147 }
148 return header;
149}
150
151
152namespace {
153// QArrayData with strictest alignment requirements supported by malloc()
154struct alignas(std::max_align_t) AlignedQArrayData : QArrayData
155{
156};
157}
158
159
162{
163 Q_ASSERT(dptr);
164 // Alignment is a power of two
166 && !(alignment & (alignment - 1)));
167
168 if (capacity == 0) {
169 *dptr = nullptr;
170 return nullptr;
171 }
172
173 qsizetype headerSize = sizeof(AlignedQArrayData);
174 const qsizetype headerAlignment = alignof(AlignedQArrayData);
175
176 if (alignment > headerAlignment) {
177 // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
178 // bytes so we can properly align the data array. This assumes malloc is
179 // able to provide appropriate alignment for the header -- as it should!
180 headerSize += alignment - headerAlignment;
181 }
182 Q_ASSERT(headerSize > 0);
183
184 qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
185 allocSize = reserveExtraBytes(allocSize);
186 if (Q_UNLIKELY(allocSize < 0)) { // handle overflow. cannot allocate reliably
187 *dptr = nullptr;
188 return nullptr;
189 }
190
191 QArrayData *header = allocateData(allocSize);
192 void *data = nullptr;
193 if (header) {
194 // find where offset should point to so that data() is aligned to alignment bytes
196 header->alloc = qsizetype(capacity);
197 }
198
199 *dptr = header;
200 return data;
201}
202
205 qsizetype objectSize, qsizetype capacity, AllocationOption option) noexcept
206{
207 Q_ASSERT(!data || !data->isShared());
208
209 const qsizetype headerSize = sizeof(AlignedQArrayData);
210 qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
211 if (Q_UNLIKELY(allocSize < 0))
212 return qMakePair<QArrayData *, void *>(nullptr, nullptr);
213
214 const qptrdiff offset = dataPointer
215 ? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data)
216 : headerSize;
217 Q_ASSERT(offset > 0);
218 Q_ASSERT(offset <= allocSize); // equals when all free space is at the beginning
219
220 allocSize = reserveExtraBytes(allocSize);
221 if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot reallocate reliably
222 return qMakePair(data, dataPointer);
223
224 QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize)));
225 if (header) {
226 header->alloc = capacity;
227 dataPointer = reinterpret_cast<char *>(header) + offset;
228 } else {
229 dataPointer = nullptr;
230 }
231 return qMakePair(static_cast<QArrayData *>(header), dataPointer);
232}
233
235 qsizetype alignment) noexcept
236{
237 // Alignment is a power of two
239 && !(alignment & (alignment - 1)));
240 Q_UNUSED(objectSize);
242
243 ::free(data);
244}
245
char value_type
Definition qbytearray.h:450
\inmodule QtCore
Definition qchar.h:48
uint alignment
Combined button and popup list for selecting options.
static QArrayData * allocateData(qsizetype allocSize)
static qsizetype calculateBlockSize(qsizetype &capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
QT_BEGIN_NAMESPACE qsizetype qCalculateBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize) noexcept
CalculateGrowingBlockSizeResult qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizetype headerSize) noexcept
static qsizetype reserveExtraBytes(qsizetype allocSize)
#define Q_UNLIKELY(x)
std::pair< T1, T2 > QPair
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
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
ptrdiff_t qptrdiff
Definition qtypes.h:69
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
static Q_CORE_EXPORT QPair< QArrayData *, void * > reallocateUnaligned(QArrayData *data, void *dataPointer, qsizetype objectSize, qsizetype newCapacity, AllocationOption option) noexcept
static 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 T * dataStart(QArrayData *data, qsizetype alignment) noexcept
Definition qarraydata.h:127