diff options
Diffstat (limited to 'src/corelib/tools/qarraydata.h')
-rw-r--r-- | src/corelib/tools/qarraydata.h | 310 |
1 files changed, 94 insertions, 216 deletions
diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 2515c1ae5a..da83fc1a21 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -1,120 +1,72 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2019 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2019 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QARRAYDATA_H #define QARRAYDATA_H #include <QtCore/qpair.h> #include <QtCore/qatomic.h> +#include <QtCore/qflags.h> +#include <QtCore/qcontainerfwd.h> #include <string.h> QT_BEGIN_NAMESPACE +#if __has_cpp_attribute(gnu::malloc) +# define Q_DECL_MALLOCLIKE [[nodiscard, gnu::malloc]] +#else +# define Q_DECL_MALLOCLIKE [[nodiscard]] +#endif + template <class T> struct QTypedArrayData; -struct Q_CORE_EXPORT QArrayData +struct QArrayData { - enum ArrayOption { - RawDataType = 0x0001, //!< this class is really a QArrayData - AllocatedDataType = 0x0002, //!< this class is really a QArrayAllocatedData - DataTypeBits = 0x000f, - - CapacityReserved = 0x0010, //!< the capacity was reserved by the user, try to keep it - GrowsForward = 0x0020, //!< allocate with eyes towards growing through append() - GrowsBackwards = 0x0040, //!< allocate with eyes towards growing through prepend() - MutableData = 0x0080, //!< the data can be changed; doesn't say anything about the header - ImmutableHeader = 0x0100, //!< the header is static, it can't be changed - - /// this option is used by the Q_ARRAY_LITERAL and similar macros - StaticDataFlags = RawDataType | ImmutableHeader, - /// this option is used by the allocate() function - DefaultAllocationFlags = MutableData, - /// this option is used by the prepareRawData() function - DefaultRawFlags = 0 + enum AllocationOption { + Grow, + KeepSize + }; + + enum GrowthPosition { + GrowsAtEnd, + GrowsAtBeginning + }; + + enum ArrayOption { + ArrayOptionDefault = 0, + CapacityReserved = 0x1 //!< the capacity was reserved by the user, try to keep it }; Q_DECLARE_FLAGS(ArrayOptions, ArrayOption) QBasicAtomicInt ref_; - uint flags; - uint alloc; + ArrayOptions flags; + qsizetype alloc; - inline size_t allocatedCapacity() + qsizetype allocatedCapacity() noexcept { return alloc; } - inline size_t constAllocatedCapacity() const + qsizetype constAllocatedCapacity() const noexcept { return alloc; } /// Returns true if sharing took place - bool ref() + bool ref() noexcept { - if (!isStatic()) - ref_.ref(); + ref_.ref(); return true; } /// Returns false if deallocation is necessary - bool deref() + bool deref() noexcept { - if (isStatic()) - return true; return ref_.deref(); } - // This refers to array data mutability, not "header data" represented by - // data members in QArrayData. Shared data (array and header) must still - // follow COW principles. - bool isMutable() const - { - return flags & MutableData; - } - - bool isStatic() const - { - return flags & ImmutableHeader; - } - - bool isShared() const + bool isShared() const noexcept { return ref_.loadRelaxed() != 1; } @@ -122,186 +74,110 @@ struct Q_CORE_EXPORT QArrayData // Returns true if a detach is necessary before modifying the data // This method is intentionally not const: if you want to know whether // detaching is necessary, you should be in a non-const function already - bool needsDetach() + bool needsDetach() noexcept { - // requires two conditionals - return !isMutable() || isShared(); + return ref_.loadRelaxed() > 1; } - size_t detachCapacity(size_t newSize) const + qsizetype detachCapacity(qsizetype newSize) const noexcept { if (flags & CapacityReserved && newSize < constAllocatedCapacity()) return constAllocatedCapacity(); return newSize; } - ArrayOptions detachFlags() const - { - ArrayOptions result = DefaultAllocationFlags; - if (flags & CapacityReserved) - result |= CapacityReserved; - return result; - } - - ArrayOptions cloneFlags() const - { - ArrayOptions result = DefaultAllocationFlags; - if (flags & CapacityReserved) - result |= CapacityReserved; - return result; - } - - Q_REQUIRED_RESULT -#if defined(Q_CC_GNU) - __attribute__((__malloc__)) -#endif - static void *allocate(QArrayData **pdata, size_t objectSize, size_t alignment, - size_t capacity, ArrayOptions options = DefaultAllocationFlags) noexcept; - Q_REQUIRED_RESULT static QArrayData *reallocateUnaligned(QArrayData *data, size_t objectSize, - size_t newCapacity, ArrayOptions newOptions = DefaultAllocationFlags) noexcept; - Q_REQUIRED_RESULT static QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer, - size_t objectSize, size_t newCapacity, ArrayOptions newOptions = DefaultAllocationFlags) Q_DECL_NOTHROW; - Q_REQUIRED_RESULT static QArrayData *prepareRawData(ArrayOptions options = ArrayOptions(RawDataType)) - Q_DECL_NOTHROW; - static void deallocate(QArrayData *data, size_t objectSize, - size_t alignment) noexcept; - - static const QArrayData shared_null[2]; - static QArrayData *sharedNull() noexcept { return const_cast<QArrayData*>(shared_null); } - static void *sharedNullData() - { - QArrayData *const null = const_cast<QArrayData *>(&shared_null[1]); - return null; - } + Q_DECL_MALLOCLIKE + static Q_CORE_EXPORT void *allocate(QArrayData **pdata, qsizetype objectSize, qsizetype alignment, + qsizetype capacity, AllocationOption option = QArrayData::KeepSize) noexcept; + Q_DECL_MALLOCLIKE + static Q_CORE_EXPORT void *allocate1(QArrayData **pdata, qsizetype capacity, + AllocationOption option = QArrayData::KeepSize) noexcept; + Q_DECL_MALLOCLIKE + static Q_CORE_EXPORT void *allocate2(QArrayData **pdata, qsizetype capacity, + AllocationOption option = QArrayData::KeepSize) noexcept; + + [[nodiscard]] static Q_CORE_EXPORT std::pair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer, + qsizetype objectSize, qsizetype newCapacity, AllocationOption option) noexcept; + static Q_CORE_EXPORT void deallocate(QArrayData *data, qsizetype objectSize, + qsizetype alignment) noexcept; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::ArrayOptions) -template <class T, size_t N> -struct QStaticArrayData -{ - // static arrays are of type RawDataType - QArrayData header; - T data[N]; -}; +namespace QtPrivate { +// QArrayData with strictest alignment requirements supported by malloc() +#if defined(Q_PROCESSOR_X86_32) && defined(Q_CC_GNU) +// GCC's definition is incorrect since GCC 8 (commit r240248 in SVN; commit +// 63012d9a57edc950c5f30242d1e19318b5708060 in Git). This is applied to all +// GCC-like compilers in case they decide to follow GCC's lead in being wrong. +constexpr size_t MaxPrimitiveAlignment = 2 * sizeof(void *); +#else +constexpr size_t MaxPrimitiveAlignment = alignof(std::max_align_t); +#endif -// Support for returning QArrayDataPointer<T> from functions -template <class T> -struct QArrayDataPointerRef +struct alignas(MaxPrimitiveAlignment) AlignedQArrayData : QArrayData { - QTypedArrayData<T> *ptr; - T *data; - uint size; }; +} template <class T> struct QTypedArrayData : QArrayData { - typedef T* iterator; - typedef const T* const_iterator; + struct AlignmentDummy { QtPrivate::AlignedQArrayData header; T data; }; - class AlignmentDummy { QArrayData header; T data; }; - - Q_REQUIRED_RESULT static QPair<QTypedArrayData *, T *> allocate(size_t capacity, - ArrayOptions options = DefaultAllocationFlags) + [[nodiscard]] static std::pair<QTypedArrayData *, T *> allocate(qsizetype capacity, AllocationOption option = QArrayData::KeepSize) { static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); QArrayData *d; - void *result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, options); -#if (defined(Q_CC_GNU) && Q_CC_GNU >= 407) || QT_HAS_BUILTIN(__builtin_assume_aligned) + void *result; + if constexpr (sizeof(T) == 1) { + // necessarily, alignof(T) == 1 + result = allocate1(&d, capacity, option); + } else if constexpr (sizeof(T) == 2) { + // alignof(T) may be 1, but that makes no difference + result = allocate2(&d, capacity, option); + } else { + result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, option); + } +#if __has_builtin(__builtin_assume_aligned) + // and yet we do offer results that have stricter alignment result = __builtin_assume_aligned(result, Q_ALIGNOF(AlignmentDummy)); #endif - return qMakePair(static_cast<QTypedArrayData *>(d), static_cast<T *>(result)); + return {static_cast<QTypedArrayData *>(d), static_cast<T *>(result)}; } - static QPair<QTypedArrayData *, T *> - reallocateUnaligned(QTypedArrayData *data, T *dataPointer, size_t capacity, - ArrayOptions options = DefaultAllocationFlags) + static std::pair<QTypedArrayData *, T *> + reallocateUnaligned(QTypedArrayData *data, T *dataPointer, qsizetype capacity, AllocationOption option) { static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); - QPair<QArrayData *, void *> pair = - QArrayData::reallocateUnaligned(data, dataPointer, sizeof(T), capacity, options); - return qMakePair(static_cast<QTypedArrayData *>(pair.first), static_cast<T *>(pair.second)); + std::pair<QArrayData *, void *> pair = + QArrayData::reallocateUnaligned(data, dataPointer, sizeof(T), capacity, option); + return {static_cast<QTypedArrayData *>(pair.first), static_cast<T *>(pair.second)}; } - static void deallocate(QArrayData *data) + static void deallocate(QArrayData *data) noexcept { static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy)); } - static QArrayDataPointerRef<T> fromRawData(const T *data, size_t n, - ArrayOptions options = DefaultRawFlags) + static T *dataStart(QArrayData *data, qsizetype alignment) noexcept { - static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); - QArrayDataPointerRef<T> result = { - static_cast<QTypedArrayData *>(prepareRawData(options)), const_cast<T *>(data), uint(n) - }; - if (result.ptr) { - Q_ASSERT(!result.ptr->isShared()); // No shared empty, please! - } - return result; + // Alignment is a power of two + Q_ASSERT(alignment >= qsizetype(alignof(QArrayData)) && !(alignment & (alignment - 1))); + void *start = reinterpret_cast<void *>( + (quintptr(data) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1)); + return static_cast<T *>(start); } - static QTypedArrayData *sharedNull() noexcept + constexpr static qsizetype max_size() noexcept { - static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); - return static_cast<QTypedArrayData *>(QArrayData::sharedNull()); - } - - static QTypedArrayData *sharedEmpty() - { - static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); - return allocate(/* capacity */ 0); - } - - static T *sharedNullData() - { - static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData)); - return static_cast<T *>(QArrayData::sharedNullData()); + // -1 to deal with the pointer one-past-the-end + return (QtPrivate::MaxAllocSize - sizeof(QtPrivate::AlignedQArrayData) - 1) / sizeof(T); } }; -//////////////////////////////////////////////////////////////////////////////// -// Q_ARRAY_LITERAL - -// The idea here is to place a (read-only) copy of header and array data in an -// mmappable portion of the executable (typically, .rodata section). This is -// accomplished by hiding a static const instance of QStaticArrayData, which is -// POD. - -// Hide array inside a lambda -#define Q_ARRAY_LITERAL(Type, ...) \ - ([]() -> QArrayDataPointerRef<Type> { \ - /* MSVC 2010 Doesn't support static variables in a lambda, but */ \ - /* happily accepts them in a static function of a lambda-local */ \ - /* struct :-) */ \ - struct StaticWrapper { \ - static QArrayDataPointerRef<Type> get() \ - { \ - Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ - return ref; \ - } \ - }; \ - return StaticWrapper::get(); \ - }()) \ - /**/ - -#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ - /* Portable compile-time array size computation */ \ - static constexpr Type data[] = { __VA_ARGS__ }; \ - enum { Size = sizeof(data) / sizeof(data[0]) }; \ - \ - static constexpr QArrayData literal = { Q_BASIC_ATOMIC_INITIALIZER(-1), QArrayData::StaticDataFlags, 0 };\ - \ - QArrayDataPointerRef<Type> ref = \ - { static_cast<QTypedArrayData<Type> *>( \ - const_cast<QArrayData *>(&literal)), \ - const_cast<Type *>(data), \ - Size }; \ - /**/ - namespace QtPrivate { struct Q_CORE_EXPORT QContainerImplHelper { @@ -340,6 +216,8 @@ struct Q_CORE_EXPORT QContainerImplHelper }; } +#undef Q_DECL_MALLOCLIKE + QT_END_NAMESPACE #endif // include guard |