diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index d70977b841..fa328a6d26 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -63,7 +63,7 @@ #include #include -#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData)) +#define IS_RAW_DATA(d) (!(d)->isMutable()) QT_BEGIN_NAMESPACE @@ -4469,16 +4469,13 @@ QByteArray QByteArray::fromRawData(const char *data, int size) */ QByteArray &QByteArray::setRawData(const char *data, uint size) { - if (d->ref.isShared() || d->allocatedCapacity()) { + if (!data || !size) { + clear(); + } else if (d->ref.isShared() || (d->flags & Data::RawDataType) == 0) { *this = fromRawData(data, size); } else { - if (data) { - d->size = size; - d->offset = data - reinterpret_cast(d); - } else { - d->offset = sizeof(QByteArrayData); - d->size = 0; - } + d->size = size; + d->offset = data - reinterpret_cast(d); } return *this; } diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index c161e92743..611cacc646 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -495,7 +495,7 @@ inline const char *QByteArray::data() const inline const char *QByteArray::constData() const { return d->data(); } inline void QByteArray::detach() -{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) reallocData(uint(d->size) + 1u, d->detachFlags()); } +{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u, d->detachFlags()); } inline bool QByteArray::isDetached() const { return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d) diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index e00f2998b0..d0f141e079 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -100,7 +100,7 @@ #define ULLONG_MAX quint64_C(18446744073709551615) #endif -#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData)) +#define IS_RAW_DATA(d) (!(d)->isMutable()) QT_BEGIN_NAMESPACE @@ -2272,7 +2272,7 @@ void QString::resize(int size) if (d->ref.isShared() || uint(size) + 1u > d->allocatedCapacity()) reallocData(uint(size) + 1u, true); - if (d->alloc) { + if (d->flags & Data::AllocatedDataType) { d->size = size; d->data()[size] = '\0'; } @@ -9149,16 +9149,13 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref.isShared() || d->allocatedCapacity()) { + if (!unicode || !size) { + clear(); + } else if (d->ref.isShared() || !IS_RAW_DATA(d)) { *this = fromRawData(unicode, size); } else { - if (unicode) { - d->size = size; - d->offset = reinterpret_cast(unicode) - reinterpret_cast(d); - } else { - d->offset = sizeof(QStringData); - d->size = 0; - } + d->size = size; + d->offset = reinterpret_cast(unicode) - reinterpret_cast(d); } return *this; } diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 4d853bfc72..03d83eeab9 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -1041,7 +1041,7 @@ inline QChar *QString::data() inline const QChar *QString::constData() const { return reinterpret_cast(d->data()); } inline void QString::detach() -{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) reallocData(uint(d->size) + 1u); } +{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u); } inline bool QString::isDetached() const { return !d->ref.isShared(); } inline void QString::clear() diff --git a/src/corelib/text/qstringliteral.h b/src/corelib/text/qstringliteral.h index b407bf6bc0..796909dcab 100644 --- a/src/corelib/text/qstringliteral.h +++ b/src/corelib/text/qstringliteral.h @@ -75,7 +75,7 @@ Q_STATIC_ASSERT_X(sizeof(qunicodechar) == 2, /**/ #define Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \ - { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::DefaultAllocationFlags, size, 0, offset } \ + { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, size, 0, offset } \ /**/ #define Q_STATIC_STRING_DATA_HEADER_INITIALIZER(size) \ diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index ce6f138497..bc48619349 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -153,17 +153,17 @@ QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers") const QArrayData QArrayData::shared_null[2] = { - { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, sizeof(QArrayData) }, // shared null + { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, sizeof(QArrayData) }, // shared null /* zero initialized terminator */}; -static const QArrayData qt_array[3] = { - { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, sizeof(QArrayData) }, // shared empty - { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, sizeof(QArrayData) }, // unsharable empty +static const QArrayData emptyNotNullShared[2] = { + { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, sizeof(QArrayData) }, // shared empty /* zero initialized terminator */}; QT_WARNING_POP -static const QArrayData &qt_array_empty = qt_array[0]; +static const QArrayData &qt_array_empty = emptyNotNullShared[0]; + static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, size_t headerSize, uint options) { @@ -179,6 +179,17 @@ static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, siz } } +static QArrayData *allocateData(size_t allocSize, uint options) +{ + QArrayData *header = static_cast(::malloc(allocSize)); + if (header) { + header->ref.atomic.storeRelaxed(1); + header->flags = options; + header->size = 0; + } + return header; +} + static QArrayData *reallocateData(QArrayData *header, size_t allocSize, uint options) { header = static_cast(::realloc(header, allocSize)); @@ -194,38 +205,43 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, Q_ASSERT(alignment >= alignof(QArrayData) && !(alignment & (alignment - 1))); - // Don't allocate empty headers - if (!(options & RawData) && !capacity) + if (capacity == 0) + // optimization for empty headers return const_cast(&qt_array_empty); size_t headerSize = sizeof(QArrayData); - // Allocate extra (alignment - alignof(QArrayData)) padding bytes so we - // can properly align the data array. This assumes malloc is able to - // provide appropriate alignment for the header -- as it should! - // Padding is skipped when allocating a header for RawData. - if (!(options & RawData)) - headerSize += (alignment - alignof(QArrayData)); + if (alignment > alignof(QArrayData)) { + // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we + // can properly align the data array. This assumes malloc is able to + // provide appropriate alignment for the header -- as it should! + headerSize += alignment - alignof(QArrayData); + } if (headerSize > size_t(MaxAllocSize)) return nullptr; size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); - QArrayData *header = static_cast(::malloc(allocSize)); + options |= ArrayOption(AllocatedDataType); + QArrayData *header = allocateData(allocSize, options); if (header) { quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1); - - header->ref.atomic.storeRelaxed(1); - header->size = 0; - header->alloc = capacity; - header->flags = options; header->offset = data - quintptr(header); + header->alloc = capacity; } return header; } +QArrayData *QArrayData::prepareRawData(ArrayOptions options) Q_DECL_NOTHROW +{ + QArrayData *header = allocateData(sizeof(QArrayData), (options & ~DataTypeBits) | RawDataType); + if (header) + header->alloc = 0; + return header; +} + QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, size_t capacity, ArrayOptions options) noexcept { @@ -233,9 +249,10 @@ QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, Q_ASSERT(data->isMutable()); Q_ASSERT(!data->ref.isShared()); + options |= ArrayOption(AllocatedDataType); size_t headerSize = sizeof(QArrayData); size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); - QArrayData *header = static_cast(reallocateData(data, allocSize, options)); + QArrayData *header = reallocateData(data, allocSize, options); if (header) header->alloc = capacity; return header; diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index feee557131..babccc6017 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -48,6 +48,24 @@ QT_BEGIN_NAMESPACE struct Q_CORE_EXPORT 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() + + /// this option is used by the Q_ARRAY_LITERAL and similar macros + StaticDataFlags = RawDataType, + /// this option is used by the allocate() function + DefaultAllocationFlags = 0, + /// this option is used by the prepareRawData() function + DefaultRawFlags = 0 + }; + Q_DECLARE_FLAGS(ArrayOptions, ArrayOption) + QtPrivate::RefCount ref; uint flags; int size; @@ -84,20 +102,9 @@ struct Q_CORE_EXPORT QArrayData // follow COW principles. bool isMutable() const { - return alloc != 0; + return flags & AllocatedDataType; } - enum ArrayOption { - CapacityReserved = 0x1, - RawData = 0x4, - GrowsForward = 0x8, - - DefaultAllocationFlags = 0, - DefaultRawFlags = 0 - }; - - Q_DECLARE_FLAGS(ArrayOptions, ArrayOption) - size_t detachCapacity(size_t newSize) const { if (flags & CapacityReserved && newSize < constAllocatedCapacity()) @@ -107,7 +114,7 @@ struct Q_CORE_EXPORT QArrayData ArrayOptions detachFlags() const { - ArrayOptions result; + ArrayOptions result = DefaultAllocationFlags; if (flags & CapacityReserved) result |= CapacityReserved; return result; @@ -115,7 +122,7 @@ struct Q_CORE_EXPORT QArrayData ArrayOptions cloneFlags() const { - ArrayOptions result; + ArrayOptions result = DefaultAllocationFlags; if (flags & CapacityReserved) result |= CapacityReserved; return result; @@ -129,6 +136,8 @@ struct Q_CORE_EXPORT QArrayData 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 QArrayData *prepareRawData(ArrayOptions options = ArrayOptions(RawDataType)) + Q_DECL_NOTHROW; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment) noexcept; @@ -269,7 +278,7 @@ struct QTypedArrayData ArrayOptions options = DefaultRawFlags) { Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData)); - QTypedArrayData *result = allocate(0, options | RawData); + QTypedArrayData *result = static_cast(prepareRawData(options)); if (result) { Q_ASSERT(!result->ref.isShared()); // No shared empty, please! @@ -302,6 +311,7 @@ struct QTypedArrayData template struct QStaticArrayData { + // static arrays are of type RawDataType QArrayData header; T data[N]; }; @@ -314,7 +324,7 @@ struct QArrayDataPointerRef }; #define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \ - { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::DefaultAllocationFlags, size, 0, offset } \ + { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, size, 0, offset } \ /**/ #define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) \ diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 23ea417481..d86f9f25d3 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -84,7 +84,7 @@ void tst_QArrayData::referenceCounting() { { // Reference counting initialized to 1 (owned) - QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, QArrayData::DefaultRawFlags, 0, 0, 0 }; QCOMPARE(array.ref.atomic.loadRelaxed(), 1); @@ -107,10 +107,9 @@ void tst_QArrayData::referenceCounting() // Now would be a good time to free/release allocated data } - { // Reference counting initialized to -1 (static read-only data) - QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, 0 }; QCOMPARE(array.ref.atomic.loadRelaxed(), -1); @@ -186,7 +185,7 @@ void tst_QArrayData::staticData() void tst_QArrayData::simpleVector() { - QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, 0 }; QStaticArrayData data1 = { Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), { 0, 1, 2, 3, 4, 5, 6 }