diff options
-rw-r--r-- | src/corelib/tools/qarraydata.cpp | 12 | ||||
-rw-r--r-- | src/corelib/tools/qarraydata.h | 15 | ||||
-rw-r--r-- | src/corelib/tools/qarraydataops.h | 14 | ||||
-rw-r--r-- | src/corelib/tools/qarraydatapointer.h | 20 | ||||
-rw-r--r-- | src/corelib/tools/qrefcount.h | 1 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp | 152 |
6 files changed, 193 insertions, 21 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 1145ecb4f4..c6e96c78a9 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -45,9 +45,10 @@ QT_BEGIN_NAMESPACE const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; const QArrayData QArrayData::shared_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; +const QArrayData QArrayData::unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, - size_t capacity, bool reserve) + size_t capacity, bool reserve, bool sharable) { // Alignment is a power of two Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) @@ -55,7 +56,9 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, // Don't allocate empty headers if (!capacity) - return const_cast<QArrayData *>(&shared_empty); + return sharable + ? const_cast<QArrayData *>(&shared_empty) + : const_cast<QArrayData *>(&unsharable_empty); // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we // can properly align the data array. This assumes malloc is able to @@ -69,7 +72,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1); - header->ref.initializeOwned(); + header->ref.atomic.store(sharable ? 1 : 0); header->size = 0; header->alloc = capacity; header->capacityReserved = reserve; @@ -87,6 +90,9 @@ void QArrayData::deallocate(QArrayData *data, size_t objectSize, && !(alignment & (alignment - 1))); Q_UNUSED(objectSize) Q_UNUSED(alignment) + if (data == &unsharable_empty) + return; + qFree(data); } diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index b7ab59257f..1fd60e2155 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -74,12 +74,13 @@ struct Q_CORE_EXPORT QArrayData } static QArrayData *allocate(size_t objectSize, size_t alignment, - size_t capacity, bool reserve) Q_REQUIRED_RESULT; + size_t capacity, bool reserve, bool sharable) Q_REQUIRED_RESULT; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment); static const QArrayData shared_null; static const QArrayData shared_empty; + static const QArrayData unsharable_empty; }; template <class T> @@ -99,11 +100,11 @@ struct QTypedArrayData class AlignmentDummy { QArrayData header; T data; }; - static QTypedArrayData *allocate(size_t capacity, bool reserve = false) - Q_REQUIRED_RESULT + static QTypedArrayData *allocate(size_t capacity, bool reserve = false, + bool sharable = true) Q_REQUIRED_RESULT { return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T), - Q_ALIGNOF(AlignmentDummy), capacity, reserve)); + Q_ALIGNOF(AlignmentDummy), capacity, reserve, sharable)); } static void deallocate(QArrayData *data) @@ -122,6 +123,12 @@ struct QTypedArrayData return static_cast<QTypedArrayData *>( const_cast<QArrayData *>(&QArrayData::shared_empty)); } + + static QTypedArrayData *unsharableEmpty() + { + return static_cast<QTypedArrayData *>( + const_cast<QArrayData *>(&QArrayData::unsharable_empty)); + } }; template <class T, size_t N> diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index e7c66a456b..a3c372fe6b 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -61,7 +61,7 @@ struct QPodArrayOps { void copyAppend(const T *b, const T *e) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(b < e); Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); @@ -71,7 +71,7 @@ struct QPodArrayOps void copyAppend(size_t n, const T &t) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(n <= this->alloc - uint(this->size)); T *iter = this->end(); @@ -91,7 +91,7 @@ struct QPodArrayOps void insert(T *where, const T *b, const T *e) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end Q_ASSERT(b < e); Q_ASSERT(e <= where || b > this->end()); // No overlap @@ -109,7 +109,7 @@ struct QGenericArrayOps { void copyAppend(const T *b, const T *e) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(b < e); Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); @@ -122,7 +122,7 @@ struct QGenericArrayOps void copyAppend(size_t n, const T &t) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(n <= this->alloc - uint(this->size)); T *iter = this->end(); @@ -149,7 +149,7 @@ struct QGenericArrayOps void insert(T *where, const T *b, const T *e) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end Q_ASSERT(b < e); Q_ASSERT(e <= where || b > this->end()); // No overlap @@ -222,7 +222,7 @@ struct QMovableArrayOps void insert(T *where, const T *b, const T *e) { - Q_ASSERT(this->ref == 1); + Q_ASSERT(!this->ref.isShared()); Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end Q_ASSERT(b < e); Q_ASSERT(e <= where || b > this->end()); // No overlap diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index f1cd1dc4b1..e42d146c58 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -64,7 +64,9 @@ public: } QArrayDataPointer(const QArrayDataPointer &other) - : d((other.d->ref.ref(), other.d)) + : d(other.d->ref.ref() + ? other.d + : other.clone()) { } @@ -110,6 +112,22 @@ public: return d; } + void setSharable(bool sharable) + { + if (d->alloc == 0 && d->size == 0) { + Q_ASSERT(Data::sharedNull() == d + || Data::sharedEmpty() == d + || Data::unsharableEmpty() == d); + d = sharable + ? Data::sharedEmpty() + : Data::unsharableEmpty(); + return; + } + + detach(); + d->ref.setSharable(sharable); + } + void swap(QArrayDataPointer &other) { qSwap(d, other.d); diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 9478ff1269..bf864f2f58 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -111,6 +111,7 @@ public: { return atomic.load(); } void initializeOwned() { atomic.store(1); } + void initializeUnsharable() { atomic.store(0); } QBasicAtomicInt atomic; }; diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index e9a3218331..e8edab20e4 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -79,6 +79,8 @@ private slots: void typedData(); void gccBug43247(); void arrayOps(); + void setSharable_data(); + void setSharable(); }; template <class T> const T &const_(const T &t) { return t; } @@ -477,6 +479,7 @@ void tst_QArrayData::allocate_data() QTest::addColumn<size_t>("objectSize"); QTest::addColumn<size_t>("alignment"); QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<bool>("isSharable"); QTest::addColumn<const QArrayData *>("commonEmpty"); struct { @@ -492,10 +495,13 @@ void tst_QArrayData::allocate_data() struct { char const *description; bool isCapacityReserved; + bool isSharable; const QArrayData *commonEmpty; } options[] = { - { "Default", false, &QArrayData::shared_empty }, - { "Reserved", true, &QArrayData::shared_empty }, + { "Default", false, true, &QArrayData::shared_empty }, + { "Reserved", true, true, &QArrayData::shared_empty }, + { "Reserved | Unsharable", true, false, &QArrayData::unsharable_empty }, + { "Unsharable", false, false, &QArrayData::unsharable_empty }, }; for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) @@ -505,7 +511,8 @@ void tst_QArrayData::allocate_data() + QLatin1String(": ") + QLatin1String(options[j].description))) << types[i].objectSize << types[i].alignment - << options[j].isCapacityReserved << options[j].commonEmpty; + << options[j].isCapacityReserved << options[j].isSharable + << options[j].commonEmpty; } void tst_QArrayData::allocate() @@ -513,6 +520,7 @@ void tst_QArrayData::allocate() QFETCH(size_t, objectSize); QFETCH(size_t, alignment); QFETCH(bool, isCapacityReserved); + QFETCH(bool, isSharable); QFETCH(const QArrayData *, commonEmpty); // Minimum alignment that can be requested is that of QArrayData. @@ -521,19 +529,20 @@ void tst_QArrayData::allocate() // Shared Empty QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, - isCapacityReserved), commonEmpty); + isCapacityReserved, isSharable), commonEmpty); Deallocator keeper(objectSize, minAlignment); keeper.headers.reserve(1024); for (int capacity = 1; capacity <= 1024; capacity <<= 1) { QArrayData *data = QArrayData::allocate(objectSize, minAlignment, - capacity, isCapacityReserved); + capacity, isCapacityReserved, isSharable); keeper.headers.append(data); QCOMPARE(data->size, 0); QVERIFY(data->alloc >= uint(capacity)); QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); + QCOMPARE(data->ref.isSharable(), isSharable); // Check that the allocated array can be used. Best tested with a // memory checker, such as valgrind, running. @@ -569,7 +578,7 @@ void tst_QArrayData::alignment() for (int i = 0; i < 100; ++i) { QArrayData *data = QArrayData::allocate(sizeof(Unaligned), - minAlignment, 8, false); + minAlignment, 8, false, true); keeper.headers.append(data); QVERIFY(data); @@ -903,5 +912,136 @@ void tst_QArrayData::arrayOps() } } +Q_DECLARE_METATYPE(QArrayDataPointer<int>) + +static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array, + int fillValue, size_t size) +{ + const int *iter = array->begin(); + const int *const end = array->end(); + + for (size_t i = 0; i < size; ++i, ++iter) + if (*iter != fillValue) + return false; + + if (iter != end) + return false; + + return true; +} + +void tst_QArrayData::setSharable_data() +{ + QTest::addColumn<QArrayDataPointer<int> >("array"); + QTest::addColumn<size_t>("size"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<int>("fillValue"); + + QArrayDataPointer<int> null; + QArrayDataPointer<int> empty; empty.clear(); + + static QStaticArrayData<int, 10> staticArrayData = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + }; + + QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, true, true)); + QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10, false, true)); + QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, true, true)); + QArrayDataPointer<int> staticArray(static_cast<QTypedArrayData<int> *>(&staticArrayData.header)); + + nonEmpty->copyAppend(5, 1); + nonEmptyReserved->copyAppend(7, 2); + + QTest::newRow("shared-null") << null << size_t(0) << size_t(0) << false << 0; + QTest::newRow("shared-empty") << empty << size_t(0) << size_t(0) << false << 0; + // unsharable-empty implicitly tested in shared-empty + QTest::newRow("empty-reserved") << emptyReserved << size_t(0) << size_t(5) << true << 0; + QTest::newRow("non-empty") << nonEmpty << size_t(5) << size_t(10) << false << 1; + QTest::newRow("non-empty-reserved") << nonEmptyReserved << size_t(7) << size_t(15) << true << 2; + QTest::newRow("static-array") << staticArray << size_t(10) << size_t(0) << false << 3; +} + +void tst_QArrayData::setSharable() +{ + QFETCH(QArrayDataPointer<int>, array); + QFETCH(size_t, size); + QFETCH(size_t, capacity); + QFETCH(bool, isCapacityReserved); + QFETCH(int, fillValue); + + QVERIFY(array->ref.isShared()); // QTest has a copy + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + // shared-null becomes shared-empty, may otherwise detach + array.setSharable(true); + + QVERIFY(array->ref.isSharable()); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QVERIFY(array->ref.isSharable()); + QCOMPARE(copy.data(), array.data()); + } + + // Unshare, must detach + array.setSharable(false); + + // Immutability (alloc == 0) is lost on detach + if (capacity == 0 && size != 0) + capacity = size; + + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + // Null/empty is always shared + QCOMPARE(copy->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(copy->ref.isSharable()); + + QCOMPARE(size_t(copy->size), size); + QCOMPARE(size_t(copy->alloc), capacity); + QCOMPARE(bool(copy->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(copy, fillValue, size)); + } + + // Make sharable, again + array.setSharable(true); + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QCOMPARE(copy.data(), array.data()); + } + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); +} + QTEST_APPLESS_MAIN(tst_QArrayData) #include "tst_qarraydata.moc" |