blob: 25e2f21d0316988583622d8237ca360bbe13cbb8 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/QString>
#include <QtCore/qarraydata.h>
#include "simplevector.h"
struct SharedNullVerifier
{
SharedNullVerifier()
{
Q_ASSERT(QArrayData::shared_null[0].ref.isStatic());
Q_ASSERT(QArrayData::shared_null[0].ref.isShared());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
Q_ASSERT(QArrayData::shared_null[0].ref.isSharable());
#endif
}
};
// This is meant to verify/ensure that shared_null is not being dynamically
// initialized and stays away from the order-of-static-initialization fiasco.
//
// Of course, if this was to fail, qmake and the build should have crashed and
// burned before we ever got to this point :-)
SharedNullVerifier globalInit;
class tst_QArrayData : public QObject
{
Q_OBJECT
private slots:
void referenceCounting();
void sharedNullEmpty();
void staticData();
void simpleVector();
void simpleVectorReserve_data();
void simpleVectorReserve();
void allocate_data();
void allocate();
void reallocate_data() { allocate_data(); }
void reallocate();
void alignment_data();
void alignment();
void typedData();
void gccBug43247();
void arrayOps();
void arrayOps2();
void setSharable_data();
void setSharable();
void fromRawData_data();
void fromRawData();
void literals();
void variadicLiterals();
void rValueReferences();
void grow();
};
template <class T> const T &const_(const T &t) { return t; }
void tst_QArrayData::referenceCounting()
{
{
// Reference counting initialized to 1 (owned)
QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 };
QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
QVERIFY(!array.ref.isStatic());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(array.ref.isSharable());
#endif
QVERIFY(array.ref.ref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 2);
QVERIFY(array.ref.deref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
QVERIFY(array.ref.ref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 2);
QVERIFY(array.ref.deref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
QVERIFY(!array.ref.deref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
// Now would be a good time to free/release allocated data
}
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
{
// Reference counting initialized to 0 (non-sharable)
QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 };
QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
QVERIFY(!array.ref.isStatic());
QVERIFY(!array.ref.isSharable());
QVERIFY(!array.ref.ref());
// Reference counting fails, data should be copied
QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
QVERIFY(!array.ref.deref());
QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
// Free/release data
}
#endif
{
// Reference counting initialized to -1 (static read-only data)
QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 };
QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
QVERIFY(array.ref.isStatic());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(array.ref.isSharable());
#endif
QVERIFY(array.ref.ref());
QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
QVERIFY(array.ref.deref());
QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
}
}
void tst_QArrayData::sharedNullEmpty()
{
QArrayData *null = const_cast<QArrayData *>(QArrayData::shared_null);
QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0);
QVERIFY(null->ref.isStatic());
QVERIFY(null->ref.isShared());
QVERIFY(empty->ref.isStatic());
QVERIFY(empty->ref.isShared());
QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(null->ref.isSharable());
QVERIFY(empty->ref.isSharable());
#endif
QVERIFY(null->ref.ref());
QVERIFY(empty->ref.ref());
QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
QVERIFY(null->ref.deref());
QVERIFY(empty->ref.deref());
QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
QVERIFY(null != empty);
QCOMPARE(null->size, 0);
QCOMPARE(null->alloc, 0u);
QCOMPARE(null->capacityReserved, 0u);
QCOMPARE(empty->size, 0);
QCOMPARE(empty->alloc, 0u);
QCOMPARE(empty->capacityReserved, 0u);
}
void tst_QArrayData::staticData()
{
QStaticArrayData<char, 10> charArray = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10),
{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
};
QStaticArrayData<int, 10> intArray = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10),
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
};
QStaticArrayData<double, 10> doubleArray = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(double, 10),
{ 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f }
};
QCOMPARE(charArray.header.size, 10);
QCOMPARE(intArray.header.size, 10);
QCOMPARE(doubleArray.header.size, 10);
QCOMPARE(charArray.header.data(), reinterpret_cast<void *>(&charArray.data));
QCOMPARE(intArray.header.data(), reinterpret_cast<void *>(&intArray.data));
QCOMPARE(doubleArray.header.data(), reinterpret_cast<void *>(&doubleArray.data));
}
void tst_QArrayData::simpleVector()
{
QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 };
QStaticArrayData<int, 7> data1 = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7),
{ 0, 1, 2, 3, 4, 5, 6 }
};
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
SimpleVector<int> v1;
SimpleVector<int> v2(v1);
SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0));
SimpleVector<int> v4(static_cast<QTypedArrayData<int> *>(&data1.header));
SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0));
SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header));
SimpleVector<int> v7(10, 5);
SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array));
v3 = v1;
v1.swap(v3);
v4.clear();
QVERIFY(v1.isNull());
QVERIFY(v2.isNull());
QVERIFY(v3.isNull());
QVERIFY(v4.isNull());
QVERIFY(!v5.isNull());
QVERIFY(!v6.isNull());
QVERIFY(!v7.isNull());
QVERIFY(!v8.isNull());
QVERIFY(v1.isEmpty());
QVERIFY(v2.isEmpty());
QVERIFY(v3.isEmpty());
QVERIFY(v4.isEmpty());
QVERIFY(v5.isEmpty());
QVERIFY(!v6.isEmpty());
QVERIFY(!v7.isEmpty());
QVERIFY(!v8.isEmpty());
QCOMPARE(v1.size(), size_t(0));
QCOMPARE(v2.size(), size_t(0));
QCOMPARE(v3.size(), size_t(0));
QCOMPARE(v4.size(), size_t(0));
QCOMPARE(v5.size(), size_t(0));
QCOMPARE(v6.size(), size_t(7));
QCOMPARE(v7.size(), size_t(10));
QCOMPARE(v8.size(), size_t(10));
QCOMPARE(v1.capacity(), size_t(0));
QCOMPARE(v2.capacity(), size_t(0));
QCOMPARE(v3.capacity(), size_t(0));
QCOMPARE(v4.capacity(), size_t(0));
QCOMPARE(v5.capacity(), size_t(0));
// v6.capacity() is unspecified, for now
QVERIFY(v7.capacity() >= size_t(10));
QVERIFY(v8.capacity() >= size_t(10));
QVERIFY(v1.isStatic());
QVERIFY(v2.isStatic());
QVERIFY(v3.isStatic());
QVERIFY(v4.isStatic());
QVERIFY(v5.isStatic());
QVERIFY(v6.isStatic());
QVERIFY(!v7.isStatic());
QVERIFY(!v8.isStatic());
QVERIFY(v1.isShared());
QVERIFY(v2.isShared());
QVERIFY(v3.isShared());
QVERIFY(v4.isShared());
QVERIFY(v5.isShared());
QVERIFY(v6.isShared());
QVERIFY(!v7.isShared());
QVERIFY((SimpleVector<int>(v7), v7.isShared()));
QVERIFY(!v7.isShared());
QVERIFY(!v8.isShared());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(v1.isSharable());
QVERIFY(v2.isSharable());
QVERIFY(v3.isSharable());
QVERIFY(v4.isSharable());
QVERIFY(v5.isSharable());
QVERIFY(v6.isSharable());
QVERIFY(v7.isSharable());
QVERIFY(v8.isSharable());
#endif
QVERIFY(v1.isSharedWith(v2));
QVERIFY(v1.isSharedWith(v3));
QVERIFY(v1.isSharedWith(v4));
QVERIFY(!v1.isSharedWith(v5));
QVERIFY(!v1.isSharedWith(v6));
QCOMPARE(v1.constBegin(), v1.constEnd());
QCOMPARE(v4.constBegin(), v4.constEnd());
QCOMPARE((v6.constBegin() + v6.size()), v6.constEnd());
QCOMPARE((v7.constBegin() + v7.size()), v7.constEnd());
QCOMPARE((v8.constBegin() + v8.size()), v8.constEnd());
QVERIFY(v1 == v2);
QVERIFY(v1 == v3);
QVERIFY(v1 == v4);
QVERIFY(v1 == v5);
QVERIFY(!(v1 == v6));
QVERIFY(v1 != v6);
QVERIFY(v4 != v6);
QVERIFY(v5 != v6);
QVERIFY(!(v1 != v5));
QVERIFY(v1 < v6);
QVERIFY(!(v6 < v1));
QVERIFY(v6 > v1);
QVERIFY(!(v1 > v6));
QVERIFY(v1 <= v6);
QVERIFY(!(v6 <= v1));
QVERIFY(v6 >= v1);
QVERIFY(!(v1 >= v6));
{
SimpleVector<int> temp(v6);
QCOMPARE(const_(v6).front(), 0);
QCOMPARE(const_(v6).back(), 6);
QVERIFY(temp.isShared());
QVERIFY(temp.isSharedWith(v6));
QCOMPARE(temp.front(), 0);
QCOMPARE(temp.back(), 6);
// Detached
QVERIFY(!temp.isShared());
const int *const tempBegin = temp.begin();
for (size_t i = 0; i < v6.size(); ++i) {
QCOMPARE(const_(v6)[i], int(i));
QCOMPARE(const_(v6).at(i), int(i));
QCOMPARE(&const_(v6)[i], &const_(v6).at(i));
QCOMPARE(const_(v8)[i], const_(v6)[i]);
QCOMPARE(temp[i], int(i));
QCOMPARE(temp.at(i), int(i));
QCOMPARE(&temp[i], &temp.at(i));
}
// A single detach should do
QCOMPARE((const int *)temp.begin(), tempBegin);
}
{
int count = 0;
Q_FOREACH (int value, v7) {
QCOMPARE(value, 5);
++count;
}
QCOMPARE(count, 10);
}
{
int count = 0;
Q_FOREACH (int value, v8) {
QCOMPARE(value, count);
++count;
}
QCOMPARE(count, 10);
}
v5 = v6;
QVERIFY(v5.isSharedWith(v6));
QVERIFY(!v1.isSharedWith(v5));
v1.swap(v6);
QVERIFY(v6.isNull());
QVERIFY(v1.isSharedWith(v5));
{
using std::swap;
swap(v1, v6);
QVERIFY(v5.isSharedWith(v6));
QVERIFY(!v1.isSharedWith(v5));
}
v1.prepend(array, array + sizeof(array)/sizeof(array[0]));
QCOMPARE(v1.size(), size_t(10));
QVERIFY(v1 == v8);
v6 = v1;
QVERIFY(v1.isSharedWith(v6));
v1.prepend(array, array + sizeof(array)/sizeof(array[0]));
QVERIFY(!v1.isSharedWith(v6));
QCOMPARE(v1.size(), size_t(20));
QCOMPARE(v6.size(), size_t(10));
for (int i = 0; i < 20; ++i)
QCOMPARE(v1[i], v6[i % 10]);
v1.clear();
v1.append(array, array + sizeof(array)/sizeof(array[0]));
// v1 is now [0 .. 9]
QCOMPARE(v1.size(), size_t(10));
QVERIFY(v1 == v8);
v6 = v1;
QVERIFY(v1.isSharedWith(v6));
v1.append(array, array + sizeof(array)/sizeof(array[0]));
// v1 is now [0 .. 9, 0 .. 9]
QVERIFY(!v1.isSharedWith(v6));
QCOMPARE(v1.size(), size_t(20));
QCOMPARE(v6.size(), size_t(10));
for (int i = 0; i < 20; ++i)
QCOMPARE(v1[i], v6[i % 10]);
v1.insert(0, v6.constBegin(), v6.constEnd());
// v1 is now [ 0 .. 9, 0 .. 9, 0 .. 9]
QCOMPARE(v1.size(), size_t(30));
for (int i = 0; i < 30; ++i)
QCOMPARE(v1[i], v8[i % 10]);
v6 = v1;
QVERIFY(v1.isSharedWith(v6));
v1.insert(10, v6.constBegin(), v6.constEnd());
// v1 is now [ 0..9, <new data>0..9, 0..9, 0..9</new data>, 0..9, 0..9 ]
QVERIFY(!v1.isSharedWith(v6));
QCOMPARE(v1.size(), size_t(60));
QCOMPARE(v6.size(), size_t(30));
for (int i = 0; i < 30; ++i)
QCOMPARE(v6[i], v8[i % 10]);
for (int i = 0; i < 60; ++i)
QCOMPARE(v1[i], v8[i % 10]);
v1.insert(int(v1.size()), v6.constBegin(), v6.constEnd());
// v1 is now [ 0..9 x 6, <new data>0..9 x 3</new data> ]
QCOMPARE(v1.size(), size_t(90));
for (int i = 0; i < 90; ++i)
QCOMPARE(v1[i], v8[i % 10]);
v1.insert(-1, v8.constBegin(), v8.constEnd());
// v1 is now [ 0..9 x 9, <new data>0..9</new data> ]
QCOMPARE(v1.size(), size_t(100));
for (int i = 0; i < 100; ++i)
QCOMPARE(v1[i], v8[i % 10]);
v1.insert(-11, v8.constBegin(), v8.constEnd());
// v1 is now [ 0..9 x 9, <new data>0..9</new data>, 0..9 ]
QCOMPARE(v1.size(), size_t(110));
for (int i = 0; i < 110; ++i)
QCOMPARE(v1[i], v8[i % 10]);
v1.insert(-200, v8.constBegin(), v8.constEnd());
// v1 is now [ <new data>0..9</new data>, 0..9 x 11 ]
QCOMPARE(v1.size(), size_t(120));
for (int i = 0; i < 120; ++i)
QCOMPARE(v1[i], v8[i % 10]);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
{
v7.setSharable(true);
QVERIFY(v7.isSharable());
SimpleVector<int> copy1(v7);
QVERIFY(copy1.isSharedWith(v7));
v7.setSharable(false);
QVERIFY(!v7.isSharable());
QVERIFY(!copy1.isSharedWith(v7));
QCOMPARE(v7.size(), copy1.size());
for (size_t i = 0; i < copy1.size(); ++i)
QCOMPARE(v7[i], copy1[i]);
SimpleVector<int> clone(v7);
QVERIFY(!clone.isSharedWith(v7));
QCOMPARE(clone.size(), copy1.size());
for (size_t i = 0; i < copy1.size(); ++i)
QCOMPARE(clone[i], copy1[i]);
v7.setSharable(true);
QVERIFY(v7.isSharable());
SimpleVector<int> copy2(v7);
QVERIFY(copy2.isSharedWith(v7));
}
{
SimpleVector<int> null;
SimpleVector<int> empty(0, 5);
QVERIFY(null.isSharable());
QVERIFY(empty.isSharable());
null.setSharable(true);
empty.setSharable(true);
QVERIFY(null.isSharable());
QVERIFY(empty.isSharable());
QVERIFY(null.isEmpty());
QVERIFY(empty.isEmpty());
null.setSharable(false);
empty.setSharable(false);
QVERIFY(!null.isSharable());
QVERIFY(!empty.isSharable());
QVERIFY(null.isEmpty());
QVERIFY(empty.isEmpty());
null.setSharable(true);
empty.setSharable(true);
QVERIFY(null.isSharable());
QVERIFY(empty.isSharable());
QVERIFY(null.isEmpty());
QVERIFY(empty.isEmpty());
}
#endif
}
Q_DECLARE_METATYPE(SimpleVector<int>)
void tst_QArrayData::simpleVectorReserve_data()
{
QTest::addColumn<SimpleVector<int> >("vector");
QTest::addColumn<size_t>("capacity");
QTest::addColumn<size_t>("size");
QTest::newRow("null") << SimpleVector<int>() << size_t(0) << size_t(0);
QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0);
QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5);
static const QStaticArrayData<int, 15> array = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 15),
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } };
QArrayDataPointerRef<int> p = {
static_cast<QTypedArrayData<int> *>(
const_cast<QArrayData *>(&array.header)) };
QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15);
QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15);
}
void tst_QArrayData::simpleVectorReserve()
{
QFETCH(SimpleVector<int>, vector);
QFETCH(size_t, capacity);
QFETCH(size_t, size);
QVERIFY(!capacity || capacity >= size);
QCOMPARE(vector.capacity(), capacity);
QCOMPARE(vector.size(), size);
const SimpleVector<int> copy(vector);
vector.reserve(0);
QCOMPARE(vector.capacity(), capacity);
QCOMPARE(vector.size(), size);
vector.reserve(10);
// zero-capacity (immutable) resets with detach
if (!capacity)
capacity = size;
QCOMPARE(vector.capacity(), qMax(size_t(10), capacity));
QCOMPARE(vector.size(), size);
vector.reserve(20);
QCOMPARE(vector.capacity(), size_t(20));
QCOMPARE(vector.size(), size);
vector.reserve(30);
QCOMPARE(vector.capacity(), size_t(30));
QCOMPARE(vector.size(), size);
QVERIFY(vector == copy);
}
struct Deallocator
{
Deallocator(size_t objectSize, size_t alignment)
: objectSize(objectSize)
, alignment(alignment)
{
}
~Deallocator()
{
Q_FOREACH (QArrayData *data, headers)
QArrayData::deallocate(data, objectSize, alignment);
}
size_t objectSize;
size_t alignment;
QVector<QArrayData *> headers;
};
Q_DECLARE_METATYPE(const QArrayData *)
Q_DECLARE_METATYPE(QArrayData::AllocationOptions)
void tst_QArrayData::allocate_data()
{
QTest::addColumn<size_t>("objectSize");
QTest::addColumn<size_t>("alignment");
QTest::addColumn<QArrayData::AllocationOptions>("allocateOptions");
QTest::addColumn<bool>("isCapacityReserved");
QTest::addColumn<bool>("isSharable"); // ### Qt6: remove
QTest::addColumn<const QArrayData *>("commonEmpty");
struct {
char const *typeName;
size_t objectSize;
size_t alignment;
} types[] = {
{ "char", sizeof(char), Q_ALIGNOF(char) },
{ "short", sizeof(short), Q_ALIGNOF(short) },
{ "void *", sizeof(void *), Q_ALIGNOF(void *) }
};
QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0);
QVERIFY(shared_empty);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable);
QVERIFY(unsharable_empty);
#endif
struct {
char const *description;
QArrayData::AllocationOptions allocateOptions;
bool isCapacityReserved;
bool isSharable;
const QArrayData *commonEmpty;
} options[] = {
{ "Default", QArrayData::Default, false, true, shared_empty },
{ "Reserved", QArrayData::CapacityReserved, true, true, shared_empty },
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
{ "Reserved | Unsharable",
QArrayData::CapacityReserved | QArrayData::Unsharable, true, false,
unsharable_empty },
{ "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty },
#endif
{ "Grow", QArrayData::Grow, false, true, shared_empty }
};
for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j)
QTest::newRow(qPrintable(
QLatin1String(types[i].typeName)
+ QLatin1String(": ")
+ QLatin1String(options[j].description)))
<< types[i].objectSize << types[i].alignment
<< options[j].allocateOptions << options[j].isCapacityReserved
<< options[j].isSharable << options[j].commonEmpty;
}
void tst_QArrayData::allocate()
{
QFETCH(size_t, objectSize);
QFETCH(size_t, alignment);
QFETCH(QArrayData::AllocationOptions, allocateOptions);
QFETCH(bool, isCapacityReserved);
QFETCH(const QArrayData *, commonEmpty);
// Minimum alignment that can be requested is that of QArrayData.
// Typically, this alignment is sizeof(void *) and ensured by malloc.
size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData));
// Shared Empty
QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
QArrayData::AllocationOptions(allocateOptions)), commonEmpty);
Deallocator keeper(objectSize, minAlignment);
keeper.headers.reserve(1024);
for (int capacity = 1; capacity <= 1024; capacity <<= 1) {
QArrayData *data = QArrayData::allocate(objectSize, minAlignment,
capacity, QArrayData::AllocationOptions(allocateOptions));
keeper.headers.append(data);
QCOMPARE(data->size, 0);
if (allocateOptions & QArrayData::Grow)
QVERIFY(data->alloc > uint(capacity));
else
QCOMPARE(data->alloc, uint(capacity));
QCOMPARE(data->capacityReserved, uint(isCapacityReserved));
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QFETCH(bool, isSharable);
QCOMPARE(data->ref.isSharable(), isSharable);
#endif
// Check that the allocated array can be used. Best tested with a
// memory checker, such as valgrind, running.
::memset(data->data(), 'A', objectSize * capacity);
}
}
void tst_QArrayData::reallocate()
{
QFETCH(size_t, objectSize);
QFETCH(size_t, alignment);
QFETCH(QArrayData::AllocationOptions, allocateOptions);
QFETCH(bool, isCapacityReserved);
// Maximum alignment that can be requested is that of QArrayData,
// otherwise, we can't use reallocate().
Q_ASSERT(alignment <= Q_ALIGNOF(QArrayData));
// Minimum alignment that can be requested is that of QArrayData.
// Typically, this alignment is sizeof(void *) and ensured by malloc.
size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData));
int capacity = 10;
Deallocator keeper(objectSize, minAlignment);
QArrayData *data = QArrayData::allocate(objectSize, minAlignment, capacity,
QArrayData::AllocationOptions(allocateOptions) & ~QArrayData::Grow);
keeper.headers.append(data);
memset(data->data(), 'A', objectSize * capacity);
data->size = capacity;
// now try to reallocate
int newCapacity = 40;
data = QArrayData::reallocateUnaligned(data, objectSize, newCapacity,
QArrayData::AllocationOptions(allocateOptions));
QVERIFY(data);
keeper.headers.clear();
keeper.headers.append(data);
QCOMPARE(data->size, capacity);
if (allocateOptions & QArrayData::Grow)
QVERIFY(data->alloc > uint(newCapacity));
else
QCOMPARE(data->alloc, uint(newCapacity));
QCOMPARE(data->capacityReserved, uint(isCapacityReserved));
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QFETCH(bool, isSharable);
QCOMPARE(data->ref.isSharable(), isSharable);
#endif
for (int i = 0; i < capacity; ++i)
QCOMPARE(static_cast<char *>(data->data())[i], 'A');
}
class Unaligned
{
char dummy[8];
};
void tst_QArrayData::alignment_data()
{
QTest::addColumn<size_t>("alignment");
for (size_t i = 1; i < 10; ++i) {
size_t alignment = size_t(1u) << i;
QTest::newRow(qPrintable(QString::number(alignment))) << alignment;
}
}
void tst_QArrayData::alignment()
{
QFETCH(size_t, alignment);
// Minimum alignment that can be requested is that of QArrayData.
// Typically, this alignment is sizeof(void *) and ensured by malloc.
size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData));
Deallocator keeper(sizeof(Unaligned), minAlignment);
keeper.headers.reserve(100);
for (int i = 0; i < 100; ++i) {
QArrayData *data = QArrayData::allocate(sizeof(Unaligned),
minAlignment, 8, QArrayData::Default);
keeper.headers.append(data);
QVERIFY(data);
QCOMPARE(data->size, 0);
QVERIFY(data->alloc >= uint(8));
// These conditions should hold as long as header and array are
// allocated together
QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData)));
QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData)
+ minAlignment - Q_ALIGNOF(QArrayData)));
// Data is aligned
QCOMPARE(quintptr(quintptr(data->data()) % alignment), quintptr(0u));
// Check that the allocated array can be used. Best tested with a
// memory checker, such as valgrind, running.
::memset(data->data(), 'A', sizeof(Unaligned) * 8);
}
}
void tst_QArrayData::typedData()
{
QStaticArrayData<int, 10> data = {
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10),
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
};
QCOMPARE(data.header.size, 10);
{
QTypedArrayData<int> *array =
static_cast<QTypedArrayData<int> *>(&data.header);
QCOMPARE(array->data(), data.data);
int j = 0;
for (QTypedArrayData<int>::iterator iter = array->begin();
iter != array->end(); ++iter, ++j)
QCOMPARE((const int *)iter, data.data + j);
QCOMPARE(j, 10);
}
{
const QTypedArrayData<int> *array =
static_cast<const QTypedArrayData<int> *>(&data.header);
QCOMPARE(array->data(), data.data);
int j = 0;
for (QTypedArrayData<int>::const_iterator iter = array->begin();
iter != array->end(); ++iter, ++j)
QCOMPARE((const int *)iter, data.data + j);
QCOMPARE(j, 10);
}
{
QTypedArrayData<int> *null = QTypedArrayData<int>::sharedNull();
QTypedArrayData<int> *empty = QTypedArrayData<int>::allocate(0);
QVERIFY(null != empty);
QCOMPARE(null->size, 0);
QCOMPARE(empty->size, 0);
QCOMPARE(null->begin(), null->end());
QCOMPARE(empty->begin(), empty->end());
}
{
Deallocator keeper(sizeof(char),
Q_ALIGNOF(QTypedArrayData<char>::AlignmentDummy));
QArrayData *array = QTypedArrayData<char>::allocate(10);
keeper.headers.append(array);
QVERIFY(array);
QCOMPARE(array->size, 0);
QCOMPARE(array->alloc, 10u);
// Check that the allocated array can be used. Best tested with a
// memory checker, such as valgrind, running.
::memset(array->data(), 0, 10 * sizeof(char));
keeper.headers.clear();
QTypedArrayData<short>::deallocate(array);
QVERIFY(true);
}
{
Deallocator keeper(sizeof(short),
Q_ALIGNOF(QTypedArrayData<short>::AlignmentDummy));
QArrayData *array = QTypedArrayData<short>::allocate(10);
keeper.headers.append(array);
QVERIFY(array);
QCOMPARE(array->size, 0);
QCOMPARE(array->alloc, 10u);
// Check that the allocated array can be used. Best tested with a
// memory checker, such as valgrind, running.
::memset(array->data(), 0, 10 * sizeof(short));
keeper.headers.clear();
QTypedArrayData<short>::deallocate(array);
QVERIFY(true);
}
{
Deallocator keeper(sizeof(double),
Q_ALIGNOF(QTypedArrayData<double>::AlignmentDummy));
QArrayData *array = QTypedArrayData<double>::allocate(10);
keeper.headers.append(array);
QVERIFY(array);
QCOMPARE(array->size, 0);
QCOMPARE(array->alloc, 10u);
// Check that the allocated array can be used. Best tested with a
// memory checker, such as valgrind, running.
::memset(array->data(), 0, 10 * sizeof(double));
keeper.headers.clear();
QTypedArrayData<double>::deallocate(array);
QVERIFY(true);
}
}
void tst_QArrayData::gccBug43247()
{
// This test tries to verify QArrayData is not affected by GCC optimizer
// bug #43247.
// Reported on GCC 4.4.3, Linux, affects QVector
QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (3)");
QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (4)");
QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (5)");
QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (6)");
QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (7)");
SimpleVector<int> array(10, 0);
// QVector<int> vector(10, 0);
for (int i = 0; i < 10; ++i) {
if (i >= 3 && i < 8)
qDebug("GCC Optimization bug #43247 not triggered (%i)", i);
// When access to data is implemented through an array of size 1, this
// line lets the compiler assume i == 0, and the conditional above is
// skipped.
QVERIFY(array.at(i) == 0);
// QVERIFY(vector.at(i) == 0);
}
}
struct CountedObject
{
CountedObject()
: id(liveCount++)
, flags(DefaultConstructed)
{
}
CountedObject(const CountedObject &other)
: id(other.id)
, flags(other.flags == DefaultConstructed
? ObjectFlags(CopyConstructed | DefaultConstructed)
: CopyConstructed)
{
++liveCount;
}
~CountedObject()
{
--liveCount;
}
CountedObject &operator=(const CountedObject &other)
{
flags = ObjectFlags(other.flags | CopyAssigned);
id = other.id;
return *this;
}
struct LeakChecker
{
LeakChecker()
: previousLiveCount(liveCount)
{
}
~LeakChecker()
{
QCOMPARE(liveCount, previousLiveCount);
}
private:
const size_t previousLiveCount;
};
enum ObjectFlags {
DefaultConstructed = 1,
CopyConstructed = 2,
CopyAssigned = 4
};
size_t id; // not unique
ObjectFlags flags;
static size_t liveCount;
};
size_t CountedObject::liveCount = 0;
void tst_QArrayData::arrayOps()
{
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
const int intArray[5] = { 80, 101, 100, 114, 111 };
const QString stringArray[5] = {
QLatin1String("just"),
QLatin1String("for"),
QLatin1String("testing"),
QLatin1String("a"),
QLatin1String("vector")
};
const CountedObject objArray[5];
QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic);
QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic);
QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic);
QCOMPARE(CountedObject::liveCount, size_t(5));
for (size_t i = 0; i < 5; ++i)
QCOMPARE(objArray[i].id, i);
////////////////////////////////////////////////////////////////////////////
// copyAppend (I)
SimpleVector<int> vi(intArray, intArray + 5);
SimpleVector<QString> vs(stringArray, stringArray + 5);
SimpleVector<CountedObject> vo(objArray, objArray + 5);
QCOMPARE(CountedObject::liveCount, size_t(10));
for (int i = 0; i < 5; ++i) {
QCOMPARE(vi[i], intArray[i]);
QVERIFY(vs[i].isSharedWith(stringArray[i]));
QCOMPARE(vo[i].id, objArray[i].id);
QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
| CountedObject::DefaultConstructed);
}
////////////////////////////////////////////////////////////////////////////
// destroyAll
vi.clear();
vs.clear();
vo.clear();
QCOMPARE(CountedObject::liveCount, size_t(5));
////////////////////////////////////////////////////////////////////////////
// copyAppend (II)
int referenceInt = 7;
QString referenceString = QLatin1String("reference");
CountedObject referenceObject;
vi = SimpleVector<int>(5, referenceInt);
vs = SimpleVector<QString>(5, referenceString);
vo = SimpleVector<CountedObject>(5, referenceObject);
QCOMPARE(vi.size(), size_t(5));
QCOMPARE(vs.size(), size_t(5));
QCOMPARE(vo.size(), size_t(5));
QCOMPARE(CountedObject::liveCount, size_t(11));
for (int i = 0; i < 5; ++i) {
QCOMPARE(vi[i], referenceInt);
QVERIFY(vs[i].isSharedWith(referenceString));
QCOMPARE(vo[i].id, referenceObject.id);
QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
| CountedObject::DefaultConstructed);
}
////////////////////////////////////////////////////////////////////////////
// insert
vi.reserve(30);
vs.reserve(30);
vo.reserve(30);
QCOMPARE(vi.size(), size_t(5));
QCOMPARE(vs.size(), size_t(5));
QCOMPARE(vo.size(), size_t(5));
QVERIFY(vi.capacity() >= 30);
QVERIFY(vs.capacity() >= 30);
QVERIFY(vo.capacity() >= 30);
// Displace as many elements as array is extended by
vi.insert(0, intArray, intArray + 5);
vs.insert(0, stringArray, stringArray + 5);
vo.insert(0, objArray, objArray + 5);
QCOMPARE(vi.size(), size_t(10));
QCOMPARE(vs.size(), size_t(10));
QCOMPARE(vo.size(), size_t(10));
// Displace more elements than array is extended by
vi.insert(0, intArray, intArray + 5);
vs.insert(0, stringArray, stringArray + 5);
vo.insert(0, objArray, objArray + 5);
QCOMPARE(vi.size(), size_t(15));
QCOMPARE(vs.size(), size_t(15));
QCOMPARE(vo.size(), size_t(15));
// Displace less elements than array is extended by
vi.insert(5, vi.constBegin(), vi.constEnd());
vs.insert(5, vs.constBegin(), vs.constEnd());
vo.insert(5, vo.constBegin(), vo.constEnd());
QCOMPARE(vi.size(), size_t(30));
QCOMPARE(vs.size(), size_t(30));
QCOMPARE(vo.size(), size_t(30));
QCOMPARE(CountedObject::liveCount, size_t(36));
for (int i = 0; i < 5; ++i) {
QCOMPARE(vi[i], intArray[i % 5]);
QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
QCOMPARE(vo[i].id, objArray[i % 5].id);
QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
| CountedObject::CopyAssigned);
}
for (int i = 5; i < 15; ++i) {
QCOMPARE(vi[i], intArray[i % 5]);
QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
QCOMPARE(vo[i].id, objArray[i % 5].id);
QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
| CountedObject::CopyAssigned);
}
for (int i = 15; i < 20; ++i) {
QCOMPARE(vi[i], referenceInt);
QVERIFY(vs[i].isSharedWith(referenceString));
QCOMPARE(vo[i].id, referenceObject.id);
QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
| CountedObject::CopyAssigned);
}
for (int i = 20; i < 25; ++i) {
QCOMPARE(vi[i], intArray[i % 5]);
QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
QCOMPARE(vo[i].id, objArray[i % 5].id);
// Originally inserted as (DefaultConstructed | CopyAssigned), later
// get shuffled into place by std::rotate (SimpleVector::insert,
// overlapping mode).
// Depending on implementation of rotate, final assignment can be:
// - straight from source: DefaultConstructed | CopyAssigned
// - through a temporary: CopyConstructed | CopyAssigned
QCOMPARE(vo[i].flags & CountedObject::CopyAssigned,
int(CountedObject::CopyAssigned));
}
for (int i = 25; i < 30; ++i) {
QCOMPARE(vi[i], referenceInt);
QVERIFY(vs[i].isSharedWith(referenceString));
QCOMPARE(vo[i].id, referenceObject.id);
QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
| CountedObject::CopyAssigned);
}
}
void tst_QArrayData::arrayOps2()
{
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
////////////////////////////////////////////////////////////////////////////
// appendInitialize
SimpleVector<int> vi(5);
SimpleVector<QString> vs(5);
SimpleVector<CountedObject> vo(5);
QCOMPARE(vi.size(), size_t(5));
QCOMPARE(vs.size(), size_t(5));
QCOMPARE(vo.size(), size_t(5));
QCOMPARE(CountedObject::liveCount, size_t(5));
for (size_t i = 0; i < 5; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i);
QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
}
////////////////////////////////////////////////////////////////////////////
// appendInitialize, again
// These will detach
vi.resize(10);
vs.resize(10);
vo.resize(10);
QCOMPARE(vi.size(), size_t(10));
QCOMPARE(vs.size(), size_t(10));
QCOMPARE(vo.size(), size_t(10));
QCOMPARE(CountedObject::liveCount, size_t(10));
for (size_t i = 0; i < 5; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i);
QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
| CountedObject::CopyConstructed);
}
for (size_t i = 5; i < 10; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i + 5);
QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
}
////////////////////////////////////////////////////////////////////////////
// truncate
QVERIFY(!vi.isShared());
QVERIFY(!vs.isShared());
QVERIFY(!vo.isShared());
// These shouldn't detach
vi.resize(7);
vs.resize(7);
vo.resize(7);
QCOMPARE(vi.size(), size_t(7));
QCOMPARE(vs.size(), size_t(7));
QCOMPARE(vo.size(), size_t(7));
QCOMPARE(CountedObject::liveCount, size_t(7));
for (size_t i = 0; i < 5; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i);
QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
| CountedObject::CopyConstructed);
}
for (size_t i = 5; i < 7; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i + 5);
QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
}
////////////////////////////////////////////////////////////////////////////
vi.resize(10);
vs.resize(10);
vo.resize(10);
for (size_t i = 7; i < 10; ++i) {
vi[i] = int(i);
vs[i] = QString::number(i);
QCOMPARE(vo[i].id, i);
QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
}
QCOMPARE(CountedObject::liveCount, size_t(10));
////////////////////////////////////////////////////////////////////////////
// erase
vi.erase(vi.begin() + 2, vi.begin() + 5);
vs.erase(vs.begin() + 2, vs.begin() + 5);
vo.erase(vo.begin() + 2, vo.begin() + 5);
QCOMPARE(vi.size(), size_t(7));
QCOMPARE(vs.size(), size_t(7));
QCOMPARE(vo.size(), size_t(7));
QCOMPARE(CountedObject::liveCount, size_t(7));
for (size_t i = 0; i < 2; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i);
QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
| CountedObject::CopyConstructed);
}
for (size_t i = 2; i < 4; ++i) {
QCOMPARE(vi[i], 0);
QVERIFY(vs[i].isNull());
QCOMPARE(vo[i].id, i + 8);
QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed)
| CountedObject::CopyAssigned);
}
for (size_t i = 4; i < 7; ++i) {
QCOMPARE(vi[i], int(i + 3));
QCOMPARE(vs[i], QString::number(i + 3));
QCOMPARE(vo[i].id, i + 3);
QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
| CountedObject::CopyAssigned);
}
}
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()
{
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
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,
QArrayData::CapacityReserved));
QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(5,
QArrayData::Default));
QArrayDataPointer<int> nonEmptyExtraCapacity(
QTypedArrayData<int>::allocate(10, QArrayData::Default));
QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15,
QArrayData::CapacityReserved));
QArrayDataPointer<int> staticArray(
static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
QArrayDataPointer<int> rawData(
QTypedArrayData<int>::fromRawData(staticArrayData.data, 10));
nonEmpty->copyAppend(5, 1);
nonEmptyExtraCapacity->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(5) << false << 1;
QTest::newRow("non-empty-extra-capacity") << nonEmptyExtraCapacity << 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;
QTest::newRow("raw-data") << rawData << size_t(10) << size_t(0) << false << 3;
#endif
}
void tst_QArrayData::setSharable()
{
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
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, as is additional capacity
// if capacityReserved flag is not set.
if ((capacity == 0 && size != 0)
|| (!isCapacityReserved && capacity > size))
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());
#endif
}
struct ResetOnDtor
{
ResetOnDtor()
: value_()
{
}
ResetOnDtor(int value)
: value_(value)
{
}
~ResetOnDtor()
{
value_ = 0;
}
int value_;
};
bool operator==(const ResetOnDtor &lhs, const ResetOnDtor &rhs)
{
return lhs.value_ == rhs.value_;
}
template <class T>
void fromRawData_impl()
{
static const T array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
{
// Default: Immutable, sharable
SimpleVector<T> raw = SimpleVector<T>::fromRawData(array,
sizeof(array)/sizeof(array[0]), QArrayData::Default);
QCOMPARE(raw.size(), size_t(11));
QCOMPARE((const T *)raw.constBegin(), array);
QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
QVERIFY(!raw.isShared());
QVERIFY(SimpleVector<T>(raw).isSharedWith(raw));
QVERIFY(!raw.isShared());
// Detach
QCOMPARE(raw.back(), T(11));
QVERIFY((const T *)raw.constBegin() != array);
}
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
{
// Immutable, unsharable
SimpleVector<T> raw = SimpleVector<T>::fromRawData(array,
sizeof(array)/sizeof(array[0]), QArrayData::Unsharable);
QCOMPARE(raw.size(), size_t(11));
QCOMPARE((const T *)raw.constBegin(), array);
QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
SimpleVector<T> copy(raw);
QVERIFY(!copy.isSharedWith(raw));
QVERIFY(!raw.isShared());
QCOMPARE(copy.size(), size_t(11));
for (size_t i = 0; i < 11; ++i) {
QCOMPARE(const_(copy)[i], const_(raw)[i]);
QCOMPARE(const_(copy)[i], T(i + 1));
}
QCOMPARE(raw.size(), size_t(11));
QCOMPARE((const T *)raw.constBegin(), array);
QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
// Detach
QCOMPARE(raw.back(), T(11));
QVERIFY((const T *)raw.constBegin() != array);
}
#endif
}
void tst_QArrayData::fromRawData_data()
{
QTest::addColumn<int>("type");
QTest::newRow("int") << 0;
QTest::newRow("ResetOnDtor") << 1;
}
void tst_QArrayData::fromRawData()
{
QFETCH(int, type);
switch (type)
{
case 0:
fromRawData_impl<int>();
break;
case 1:
fromRawData_impl<ResetOnDtor>();
break;
default:
QFAIL("Unexpected type data");
}
}
void tst_QArrayData::literals()
{
{
QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
QCOMPARE(d->size, 10 + 1);
for (int i = 0; i < 10; ++i)
QCOMPARE(d->data()[i], char('A' + i));
}
{
// wchar_t is not necessarily 2-bytes
QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ");
QCOMPARE(d->size, 10 + 1);
for (int i = 0; i < 10; ++i)
QCOMPARE(d->data()[i], wchar_t('A' + i));
}
{
SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
QVERIFY(!v.isNull());
QVERIFY(!v.isEmpty());
QCOMPARE(v.size(), size_t(11));
// v.capacity() is unspecified, for now
QVERIFY(v.isStatic());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(v.isSharable());
#endif
QCOMPARE((void*)(const char*)(v.constBegin() + v.size()), (void*)(const char*)v.constEnd());
for (int i = 0; i < 10; ++i)
QCOMPARE(const_(v)[i], char('A' + i));
QCOMPARE(const_(v)[10], char('\0'));
}
{
struct LiteralType {
int value;
Q_DECL_CONSTEXPR LiteralType(int v = 0) : value(v) {}
};
QArrayDataPointer<LiteralType> d = Q_ARRAY_LITERAL(LiteralType, LiteralType(0), LiteralType(1), LiteralType(2));
QCOMPARE(d->size, 3);
for (int i = 0; i < 3; ++i)
QCOMPARE(d->data()[i].value, i);
}
}
// Variadic Q_ARRAY_LITERAL need to be available in the current configuration.
void tst_QArrayData::variadicLiterals()
{
{
QArrayDataPointer<int> d =
Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
QCOMPARE(d->size, 10);
for (int i = 0; i < 10; ++i)
QCOMPARE(d->data()[i], i);
}
{
QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char,
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
QCOMPARE(d->size, 10);
for (int i = 0; i < 10; ++i)
QCOMPARE(d->data()[i], char('A' + i));
}
{
QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *,
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J");
QCOMPARE(d->size, 10);
for (int i = 0; i < 10; ++i) {
QCOMPARE(d->data()[i][0], char('A' + i));
QCOMPARE(d->data()[i][1], '\0');
}
}
{
SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6);
QVERIFY(!v.isNull());
QVERIFY(!v.isEmpty());
QCOMPARE(v.size(), size_t(7));
// v.capacity() is unspecified, for now
QVERIFY(v.isStatic());
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
QVERIFY(v.isSharable());
#endif
QCOMPARE((const int *)(v.constBegin() + v.size()), (const int *)v.constEnd());
for (int i = 0; i < 7; ++i)
QCOMPARE(const_(v)[i], i);
}
}
// std::remove_reference is in C++11, but requires library support
template <class T> struct RemoveReference { typedef T Type; };
template <class T> struct RemoveReference<T &> { typedef T Type; };
// single-argument std::move is in C++11, but requires library support
template <class T>
typename RemoveReference<T>::Type &&cxx11Move(T &&t)
{
return static_cast<typename RemoveReference<T>::Type &&>(t);
}
struct CompilerHasCxx11ImplicitMoves
{
static bool value()
{
DetectImplicitMove d(cxx11Move(DetectImplicitMove()));
return d.constructor == DetectConstructor::MoveConstructor;
}
struct DetectConstructor
{
Q_DECL_CONSTEXPR DetectConstructor()
: constructor(DefaultConstructor)
{
}
Q_DECL_CONSTEXPR DetectConstructor(const DetectConstructor &)
: constructor(CopyConstructor)
{
}
Q_DECL_CONSTEXPR DetectConstructor(DetectConstructor &&)
: constructor(MoveConstructor)
{
}
enum Constructor {
DefaultConstructor,
CopyConstructor,
MoveConstructor
};
Constructor constructor;
};
struct DetectImplicitMove
: DetectConstructor
{
};
};
// RValue references need to be supported in the current configuration
void tst_QArrayData::rValueReferences()
{
if (!CompilerHasCxx11ImplicitMoves::value())
QSKIP("Implicit move ctor not supported in current configuration");
SimpleVector<int> v1(1, 42);
SimpleVector<int> v2;
const SimpleVector<int>::const_iterator begin = v1.constBegin();
QVERIFY(!v1.isNull());
QVERIFY(v2.isNull());
// move-assign
v2 = cxx11Move(v1);
QVERIFY(v1.isNull());
QVERIFY(!v2.isNull());
QCOMPARE(v2.constBegin(), begin);
SimpleVector<int> v3(cxx11Move(v2));
QVERIFY(v1.isNull());
QVERIFY(v2.isNull());
QVERIFY(!v3.isNull());
QCOMPARE(v3.constBegin(), begin);
QCOMPARE(v3.size(), size_t(1));
QCOMPARE(v3.front(), 42);
}
void tst_QArrayData::grow()
{
SimpleVector<int> vector;
QCOMPARE(vector.size(), size_t(0));
QCOMPARE(vector.capacity(), size_t(0));
size_t previousCapacity = 0;
size_t allocations = 0;
for (size_t i = 1; i < (1 << 20); ++i) {
int source[1] = { int(i) };
vector.append(source, source + 1);
QCOMPARE(vector.size(), i);
if (vector.capacity() != previousCapacity) {
// Don't re-allocate until necessary
QVERIFY(previousCapacity < i);
previousCapacity = vector.capacity();
++allocations;
// Going element-wise is slow under valgrind
if (previousCapacity - i > 10) {
i = previousCapacity - 5;
vector.back() = -int(i);
vector.resize(i);
// It's still not the time to re-allocate
QCOMPARE(vector.capacity(), previousCapacity);
}
}
}
QVERIFY(vector.size() >= size_t(1 << 20));
// QArrayData::Grow prevents excessive allocations on a growing container
QVERIFY(allocations > 20 / 2);
QVERIFY(allocations < 20 * 2);
for (size_t i = 0; i < vector.size(); ++i) {
int value = const_(vector).at(i);
if (value < 0) {
i = -value;
continue;
}
QCOMPARE(value, int(i + 1));
}
}
QTEST_APPLESS_MAIN(tst_QArrayData)
#include "tst_qarraydata.moc"