Revision control

++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QSHAREDDATA_H
++ #define QSHAREDDATA_H
++
++ #include <QtCore/qatomic.h>
++ #include <QtCore/qcompare.h>
++ #include <QtCore/qhashfunctions.h>
++
++ QT_BEGIN_NAMESPACE
++
++ template <class T> class QSharedDataPointer;
++ template <class T> class QExplicitlySharedDataPointer;
++
++ namespace QtPrivate {
++ template <template <typename> class P, typename T> struct QSharedDataPointerTraits;
++ template <typename T> struct QSharedDataPointerTraits<QSharedDataPointer, T>
++ {
++ static constexpr bool ImplicitlyDetaches = true;
++ using Type = T;
++ using pointer = T *;
++ // for const-qualified functions:
++ using constT = const T;
++ };
++
++ template <typename T> struct QSharedDataPointerTraits<QExplicitlySharedDataPointer, T>
++ {
++ static constexpr bool ImplicitlyDetaches = false;
++ using Type = T;
++ using pointer = T *;
++ // for const-qualified functions:
++ using constT = T;
++ };
++ }
++
++ class QSharedData
++ {
++ public:
++ mutable QAtomicInt ref;
++
++ QSharedData() noexcept : ref(0) { }
++ QSharedData(const QSharedData &) noexcept : ref(0) { }
++
++ // using the assignment operator would lead to corruption in the ref-counting
++ QSharedData &operator=(const QSharedData &) = delete;
++ ~QSharedData() = default;
++ };
++
++ struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; };
++
++ // CRTP common base class for both QSharedDataPointer and QExplicitlySharedDataPointer
++ template <template <typename> class P, typename T> class QSharedDataPointerBase
++ {
++ #ifndef Q_QDOC
++ using Self = P<T>;
++ using Traits = QtPrivate::QSharedDataPointerTraits<P, T>;
++ using constT = typename Traits::constT;
++
++ protected:
++ constexpr QSharedDataPointerBase(T *ptr = nullptr) noexcept : d(ptr) {}
++ QT_DECLARE_RO5_SMF_AS_DEFAULTED(QSharedDataPointerBase)
++
++ public:
++ // When adding anything public to this class, make sure to add the doc version to
++ // both QSharedDataPointer and QExplicitlySharedDataPointer.
++
++ using Type = T;
++ using pointer = T *;
++
++ void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
++ T &operator*() { detachIfImplicit(); return *(d.get()); }
++ constT &operator*() const { return *(d.get()); }
++ T *operator->() { detachIfImplicit(); return d.get(); }
++ constT *operator->() const noexcept { return d.get(); }
++ operator T *() { detachIfImplicit(); return d.get(); }
++ operator const T *() const noexcept { return d.get(); }
++ T *data() { detachIfImplicit(); return d.get(); }
++ T *get() { detachIfImplicit(); return d.get(); }
++ const T *data() const noexcept { return d.get(); }
++ const T *get() const noexcept { return d.get(); }
++ const T *constData() const noexcept { return d.get(); }
++ T *take() noexcept { return std::exchange(d, nullptr).get(); }
++
++ void reset(T *ptr = nullptr) noexcept
++ {
++ if (ptr != d.get()) {
++ if (ptr)
++ ptr->ref.ref();
++ T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get();
++ if (old && !old->ref.deref())
++ destroy(old);
++ }
++ }
++
++ void clearPayload()
++ {
++ if (d && (d->ref.loadAcquire() == 1 || !d->ref.deref())) {
++ // reference was 1, so we can reuse the storage
++ d->ref.storeRelaxed(1);
++ d->clear();
++ } else {
++ // reference was >= 2 before we dropped the count
++ d.reset(nullptr); // in case new or T::T() throws
++ T *x = create();
++ x->ref.ref();
++ d.reset(x);
++ }
++ }
++
++ operator bool () const noexcept { return d != nullptr; }
++ bool operator!() const noexcept { return d == nullptr; }
++
++ void swap(Self &other) noexcept
++ { qt_ptr_swap(d, other.d); }
++
++ private:
++ // The concrete class MUST override these, otherwise we will be calling
++ // ourselves.
++ T *clone() { return static_cast<Self *>(this)->clone(); }
++ template <typename... Args> static T *create(Args &&... args)
++ { return Self::create(std::forward(args)...); }
++ static void destroy(T *ptr) { Self::destroy(ptr); }
++
++ void detachIfImplicit()
++ {
++ if constexpr (Traits::ImplicitlyDetaches)
++ static_cast<Self *>(this)->detach();
++ }
++
++ friend bool comparesEqual(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept
++ { return lhs.d == rhs.d; }
++ friend Qt::strong_ordering
++ compareThreeWay(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept
++ { return Qt::compareThreeWay(lhs.d, rhs.d); }
++
++ friend bool comparesEqual(const QSharedDataPointerBase &lhs, const T *rhs) noexcept
++ { return lhs.d == rhs; }
++ friend Qt::strong_ordering
++ compareThreeWay(const QSharedDataPointerBase &lhs, const T *rhs) noexcept
++ { return Qt::compareThreeWay(lhs.d, rhs); }
++
++ friend bool comparesEqual(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept
++ { return lhs.d == nullptr; }
++ friend Qt::strong_ordering
++ compareThreeWay(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept
++ { return Qt::compareThreeWay(lhs.d, nullptr); }
++
++ friend size_t qHash(const QSharedDataPointerBase &ptr, size_t seed = 0) noexcept
++ { return qHash(ptr.data(), seed); }
++
++ protected:
++ void detach_helper();
++
++ Qt::totally_ordered_wrapper<T *> d;
++ #endif // !Q_QDOC
++ };
++
++ template <typename T>
++ class QSharedDataPointer : public QSharedDataPointerBase<QSharedDataPointer, T>
++ {
++ using Base = QSharedDataPointerBase<QSharedDataPointer, T>;
++ friend Base;
++ public:
++ typedef T Type;
++ typedef T *pointer;
++
++ void detach() { Base::detach(); }
++ #ifdef Q_QDOC
++ T &operator*();
++ const T &operator*() const;
++ T *operator->();
++ const T *operator->() const noexcept;
++ operator T *();
++ operator const T *() const noexcept;
++ T *data();
++ T *get();
++ const T *data() const noexcept;
++ const T *get() const noexcept;
++ const T *constData() const noexcept;
++ T *take() noexcept;
++ #endif
++
++ Q_NODISCARD_CTOR
++ QSharedDataPointer() noexcept : Base(nullptr) { }
++ ~QSharedDataPointer() { if (d && !d->ref.deref()) destroy(d.get()); }
++
++ Q_NODISCARD_CTOR
++ explicit QSharedDataPointer(T *data) noexcept : Base(data)
++ { if (d) d->ref.ref(); }
++ Q_NODISCARD_CTOR
++ QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data)
++ {}
++ Q_NODISCARD_CTOR
++ QSharedDataPointer(const QSharedDataPointer &o) noexcept : Base(o.d.get())
++ { if (d) d->ref.ref(); }
++
++ QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept
++ {
++ reset(o.d.get());
++ return *this;
++ }
++ inline QSharedDataPointer &operator=(T *o) noexcept
++ {
++ reset(o);
++ return *this;
++ }
++ Q_NODISCARD_CTOR
++ QSharedDataPointer(QSharedDataPointer &&o) noexcept
++ : Base(std::exchange(o.d, nullptr).get())
++ {}
++ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer)
++
++ #ifdef Q_QDOC
++ void reset(T *ptr = nullptr) noexcept;
++ void clearPayload();
++
++ operator bool () const noexcept;
++ bool operator!() const noexcept;
++
++ void swap(QSharedDataPointer &other) noexcept;
++ #else
++ using Base::reset;
++ using Base::swap;
++ #endif
++
++ protected:
++ T *clone();
++ template <typename... Args> static T *create(Args &&... args)
++ { return new T(std::forward(args)...); }
++ static void destroy(T *ptr) { delete ptr; }
++
++ private:
++ Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer)
++ Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, T*)
++ Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, std::nullptr_t)
++
++ using Base::d;
++ };
++
++ template <typename T>
++ class QExplicitlySharedDataPointer : public QSharedDataPointerBase<QExplicitlySharedDataPointer, T>
++ {
++ using Base = QSharedDataPointerBase<QExplicitlySharedDataPointer, T>;
++ friend Base;
++ public:
++ typedef T Type;
++ typedef T *pointer;
++
++ // override to make explicit. Can use explicit(!ImplicitlyShared) once we
++ // can depend on C++20.
++ explicit operator T *() { return d.get(); }
++ explicit operator const T *() const noexcept { return d.get(); }
++
++ // override to make const. There is no const(cond), but we could use
++ // requires(!ImplicitlyShared)
++ T *data() const noexcept { return d.get(); }
++ T *get() const noexcept { return d.get(); }
++
++ #ifdef Q_QDOC
++ T &operator*() const;
++ T *operator->() noexcept;
++ T *operator->() const noexcept;
++ T *data() const noexcept;
++ T *get() const noexcept;
++ const T *constData() const noexcept;
++ T *take() noexcept;
++ #endif
++
++ void detach() { Base::detach(); }
++
++ Q_NODISCARD_CTOR
++ QExplicitlySharedDataPointer() noexcept : Base(nullptr) { }
++ ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); }
++
++ Q_NODISCARD_CTOR
++ explicit QExplicitlySharedDataPointer(T *data) noexcept : Base(data)
++ { if (d) d->ref.ref(); }
++ Q_NODISCARD_CTOR
++ QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data)
++ {}
++ Q_NODISCARD_CTOR
++ QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : Base(o.d.get())
++ { if (d) d->ref.ref(); }
++
++ template<typename X>
++ Q_NODISCARD_CTOR
++ QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) noexcept
++ #ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST
++ #error This macro has been removed in Qt 6.9.
++ #endif
++ : Base(o.data())
++ { if (d) d->ref.ref(); }
++
++ QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept
++ {
++ reset(o.d.get());
++ return *this;
++ }
++ QExplicitlySharedDataPointer &operator=(T *o) noexcept
++ {
++ reset(o);
++ return *this;
++ }
++ Q_NODISCARD_CTOR
++ QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept
++ : Base(std::exchange(o.d, nullptr).get())
++ {}
++ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer)
++
++ #ifdef Q_QDOC
++ void reset(T *ptr = nullptr) noexcept;
++ void clearPayload();
++
++ operator bool () const noexcept;
++ bool operator!() const noexcept;
++
++ void swap(QExplicitlySharedDataPointer &other) noexcept;
++ #else
++ using Base::swap;
++ using Base::reset;
++ #endif
++
++ protected:
++ T *clone();
++ template <typename... Args> static T *create(Args &&... args)
++ { return new T(std::forward(args)...); }
++ static void destroy(T *ptr) { delete ptr; }
++
++ private:
++ Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer)
++ Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, const T*)
++ Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, std::nullptr_t)
++
++ using Base::d;
++ };
++
++ // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time.
++ template <typename T>
++ Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone()
++ {
++ return new T(*this->d);
++ }
++
++ template <typename T>
++ Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone()
++ {
++ return new T(*this->d.get());
++ }
++
++ template <template <typename> class P, typename T> Q_OUTOFLINE_TEMPLATE void
++ QSharedDataPointerBase<P, T>::detach_helper()
++ {
++ T *x = clone();
++ x->ref.ref();
++ if (!d->ref.deref())
++ destroy(d.get());
++ d.reset(x);
++ }
++
++ template <typename T>
++ void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) noexcept
++ { p1.swap(p2); }
++
++ template <typename T>
++ void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept
++ { p1.swap(p2); }
++
++ template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE);
++ template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_RELOCATABLE_TYPE);
++
++ #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR_COMMON(Pointer, Class, ExportMacro) \
++ template<> ExportMacro Pointer<Class>::~Pointer();
++ #define QT_DEFINE_QSDP_SPECIALIZATION_DTOR_COMMON(Pointer, Class, Inline) \
++ template<> Inline Pointer<Class>::~Pointer() \
++ { \
++ if (d && !d->ref.deref()) \
++ delete d.get(); \
++ }
++
++ #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR(Class) \
++ QT_DECLARE_QSDP_SPECIALIZATION_DTOR_COMMON(QSharedDataPointer, Class, )
++
++ #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \
++ QT_DECLARE_QSDP_SPECIALIZATION_DTOR_COMMON(QSharedDataPointer, Class, ExportMacro)
++
++ #define QT_DEFINE_QSDP_SPECIALIZATION_DTOR(Class) \
++ QT_DEFINE_QSDP_SPECIALIZATION_DTOR_COMMON(QSharedDataPointer, Class, )
++
++ #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR(Class) \
++ QT_DECLARE_QSDP_SPECIALIZATION_DTOR_COMMON(QExplicitlySharedDataPointer, Class, )
++
++ #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \
++ QT_DECLARE_QSDP_SPECIALIZATION_DTOR_COMMON(QExplicitlySharedDataPointer, Class, ExportMacro)
++
++ #define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class) \
++ QT_DEFINE_QSDP_SPECIALIZATION_DTOR_COMMON(QExplicitlySharedDataPointer, Class, )
++
++ QT_END_NAMESPACE
++
++ #endif // QSHAREDDATA_H