1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QARRAYDATAPOINTER_H
41#define QARRAYDATAPOINTER_H
42
43#include <QtCore/qarraydataops.h>
44#include <QtCore/qcontainertools_impl.h>
45
46QT_BEGIN_NAMESPACE
47
48template <class T>
49struct QArrayDataPointer
50{
51private:
52 typedef QTypedArrayData<T> Data;
53 typedef QArrayDataOps<T> DataOps;
54
55public:
56 typedef typename Data::iterator iterator;
57 typedef typename Data::const_iterator const_iterator;
58 enum {
59 pass_parameter_by_value =
60 std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_enum<T>::value
61 };
62
63 typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
64
65 constexpr QArrayDataPointer() noexcept
66 : d(nullptr), ptr(nullptr), size(0)
67 {
68 }
69
70 QArrayDataPointer(const QArrayDataPointer &other) noexcept
71 : d(other.d), ptr(other.ptr), size(other.size)
72 {
73 ref();
74 }
75
76 constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n = 0) noexcept
77 : d(header), ptr(adata), size(n)
78 {
79 }
80
81 explicit QArrayDataPointer(QPair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
82 : d(adata.first), ptr(adata.second), size(n)
83 {
84 }
85
86 static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
87 {
88 Q_ASSERT(rawData || !length);
89 return { nullptr, const_cast<T *>(rawData), length };
90 }
91
92 QArrayDataPointer &operator=(const QArrayDataPointer &other) noexcept
93 {
94 QArrayDataPointer tmp(other);
95 this->swap(tmp);
96 return *this;
97 }
98
99 QArrayDataPointer(QArrayDataPointer &&other) noexcept
100 : d(other.d), ptr(other.ptr), size(other.size)
101 {
102 other.d = nullptr;
103 other.ptr = nullptr;
104 other.size = 0;
105 }
106
107 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
108
109 DataOps &operator*() noexcept
110 {
111 return *static_cast<DataOps *>(this);
112 }
113
114 DataOps *operator->() noexcept
115 {
116 return static_cast<DataOps *>(this);
117 }
118
119 const DataOps &operator*() const noexcept
120 {
121 return *static_cast<const DataOps *>(this);
122 }
123
124 const DataOps *operator->() const noexcept
125 {
126 return static_cast<const DataOps *>(this);
127 }
128
129 ~QArrayDataPointer()
130 {
131 if (!deref()) {
132 (*this)->destroyAll();
133 Data::deallocate(d);
134 }
135 }
136
137 bool isNull() const noexcept
138 {
139 return !ptr;
140 }
141
142 T *data() noexcept { return ptr; }
143 const T *data() const noexcept { return ptr; }
144
145 iterator begin(iterator = iterator()) noexcept { return data(); }
146 iterator end(iterator = iterator()) noexcept { return data() + size; }
147 const_iterator begin(const_iterator = const_iterator()) const noexcept { return data(); }
148 const_iterator end(const_iterator = const_iterator()) const noexcept { return data() + size; }
149 const_iterator constBegin(const_iterator = const_iterator()) const noexcept { return data(); }
150 const_iterator constEnd(const_iterator = const_iterator()) const noexcept { return data() + size; }
151
152 void swap(QArrayDataPointer &other) noexcept
153 {
154 qSwap(d, other.d);
155 qSwap(ptr, other.ptr);
156 qSwap(size, other.size);
157 }
158
159 void clear() noexcept(std::is_nothrow_destructible<T>::value)
160 {
161 QArrayDataPointer tmp;
162 swap(tmp);
163 }
164
165 bool detach()
166 {
167 if (needsDetach()) {
168 QPair<Data *, T *> copy = clone(detachFlags());
169 QArrayDataPointer old(d, ptr, size);
170 d = copy.first;
171 ptr = copy.second;
172 return true;
173 }
174
175 return false;
176 }
177
178 // forwards from QArrayData
179 qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
180 qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
181 void ref() noexcept { if (d) d->ref(); }
182 bool deref() noexcept { return !d || d->deref(); }
183 bool isMutable() const noexcept { return d; }
184 bool isShared() const noexcept { return !d || d->isShared(); }
185 bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
186 bool needsDetach() const noexcept { return !d || d->needsDetach(); }
187 qsizetype detachCapacity(qsizetype newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
188 const typename Data::ArrayOptions flags() const noexcept { return d ? typename Data::ArrayOption(d->flags) : Data::DefaultAllocationFlags; }
189 void setFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags |= f; }
190 void clearFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags &= ~f; }
191 typename Data::ArrayOptions detachFlags() const noexcept { return d ? d->detachFlags() : Data::DefaultAllocationFlags; }
192
193 Data *d_ptr() noexcept { return d; }
194 void setBegin(T *begin) noexcept { ptr = begin; }
195
196 qsizetype freeSpaceAtBegin() const noexcept
197 {
198 if (d == nullptr)
199 return 0;
200 return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
201 }
202
203 qsizetype freeSpaceAtEnd() const noexcept
204 {
205 if (d == nullptr)
206 return 0;
207 return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
208 }
209
210 static QArrayDataPointer allocateGrow(const QArrayDataPointer &from,
211 qsizetype newSize, QArrayData::ArrayOptions options)
212 {
213 return allocateGrow(from, from.detachCapacity(newSize), newSize, options);
214 }
215
216 static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype capacity,
217 qsizetype newSize, QArrayData::ArrayOptions options)
218 {
219 auto [header, dataPtr] = Data::allocate(capacity, options);
220 const bool valid = header != nullptr && dataPtr != nullptr;
221 const bool grows = (options & (Data::GrowsForward | Data::GrowsBackwards));
222 if (!valid || !grows)
223 return QArrayDataPointer(header, dataPtr);
224
225 // when growing, special rules apply to memory layout
226
227 if (from.needsDetach()) {
228 // When detaching: the free space reservation is biased towards
229 // append as in Qt5 QList. If we're growing backwards, put the data
230 // in the middle instead of at the end - assuming that prepend is
231 // uncommon and even initial prepend will eventually be followed by
232 // at least some appends.
233 if (options & Data::GrowsBackwards)
234 dataPtr += (header->alloc - newSize) / 2;
235 } else {
236 // When not detaching: fake ::realloc() policy - preserve existing
237 // free space at beginning.
238 dataPtr += from.freeSpaceAtBegin();
239 }
240 return QArrayDataPointer(header, dataPtr);
241 }
242
243private:
244 [[nodiscard]] QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
245 {
246 QPair<Data *, T *> pair = Data::allocate(detachCapacity(size), options);
247 QArrayDataPointer copy(pair.first, pair.second, 0);
248 if (size)
249 copy->copyAppend(begin(), end());
250
251 pair.first = copy.d;
252 copy.d = nullptr;
253 copy.ptr = nullptr;
254 return pair;
255 }
256
257protected:
258 Data *d;
259 T *ptr;
260
261public:
262 qsizetype size;
263};
264
265template <class T>
266inline bool operator==(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) noexcept
267{
268 return lhs.data() == rhs.data() && lhs.size == rhs.size;
269}
270
271template <class T>
272inline bool operator!=(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) noexcept
273{
274 return lhs.data() != rhs.data() || lhs.size != rhs.size;
275}
276
277template <class T>
278inline void qSwap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) noexcept
279{
280 p1.swap(p2);
281}
282
283////////////////////////////////////////////////////////////////////////////////
284// Q_ARRAY_LITERAL
285
286// The idea here is to place a (read-only) copy of header and array data in an
287// mmappable portion of the executable (typically, .rodata section).
288
289// Hide array inside a lambda
290#define Q_ARRAY_LITERAL(Type, ...) \
291 ([]() -> QArrayDataPointer<Type> { \
292 static Type const data[] = { __VA_ARGS__ }; \
293 return QArrayDataPointer<Type>::fromRawData(const_cast<Type *>(data), std::size(data)); \
294 }())
295/**/
296
297QT_END_NAMESPACE
298
299#endif // include guard
300