summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/tools/qarraydata.cpp12
-rw-r--r--src/corelib/tools/qarraydata.h15
-rw-r--r--src/corelib/tools/qarraydataops.h14
-rw-r--r--src/corelib/tools/qarraydatapointer.h20
-rw-r--r--src/corelib/tools/qrefcount.h1
-rw-r--r--tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp152
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"