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 QSHAREDDATA_H
41#define QSHAREDDATA_H
42
43#include <QtCore/qglobal.h>
44#include <QtCore/qatomic.h>
45#include <QtCore/qhashfunctions.h>
46
47#include <functional>
48
49QT_BEGIN_NAMESPACE
50
51
52template <class T> class QSharedDataPointer;
53
54class QSharedData
55{
56public:
57 mutable QAtomicInt ref;
58
59 QSharedData() noexcept : ref(0) { }
60 QSharedData(const QSharedData &) noexcept : ref(0) { }
61
62 // using the assignment operator would lead to corruption in the ref-counting
63 QSharedData &operator=(const QSharedData &) = delete;
64 ~QSharedData() = default;
65};
66
67struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; };
68
69template <typename T>
70class QSharedDataPointer
71{
72public:
73 typedef T Type;
74 typedef T *pointer;
75
76 void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
77 T &operator*() { detach(); return *d; }
78 const T &operator*() const { return *d; }
79 T *operator->() { detach(); return d; }
80 const T *operator->() const noexcept { return d; }
81 operator T *() { detach(); return d; }
82 operator const T *() const noexcept { return d; }
83 T *data() { detach(); return d; }
84 T *get() { detach(); return d; }
85 const T *data() const noexcept { return d; }
86 const T *get() const noexcept { return d; }
87 const T *constData() const noexcept { return d; }
88 T *take() noexcept { return qExchange(d, nullptr); }
89
90 QSharedDataPointer() noexcept : d(nullptr) { }
91 ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d; }
92
93 explicit QSharedDataPointer(T *data) noexcept : d(data)
94 { if (d) d->ref.ref(); }
95 QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data)
96 {}
97 QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d)
98 { if (d) d->ref.ref(); }
99
100 void reset(T *ptr = nullptr) noexcept
101 {
102 if (ptr != d) {
103 if (ptr)
104 ptr->ref.ref();
105 T *old = qExchange(d, ptr);
106 if (old && !old->ref.deref())
107 delete old;
108 }
109 }
110
111 QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept
112 {
113 reset(o.d);
114 return *this;
115 }
116 inline QSharedDataPointer &operator=(T *o) noexcept
117 {
118 reset(o);
119 return *this;
120 }
121 QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {}
122 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer)
123
124 operator bool () const noexcept { return d != nullptr; }
125 bool operator!() const noexcept { return d == nullptr; }
126
127 void swap(QSharedDataPointer &other) noexcept
128 { qSwap(d, other.d); }
129
130#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \
131 friend bool operator<(T1, T2) noexcept \
132 { return std::less<T*>{}(A1, A2); } \
133 friend bool operator<=(T1, T2) noexcept \
134 { return !std::less<T*>{}(A2, A1); } \
135 friend bool operator>(T1, T2) noexcept \
136 { return std::less<T*>{}(A2, A1); } \
137 friend bool operator>=(T1, T2) noexcept \
138 { return !std::less<T*>{}(A1, A2); } \
139 friend bool operator==(T1, T2) noexcept \
140 { return A1 == A2; } \
141 friend bool operator!=(T1, T2) noexcept \
142 { return A1 != A2; } \
143
144 DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const QSharedDataPointer &p2, p2.d)
145 DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const T *ptr, ptr)
146 DECLARE_COMPARE_SET(const T *ptr, ptr, const QSharedDataPointer &p2, p2.d)
147 DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, std::nullptr_t, nullptr)
148 DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QSharedDataPointer &p2, p2.d)
149
150protected:
151 T *clone();
152
153private:
154 void detach_helper();
155
156 T *d;
157};
158
159template <typename T>
160class QExplicitlySharedDataPointer
161{
162public:
163 typedef T Type;
164 typedef T *pointer;
165
166 T &operator*() const { return *d; }
167 T *operator->() noexcept { return d; }
168 T *operator->() const noexcept { return d; }
169 explicit operator T *() { return d; }
170 explicit operator const T *() const noexcept { return d; }
171 T *data() const noexcept { return d; }
172 T *get() const noexcept { return d; }
173 const T *constData() const noexcept { return d; }
174 T *take() noexcept { return qExchange(d, nullptr); }
175
176 void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
177
178 QExplicitlySharedDataPointer() noexcept : d(nullptr) { }
179 ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; }
180
181 explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data)
182 { if (d) d->ref.ref(); }
183 QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data)
184 {}
185 QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d)
186 { if (d) d->ref.ref(); }
187
188 template<typename X>
189 QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) noexcept
190#ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST
191 : d(static_cast<T *>(o.data()))
192#else
193 : d(o.data())
194#endif
195 { if (d) d->ref.ref(); }
196
197 void reset(T *ptr = nullptr) noexcept
198 {
199 if (ptr != d) {
200 if (ptr)
201 ptr->ref.ref();
202 T *old = qExchange(d, ptr);
203 if (old && !old->ref.deref())
204 delete old;
205 }
206 }
207
208 QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept
209 {
210 reset(o.d);
211 return *this;
212 }
213 QExplicitlySharedDataPointer &operator=(T *o) noexcept
214 {
215 reset(o);
216 return *this;
217 }
218 QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {}
219 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer)
220
221 operator bool () const noexcept { return d != nullptr; }
222 bool operator!() const noexcept { return d == nullptr; }
223
224 void swap(QExplicitlySharedDataPointer &other) noexcept
225 { qSwap(d, other.d); }
226
227 DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const QExplicitlySharedDataPointer &p2, p2.d)
228 DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const T *ptr, ptr)
229 DECLARE_COMPARE_SET(const T *ptr, ptr, const QExplicitlySharedDataPointer &p2, p2.d)
230 DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, std::nullptr_t, nullptr)
231 DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QExplicitlySharedDataPointer &p2, p2.d)
232
233#undef DECLARE_COMPARE_SET
234
235protected:
236 T *clone();
237
238private:
239 void detach_helper();
240
241 T *d;
242};
243
244// Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time.
245template <typename T>
246Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone()
247{
248 return new T(*d);
249}
250
251template <typename T>
252Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper()
253{
254 T *x = clone();
255 x->ref.ref();
256 if (!d->ref.deref())
257 delete d;
258 d = x;
259}
260
261template <typename T>
262Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone()
263{
264 return new T(*d);
265}
266
267template <typename T>
268Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper()
269{
270 T *x = clone();
271 x->ref.ref();
272 if (!d->ref.deref())
273 delete d;
274 d = x;
275}
276
277template <typename T>
278void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) noexcept
279{ p1.swap(p2); }
280
281template <typename T>
282void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept
283{ p1.swap(p2); }
284
285template <typename T>
286size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept
287{
288 return qHash(ptr.data(), seed);
289}
290template <typename T>
291size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept
292{
293 return qHash(ptr.data(), seed);
294}
295
296template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_MOVABLE_TYPE);
297template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_MOVABLE_TYPE);
298
299#define QT_DECLARE_QSDP_SPECIALIZATION_DTOR(Class) \
300 template<> QSharedDataPointer<Class>::~QSharedDataPointer();
301
302#define QT_DECLARE_QSDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \
303 template<> ExportMacro QSharedDataPointer<Class>::~QSharedDataPointer();
304
305#define QT_DEFINE_QSDP_SPECIALIZATION_DTOR(Class) \
306 template<> QSharedDataPointer<Class>::~QSharedDataPointer() \
307 { \
308 if (d && !d->ref.deref()) \
309 delete d; \
310 }
311
312#define QT_DECLARE_QESDP_SPECIALIZATION_DTOR(Class) \
313 template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer();
314
315#define QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \
316 template<> ExportMacro QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer();
317
318#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class) \
319 template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer() \
320 { \
321 if (d && !d->ref.deref()) \
322 delete d; \
323 }
324
325QT_END_NAMESPACE
326
327#endif // QSHAREDDATA_H
328